Skip to content

Set up staging environment for pre-production testing #38

@michaeldistel

Description

@michaeldistel

Problem

No staging environment means:

  • Deploy directly to production
  • Bugs discovered by users
  • No safe testing environment
  • Risky deployments

Solution

Create staging environment with separate domain:

Option 1: Subdomain (Recommended)

Option 2: Separate Domain

Infrastructure Setup

Docker Compose

# docker-compose.staging.yml
version: '3.8'
services:
  staging:
    build: .
    container_name: controlforge-staging
    ports:
      - "3001:3000"
    environment:
      - NODE_ENV=staging
      - BASE_URL=https://staging.controlforge.dev
    restart: unless-stopped

Nginx Config

# /etc/nginx/sites-available/staging.controlforge.dev
server {
    listen 80;
    listen [::]:80;
    server_name staging.controlforge.dev;
    
    # Redirect to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name staging.controlforge.dev;
    
    ssl_certificate /etc/letsencrypt/live/staging.controlforge.dev/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/staging.controlforge.dev/privkey.pem;
    
    location / {
        proxy_pass http://localhost:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Deploy Scripts

// package.json
{
  "scripts": {
    "deploy:staging": "pnpm build:prod && rsync -avz --delete build/ user@homelab:/srv/controlforge-staging/ && ssh user@homelab 'cd /srv/controlforge-staging && docker-compose -f docker-compose.staging.yml up -d --build'",
    "deploy:prod": "pnpm build:prod && ./scripts/pre-deployment-check.sh && rsync -avz --delete build/ user@homelab:/srv/controlforge/ && ssh user@homelab 'cd /srv/controlforge && docker-compose up -d --build'"
  }
}

CI/CD Integration

Auto-deploy PRs to Staging

# .github/workflows/deploy-staging.yml
name: Deploy to Staging

on:
  push:
    branches: [staging, develop]
  pull_request:
    types: [opened, synchronize]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v2
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'
      
      - run: pnpm install
      - run: pnpm build:prod
      
      - name: Deploy to staging
        env:
          SSH_KEY: ${{ secrets.STAGING_SSH_KEY }}
        run: |
          echo "$SSH_KEY" > ssh_key
          chmod 600 ssh_key
          rsync -avz -e "ssh -i ssh_key" build/ user@homelab:/srv/controlforge-staging/
          ssh -i ssh_key user@homelab 'cd /srv/controlforge-staging && docker-compose -f docker-compose.staging.yml up -d --build'
      
      - name: Comment PR with staging URL
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '✅ Deployed to staging: https://staging.controlforge.dev'
            })

Features

  • Auto-deploy on PR
  • Password protection (basic auth)
  • Separate analytics property
  • Noindex robots meta (prevent Google indexing)
  • Visual indicator (banner: "STAGING ENVIRONMENT")

Staging Banner

<!-- src/lib/components/StagingBanner.svelte -->
{#if import.meta.env.MODE === 'staging'}
  <div class="staging-banner">
    ⚠️ STAGING ENVIRONMENT - Not for production use
  </div>
{/if}

<style>
  .staging-banner {
    background: #ff9800;
    color: white;
    text-align: center;
    padding: 0.5rem;
    font-weight: 600;
  }
</style>

Success Criteria

  • Staging URL accessible
  • Auto-deploys on PR
  • Separate from production
  • Visual staging indicator
  • Password protected
  • SSL certificate configured

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions