Multi-environment Deployment
Environment Strategy
Staging → Production Flow
┌─────────────────────────────────────────────────────────────┐
│ DEPLOYMENT FLOW │
├─────────────────────────────────────────────────────────────┤
│ │
│ Developer │
│ │ │
│ ▼ │
│ Feature Branch ──▶ PR ──▶ Code Review │
│ │ │
│ ▼ │
│ Merge to staging │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ STAGING ENVIRONMENT │ │
│ │ - staging.myapp.com │ │
│ │ - Supabase staging project │ │
│ │ - Cloudflare staging worker │ │
│ │ - Test with real data (sanitized) │ │
│ └───────────────────────────────────────────────────┘ │
│ │ │
│ Manual approval │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ PRODUCTION ENVIRONMENT │ │
│ │ - myapp.com │ │
│ │ - Supabase production project │ │
│ │ - Cloudflare production worker │ │
│ └───────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Branch Strategy
Git Flow cho Light Stack
main (production)
│
├── staging (staging env)
│ │
│ ├── feature/add-user-auth
│ ├── feature/task-list
│ └── fix/login-bug
│
└── Hotfix branches (direct to main)
Branch Rules
main:
- Protected branch
- Requires PR + approval
- Auto-deploy to production
- No direct pushes
staging:
- Auto-deploy to staging
- Merge features here first
- Integration testing
feature/*:
- Development work
- PR to staging
- CI checks must pass
GitHub Environments
Setup Environments
Repository Settings → Environments
1. Create "staging" environment
- No protection rules (auto-deploy)
- Secrets: SUPABASE_URL, etc.
2. Create "production" environment
- Required reviewers: 1+
- Wait timer: Optional
- Deployment branches: main only
- Secrets: Production values
Environment Secrets
staging:
├── SUPABASE_URL=https://staging-xxx.supabase.co
├── SUPABASE_ANON_KEY=eyJ...staging
├── SUPABASE_SERVICE_KEY=eyJ...staging
├── CLOUDFLARE_ACCOUNT_ID=xxx
└── CLOUDFLARE_API_TOKEN=xxx
production:
├── SUPABASE_URL=https://xxx.supabase.co
├── SUPABASE_ANON_KEY=eyJ...production
├── SUPABASE_SERVICE_KEY=eyJ...production
├── CLOUDFLARE_ACCOUNT_ID=xxx
└── CLOUDFLARE_API_TOKEN=xxx
Complete Workflow
Multi-environment Deploy
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [staging, main]
pull_request:
branches: [staging, main]
env:
NODE_VERSION: '20'
jobs:
# ========================================
# Build and Test (all branches)
# ========================================
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Test
run: npm run test
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build
path: .open-next/
retention-days: 1
# ========================================
# Deploy to Staging
# ========================================
deploy-staging:
needs: build
if: github.ref == 'refs/heads/staging'
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build
path: .open-next/
- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
- name: Run Supabase Migrations
run: |
supabase link --project-ref ${{ vars.SUPABASE_PROJECT_REF }}
supabase db push
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: Deploy to Cloudflare
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --env staging
# ========================================
# Deploy to Production
# ========================================
deploy-production:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build
path: .open-next/
- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
- name: Run Supabase Migrations
run: |
supabase link --project-ref ${{ vars.SUPABASE_PROJECT_REF }}
supabase db push
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: Deploy to Cloudflare
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --env production
Cloudflare Environments
wrangler.toml
name = "my-app"
main = "src/index.ts"
compatibility_date = "2024-01-15"
# Default (production) environment
[vars]
ENVIRONMENT = "production"
NEXT_PUBLIC_SUPABASE_URL = "https://xxx.supabase.co"
# Staging environment
[env.staging]
name = "my-app-staging"
[env.staging.vars]
ENVIRONMENT = "staging"
NEXT_PUBLIC_SUPABASE_URL = "https://staging-xxx.supabase.co"
# KV bindings per environment
[[kv_namespaces]]
binding = "MY_KV"
id = "production-kv-id"
[[env.staging.kv_namespaces]]
binding = "MY_KV"
id = "staging-kv-id"
Supabase Multi-project
Project per Environment
Supabase Dashboard
├── my-app-staging (staging project)
│ └── Project ref: staging-xxx
│
└── my-app (production project)
└── Project ref: xxx
Migrations for Both
# Link to staging
supabase link --project-ref staging-xxx
supabase db push
# Link to production
supabase link --project-ref xxx
supabase db push
Environment Variables
Next.js Environment Files
# .env.local (local development)
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=local-key
# .env.staging (staging - not committed)
NEXT_PUBLIC_SUPABASE_URL=https://staging-xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=staging-key
# .env.production (production - not committed)
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=production-key
Build-time Variables
# In GitHub Actions
- name: Build for staging
run: npm run build
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ vars.SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
Tổng kết
Environment Setup Checklist
- [ ] Create staging Supabase project
- [ ] Create production Supabase project
- [ ] Configure wrangler.toml environments
- [ ] Setup GitHub environments
- [ ] Add secrets per environment
- [ ] Configure branch protection
- [ ] Setup deployment workflow
Key Points
✅ Separate Supabase projects per environment
✅ GitHub environments for secrets isolation
✅ wrangler.toml [env.staging] for Cloudflare
✅ Automatic deploy on push to branch
✅ Manual approval for production
Q&A
- Có cần staging environment không?
- Approval process cho production?
- Rollback strategy?