Multiple Environments¶
small-config treats environments as a first-class concept. Every command accepts --env and each environment gets its own isolated state file.
How --env is resolved¶
When you run sc build, the CLI resolves the environment name in this order:
--env <name>— explicit flag always winsCONFIG.default_env— if usingconfig.pyand--envis omitted"production"— fallback for legacysc.pyfiles withoutProjectConfig
sc build # uses CONFIG.default_env (e.g. "dev")
sc build --env staging # forces "staging"
sc build -e production # short form
Per-environment state files¶
Each environment writes to its own state file:
| Environment | State file |
|---|---|
dev |
sc.state.dev.json |
staging |
sc.state.staging.json |
production |
sc.state.production.json |
State files are ignored by .gitignore (added by sc init). Each file tracks which components are deployed, their outputs, operator, and git SHA.
Legacy migration¶
If you have an existing sc.state.json from before per-env isolation was introduced, it is still used automatically when --env production is requested and sc.state.production.json doesn't exist yet. New writes will create the per-env file.
Branching in config.py¶
project() receives the env string. Use it to vary any aspect of the stack:
class AppConfig(ProjectConfig):
default_env = "dev"
def project(self, env: str) -> Project:
if env == "production":
project_id = "my-app-prod"
machine_type = "n2-standard-4"
else:
project_id = "my-app-dev"
machine_type = "e2-micro"
infra = TerraformComponent(
name="infra",
vars={
"project_id": project_id,
"machine_type": machine_type,
"env": env,
},
)
return Project(name="my-app", env=env, components=[infra], tests=[...])
You can also branch on env to skip certain components entirely:
Using environment names in Terraform¶
Pass env to Terraform as a variable so resource names (VMs, buckets, databases) are unique per environment:
# infra/main.tf
variable "env" {}
variable "project_id" {}
resource "google_compute_instance" "app" {
name = "app-${var.env}"
project = var.project_id
...
}
This avoids collisions when dev and production live in the same GCP project.
Deploying and destroying per env¶
Each env is fully independent:
# dev (default)
sc build
sc destroy
# staging
sc build --env staging
sc destroy --env staging
# production
sc build --env production
sc destroy --env production
Locks are also per-env — a running sc build --env staging won't block sc build --env dev.
Recommended workflow¶
For small teams:
default_env = "dev"— daily work uses dev by default, no--envneededsc build --env stagingbefore every release to stagingsc build --env productionas the final deploy step (gate behind CI approval)