Replit Multi-Environment Setup
Overview
Configure development, staging, and production environments on Replit. Leverages Replit's built-in dev/prod database separation, environment-specific secrets, and deployment types. Covers the Replit-native approach (single Repl, dual databases) and the multi-Repl approach (separate Repls per environment).
Prerequisites
- Replit Core or Teams plan (deployment access)
- PostgreSQL provisioned in Database pane
- Understanding of Replit Secrets
Environment Strategy
Approach 1: Single Repl, Dual Databases (Recommended)
Replit natively provides separate development and production databases:
Workspace "Run" button → Development database
Deployed app (.replit.app) → Production database
Both use the same DATABASE_URL env var — Replit routes automatically.
No code changes needed between environments.
Approach 2: Multi-Repl (Staging + Production)
For teams that need a staging environment:
Repl 1: my-app-staging → Autoscale deployment → staging.replit.app
Repl 2: my-app-prod → Reserved VM deployment → app.example.com
Each Repl has its own:
- Secrets (different API keys per environment)
- PostgreSQL database (separate data)
- Deployment configuration
- GitHub branch (staging → staging, main → production)
Instructions
Step 1: Environment Detection
// src/config/environment.ts
type Environment = 'development' | 'staging' | 'production';
export function detectEnvironment(): Environment {
// Replit deployment context
if (process.env.REPL_DEPLOYMENT) {
// Check if this is the staging Repl
if (process.env.REPL_SLUG?.includes('staging')) return 'staging';
return 'production';
}
// Workspace "Run" context
if (process.env.REPL_SLUG) return 'development';
// Fallback to NODE_ENV
const env = process.env.NODE_ENV || 'development';
if (env === 'production') return 'production';
if (env === 'staging') return 'staging';
return 'development';
}
export const ENV = detectEnvironment();
export const IS_PROD = ENV === 'production';
Step 2: Environment-Specific Configuration
// src/config/index.ts
import { ENV, IS_PROD } from './environment';
const configs = {
development: {
logLevel: 'debug',
corsOrigins: ['*'],
rateLimit: { windowMs: 60000, max: 1000 },
cache: { ttlMs: 5000 },
features: { debugEndpoints: true },
},
staging: {
logLevel: 'info',
corsOrigins: ['https://staging.example.com'],
rateLimit: { windowMs: 60000, max: 200 },
cache: { ttlMs: 30000 },
features: { debugEndpoints: true },
},
production: {
logLevel: 'warn',
corsOrigins: ['https://app.example.com'],
rateLimit: { windowMs: 60000, max: 100 },
cache: { ttlMs: 300000 },
features: { debugEndpoints: false },
},
};
export const config = {
env: ENV,
port: parseInt(process.env.PORT || '3000'),
...configs[ENV],
db: {
// DATABASE_URL auto-switches between dev and prod on Replit
url: process.env.DATABASE_URL,
},
};
// Validate production secrets
if (IS_PROD) {
const required = ['DATABASE_URL', 'JWT_SECRET'];
const missing = required.filter(k => !process.env[k]);
if (missing.length) {
console.error(`FATAL: Missing production secrets: ${missing.join(', ')}`);
process.exit(1);
}
}
Step 3: Separate Secrets Per Environment
For Single Repl (dev/prod separation):
- All secrets set once in Secrets tab
- DATABASE_URL auto-switches (Replit manages)
- Same JWT_SECRET for both (or use REPL_IDENTITY for dev)
For Multi-Repl (staging + prod):
- Each Repl has its own Secrets tab
- staging Repl: JWT_SECRET=staging-secret, API_KEY=test-key
- production Repl: JWT_SECRET=prod-secret, API_KEY=live-key
Account-level secrets (shared across all Repls):
- Settings > Secrets > Account secrets
- Useful for: monitoring tokens, shared infrastructure keys
Step 4: GitHub Branch Strategy (Multi-Repl)
Repository setup:
- main branch → connected to production Repl
- staging branch → connected to staging Repl
- feature branches → PR to staging first
Workflow:
1. Feature branch → PR to staging
2. Tests pass + review → merge to staging
3. Staging Repl auto-deploys → verify staging
4. Staging → PR to main
5. Merge → production Repl auto-deploys
# .github/workflows/deploy.yml
name: Deploy Pipeline
on:
push:
branches: [staging, main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test
verify-staging:
if: github.ref == 'refs/heads/staging'
needs: test
runs-on: ubuntu-latest
steps:
- name: Wait for Replit deploy
run: sleep 60
- name: Health check staging
run: curl -sf ${{ secrets.STAGING_URL }}/health
verify-production:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
steps:
- name: Wait for Replit deploy
run: sleep 60
- name: Health check production
run: curl -sf ${{ secrets.PRODUCTION_URL }}/health
Step 5: Database Migration Between Environments
// scripts/promote-data.ts — Copy staging data to production (carefully!)
import { Pool } from 'pg';
const staging = new Pool({ connectionString: process.env.STAGING_DATABASE_URL });
const production = new Pool({ connectionString: process.env.PRODUCTION_DATABASE_URL });
async function promoteConfig() {
// Only promote configuration/reference data, never user data
const { rows: configs } = await staging.query('SELECT * FROM feature_flags');
for (const config of configs) {
await production.query(
`INSERT INTO feature_flags (key, value, updated_at)
VALUES ($1, $2, NOW())
ON CONFLICT (key) DO UPDATE SET value = $2, updated_at = NOW()`,
[config.key, config.value]
);
}
console.log(`Promoted ${configs.length} feature flags to production`);
}
Step 6: Environment Indicator in UI
// Show environment badge in development/staging
app.use((req, res, next) => {
if (!IS_PROD) {
res.setHeader('X-Environment', ENV);
}
next();
});
app.get('/api/status', (req, res) => {
res.json({
environment: ENV,
version: process.env.npm_package_version,
repl: process.env.REPL_SLUG,
});
});
Error Handling
| Issue | Cause | Solution | |-------|-------|----------| | Wrong database in prod | Manual DATABASE_URL override | Let Replit manage DB routing | | Staging secrets in prod | Copied secrets incorrectly | Each Repl has independent Secrets | | GitHub sync conflict | Both Repls on same branch | Use separate branches per Repl | | Config validation fails | Missing env-specific secret | Add secret to correct Repl's Secrets tab |
Resources
Next Steps
For monitoring, see replit-observability. For deployment, see replit-deploy-integration.