Deployment environments and versions¶
How deployment environments separate what runs where, how designated branches and variables shape a deploy, and how immutable versions and plan-only deploys make promoting to production a deliberate act. Read this before you set up a second deployment environment or your first production deploy.
Deployment environments are isolated projects¶
A deployment environment is a target inside a product that services deploy into. Each one has its own Google Cloud project, so Development and Production never share a Cloud Run service, a bucket or a secret. A new product starts with one development environment; you add others when the product is ready for them, including a production environment, which is created explicitly and marked as such.
flowchart LR
V[Service version 1.4.7] -->|deploy| D1["Development<br/><small>own project</small>"]
V -->|plan, then confirmed deploy| D2["Production<br/><small>own project</small>"]
Every deployment environment carries three things a deploy reads:
- its type: development or production. Production is the one that is gated.
- its variables: named values your Terraform receives as inputs, and through it your service. Variables live on the deployment environment, not on the service, so the same version deployed to two deployment environments gets two configurations.
- its designated branches: the build-repo branches versions deployed here are expected to come from.
Variables are deployment environment configuration¶
Because a version is immutable, anything that differs between deployment environments has to come from outside the version. That is what environment variables are for: hostnames, feature flags, third-party keys. They reach the service's Terraform as input variables of the same name on the next deploy, and your Terraform passes the ones the container needs into it.
A secret can be stored in the deployment environment project's Secret Manager instead of in Alis. Such a variable is left out of the deploy inputs entirely, and your Terraform reads the secret by name. This keeps secrets inside your own cloud project while the configuration still shows up, masked, in one list.
Designated branches¶
A production environment usually deploys from main; a development environment from anything. Designating branches on a deployment environment records that expectation. From then on, deploying a version built from a commit on another branch triggers a confirmation, interactively or through --allow-branch-mismatch. The deploy is warned about, never blocked: the designation is a guard against accidents, not a policy engine. The version's branch is recorded at build time by the client that ran the build, so the check works on what was built, not on what is checked out now.
Feature freeze¶
Where designated branches warn, a feature freeze blocks. A product admin marks a service's deployment in a deployment environment as frozen and, until it is lifted, nothing can deploy, plan or destroy that service there: the platform refuses with DEPLOYMENT_FROZEN, and no flag overrides it. The freeze is per service and per deployment environment, so a freeze on production never slows development, and it belongs to the product's admins, not to whoever may deploy.
Versions and deployments¶
A version is the product of one build: a semantic version number, the commit and branch it came from, its images and their states. It exists once and never changes.
A deployment is a version running in a deployment environment. Each deployment environment has at most one deployment per service, named after the service, and the deployment records which version it runs, its state (RUNNING, DEPLOYING, DEPLOY_FAILED, PLANNED and so on) and who deployed it. Each deploy also leaves a deployment version behind, so the history of what ran where is kept.
The same version can be deployed to any number of deployment environments. Promotion is therefore not a rebuild: the version that passed in Development is the exact set of images that goes to Production.
Plan first, then confirm¶
Deploy is Terraform, so every deploy can be previewed. A plan-only deploy runs terraform plan against the target deployment environment and reports the changes without making them. It needs no production confirmation, which makes it the right first step for a production deploy and for any change to infra/.
The production gate is the second step. A deploy to a production environment stops and asks. At an interactive terminal that is a [y/N] prompt; in scripts and coding agents it is exit code 3 with PRODUCTION_CONFIRMATION_REQUIRED, and only re-running with --confirm-production proceeds. The flag cannot be replaced by any general approval mechanism, and it applies to that one deploy. The same gate protects changing and revealing a production environment's variables.
The gate is deliberately a human step. Everything else in the loop can be automated end to end; the moment a change reaches paying users is the one moment the platform insists a person has looked at the version, the deployment environment and the plan.
Rolling back¶
Because versions are immutable and deployments point at versions, a rollback is a deploy of an earlier version: alis deploy <pkg> --version <older> -e <env>. No rebuild, no special command, the same plan and the same gate.