Skip to content

Set up staging environment for pre-production testing #38

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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