Environments & Config Promotion
Environment parity catches Rust-specific failures before prod: Tokio worker counts, sqlx pool sizes, RUST_LOG levels, and feature flags. Config promotion moves tested values through dev, staging, and prod with secrets injected at runtime, not copied between laptops.
Recipe
Quick-reference recipe card - copy-paste ready.
dev: local docker-compose, .env.example only (no real secrets)
staging: 50% prod RPS load test, pool max = prod, anonymized prod snapshot
prod: vault-injected secrets, immutable image SHA, change window
Promotion: PR updates config/values.staging.yaml -> soak 48h -> PR values.prod.yamlWhen to reach for this:
- New service bootstrap
- Repeated "works in staging, fails in prod" incidents
- Audit of secret handling
- Multi-region expansion
Working Example
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct AppConfig {
pub database_url: String,
pub database_max_connections: u32,
pub tokio_worker_threads: usize,
pub git_sha: String,
}
impl AppConfig {
pub fn from_env() -> Result<Self, config::ConfigError> {
config::Config::builder()
.add_source(config::Environment::default().separator("__"))
.build()?
.try_deserialize()
}
pub fn validate(&self) -> Result<(), String> {
if self.database_max_connections < 5 {
return Err("DATABASE_MAX_CONNECTIONS must be >= 5".into());
}
if self.tokio_worker_threads == 0 {
return Err("TOKIO_WORKER_THREADS must be set".into());
}
Ok(())
}
}# CI: validate staging fixture against schema
cargo test -p orders-api -- config_validate_staging_fixture
# Promote (GitOps example)
cp deploy/staging/values.yaml deploy/prod/values.yaml
# diff review: only intended deltas (replicas, URLs)What this demonstrates:
- Typed config with validation catches typos before deploy
- Nested env keys via
__separator map to struct fields - Promotion is reviewed PR, not manual kubectl edit
Deep Dive
Parity Dimensions
| Dimension | Minimum staging fidelity |
|---|---|
| Rust toolchain | Same rust-toolchain.toml |
| CPU / memory limits | Same ratio as prod per pod |
| DB pool size | Match prod max_connections |
| TLS / rustls config | Same cipher policy |
| Dependency services | Realistic latency inject optional |
Secrets Flow
- Developers: fake credentials or local vault dev role.
- CI: short-lived tokens from OIDC.
- Staging/prod: Vault/K8s secrets operator; mount as env at pod start.
- Never commit
.envwith prod secrets;cargo deny+ secret scanners in CI.
Config vs Code
- Feature flags and pool sizes: config layer.
- Business logic: code + normal release.
- Document which changes need only config promotion vs image rebuild.
Gotchas
- Staging at 1/100 traffic - Pool bugs hide. Fix: Load test at 50% peak quarterly.
- Prod-only env var typo - Classic incident. Fix: Schema validate all env fixtures in CI.
- Different Tokio workers staging vs prod - Race hides. Fix: Explicit
TOKIO_WORKER_THREADSin both. - Copy prod DB to laptop - Compliance violation. Fix: Anonymized snapshot pipeline.
- Manual prod config edit - Drift from GitOps. Fix: Revert + PR within 24h policy.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Preview env per PR | High change volume | Cost-sensitive small team |
| Ephemeral env | Integration tests | Long soak tests |
| Single staging | Tiny product | Tier-1 revenue service |
| Production shadow | Read-only validation | Writes not idempotent |
FAQs
How many environments?
Dev + staging + prod minimum for tier-1; add perf env if compile/load tests need isolation.
Config in ConfigMap vs env?
Env for 12-factor simplicity; files for large TLS bundles; document reload behavior.
Regional prod config?
Base values + region overlay; promote overlays together or per region playbook.
sqlx offline mode CI?
SQLX_OFFLINE=true with cached query data; same DB version as staging.
Worker env parity?
Workers share config crate with API; same promotion PR when possible.
Local prod debugging?
Sandbox with prod-like flags, never prod credentials on laptop.
Rotate secrets?
Dual-read window during rotation; document in promotion runbook.
MSRV across envs?
Same compiler; avoid "staging nightly, prod pinned" skew except brief canary.
Polars ETL env?
Separate data env with warehouse credentials; not same k8s ns as API.
Audit config changes?
Git history for values files; vault audit log for secret access.
Related
- Release Management - promotion timing
- Feature Flags & Progressive Delivery - flag per env
- Secrets Management - secret patterns
- Configuration and Feature Design - design
- Production Debugging - env repro
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.