It is 4:45 on a Friday. You changed one environment variable, ran your deploy script, and went to grab a coffee. By the time you got back, the homepage was throwing 500s. The variable was right. The problem was that your script also picked up a half-finished config change from your local shell, bundled it with code you had not actually tested together, and pushed the whole thing to production in one motion. Now you are reading logs instead of starting your weekend, and you are not even sure which change broke things because they all went out at once.
If you are the only ops person on a scaling team, or you are the lead who quietly became the ops person, you have lived some version of this. The root cause is almost never carelessness. It is that the word "deploy" is doing too much work. A single deploy step quietly conflates three completely different operations, and when they all happen at once, you lose the ability to reason about what actually changed.
This post is about a different model. Convox splits deployment into three primitives that you can run independently: a Build, a Release, and a Promotion. Once you understand how they fit together, the Friday-afternoon disaster above stops being possible, because configuring and shipping are no longer the same action. This is the heart of practical release management, and it does not require you to learn Kubernetes or hire a DevOps team to get it.
When you run a typical homegrown deploy script, three things happen back to back. First, your source code gets turned into a runnable artifact, usually a container image. Second, that artifact gets combined with the configuration it needs to run, things like database URLs, API keys, and feature flags. Third, the combined thing gets pushed live and starts serving real traffic.
Each of those steps has a different failure mode. A bad build is a code problem. A bad configuration is an environment problem. A bad ship is a timing or process problem. When your script fuses all three into one command, you cannot isolate which layer failed, and worse, you cannot verify any layer before the next one runs. The configuration you set is the configuration that ships, with no gap in between to test, review, or change your mind.
For a large team with a dedicated release engineer, that gap gets enforced by people and process. For a lean team, there is no one to enforce it, so the safety has to come from the tooling itself. That is exactly what the build, release, promote model gives you. It turns the implicit, all-at-once "deploy" into three explicit operations, each of which you can inspect and control on its own.
Let us define each piece precisely, because the precision is the point.
A Build is an image created from your source code. You produce one with convox build, which packages your code, builds it according to your Dockerfile, and stores the resulting image in your Rack's registry.
$ convox build -a myapp
Packaging source... OK
Uploading source... OK
Starting build... OK
Building: .
...
Build: BABCDEFGHI
Release: RBCDEFGHIJ
Notice that a single convox build produces two IDs: a Build (BABCDEFGHI) and a Release (RBCDEFGHIJ). The build is the artifact. The release is what we are about to talk about. The build itself is immutable. Once it exists, that image does not change. If you want different code, you make a new build. This matters because it means the exact bytes you tested are the exact bytes that can ship later, with no rebuild in between to introduce surprises.
If your source directory is large, or your uploads are slow, you can run the Docker build on your own machine and push it straight to the Rack registry with convox build --external. The result is the same kind of immutable build, just produced locally. The build command reference covers the full set of flags.
A Release is where the model earns its keep. A release is an immutable bundle of a specific build and a specific set of environment variables. It captures both the code and the configuration together, frozen at a point in time. You can list your releases with convox releases.
$ convox releases -a myapp
ID STATUS BUILD CREATED DESCRIPTION
RCDEFGHIJK BABCDEFGHI 1 minute ago env add:API_KEY
RBCDEFGHIJ active BABCDEFGHI 5 minutes ago build 0a1b2c3d my commit message
The key insight is that changing configuration creates a new release. When you run convox env set to update an environment variable, Convox does not silently mutate the running app. It creates a brand-new release that pairs your existing build with the new configuration.
$ convox env set API_KEY=sk-live-abc123 -a myapp
Setting API_KEY... OK
Release: RCDEFGHIJK
That command did not touch production. It created release RCDEFGHIJK and stopped. Your app is still serving the previous release. This is the gap that your old deploy script never had: a moment where the new configuration exists, is captured immutably, and is waiting for you to decide whether to ship it. Go back to the Friday story. With this model, setting the variable and shipping it are two separate actions, so the half-finished change cannot ride along by accident.
Because the release captures both the build and the environment, what you test is what you ship. There is no drift between the thing you validated and the thing that goes live, because they are the same immutable bundle. The environment variables documentation goes deeper on how variables are declared and scoped, including how to set a value on a specific release before promoting it.
A release does nothing until you promote it. Promotion is the act of taking a specific release and making it the active one serving production traffic. You do it explicitly, by ID.
$ convox releases promote RCDEFGHIJK -a myapp
Promoting RCDEFGHIJK...
2026-01-15T14:30:49Z system/k8s/atom/app Status: Running => Pending
2026-01-15T14:30:53Z system/k8s/atom/app Status: Pending => Updating
2026-01-15T14:30:56Z system/k8s/web-745f845dc-rzl2q Started container main
OK
This is the safety gate. Nothing reaches your users until you name a release and promote it. Building does not ship. Setting an environment variable does not ship. Only promotion ships. For a solo ops engineer, that single property removes an entire category of accidents, because there is no command you can run that quietly pushes an unintended change to production. You have to point at a release and say "this one."
When you genuinely want to build and ship in one motion, Convox still supports that with convox deploy, which creates a release and promotes it together. That is the right call for low-risk changes where you trust the pipeline. The point is not that you must always do three separate steps. The point is that you can split them apart whenever the change is risky enough to warrant a check.
The two-step flow is where the model shines. You build first, then do whatever verification you need, then promote. This is exactly the right shape for running a database migration against the new release before traffic hits it, or for setting and reviewing environment variables before they go live.
$ convox build -a myapp
Build: BABCDEFGHI
Release: RBCDEFGHIJ
$ convox run web bin/migrate -a myapp
Running... OK
$ convox releases promote RBCDEFGHIJ -a myapp
Promoting RBCDEFGHIJ... OK
The deploying changes guide walks through both the one-step and two-step flows in detail.
Here is the part that turns the whole model from "nice in theory" into "I sleep better at night." Because every release is immutable and kept in history, rolling back is not a special emergency procedure. It is just promotion pointed at an older release.
Say release RCDEFGHIJK is live and causing trouble. You know RBCDEFGHIJ was fine. You roll back by promoting the good one.
$ convox releases rollback RBCDEFGHIJ -a myapp
Rolling back to RBCDEFGHIJ...
2026-01-15T14:30:49Z system/k8s/atom/app Status: Running => Pending
2026-01-15T14:30:53Z system/k8s/atom/app Status: Pending => Updating
2026-01-15T14:30:56Z system/k8s/web-745f845dc-rzl2q Started container main
OK, RZYXWVUTSR
Rolling back creates a fresh copy of the target release and promotes it. The original release is untouched. The build and environment variables from the known-good release are restored exactly, because they were captured immutably in the first place. There is no rebuild, no re-fetching of config, no "I hope the old image is still around." It is the same artifact you ran before, brought back in seconds.
This is what a safe deployment workflow actually means in practice. Forward and backward moves are the same operation: promote a release. The rollback documentation covers the details, including the one thing the platform cannot undo for you, which is destructive database migrations. Reverting code is instant; reverting schema changes is still your responsibility to plan for.
When you promote a release, your app moves through a set of statuses, and knowing them removes the anxiety of watching a deploy you do not fully understand. An app reports its lifecycle state in the Console and from the CLI with convox apps info.
During a normal promotion you will see the status move from running to updating and back to running. The updating state means a new release is being rolled out across your processes. Convox uses a make-one, break-one rolling update, starting a new process on the new release, verifying it passes its health check, then retiring an old one, repeating until every process is on the new release. Your service stays up the whole time.
If a new process fails to start or fails its health check during the rollout, the app moves into the rollback status automatically. Convox reverses the rollout and returns every process to the previous release. You did not have to do anything. The failed promotion simply did not take, and you are back where you started. The full list of states and what each one allows is in the app statuses reference.
Here is how the model compares to wiring this kind of safety together by hand on raw Kubernetes.
| Concern | Raw Kubernetes | Convox |
|---|---|---|
| Immutable build + config bundle | Manage image tags, ConfigMaps, and Secrets yourself and hope they stay in sync | Every Release captures both, automatically |
| Config change creates new version | Edit a Secret, then manually trigger a rollout restart | convox env set creates a release |
| Explicit ship gate | Build your own approval and apply tooling | convox releases promote by ID |
| One-command rollback | Track prior tags and config revisions, reapply by hand | convox releases rollback RID |
| Auto-revert on failed rollout | Configure probes, deadlines, and rollback logic per workload | Built in, app moves to rollback on its own |
Here is the whole devops release cycle from a code change to a confident ship, the way a lean team would actually run it. You start by building from your latest commit, which gives you an immutable artifact and a release without touching production.
# 1. Build from source, nothing ships yet
$ convox build -a myapp
Build: BNEWBUILD01
Release: RNEWREL0001
# 2. Update config if needed, creating another release
$ convox env set FEATURE_FLAG=true -a myapp
Release: RNEWREL0002
# 3. Run a migration against the new release before traffic hits it
$ convox run web bin/migrate -a myapp
# 4. Promote explicitly. This is the only step that ships.
$ convox releases promote RNEWREL0002 -a myapp
Promoting RNEWREL0002... OK
# 5. If something looks wrong, roll back to the last good release
$ convox releases rollback RPREVGOOD9 -a myapp
Every line in that sequence has a single, clear job. Building does not configure. Configuring does not ship. Shipping is one named, deliberate command. And recovery is the same command pointed backward. There is no script that does five things at once and leaves you guessing which one broke production.
This is the difference between a deploy process you hope works and a release model you can reason about. For a solo ops engineer or a small-team lead, that difference is the line between dreading Friday afternoons and not thinking twice about them.
Adopting the build, release, promote workflow does not require rebuilding your stack or learning Kubernetes. If your app already has a Dockerfile and a convox.yml, you can create your first release today and feel the difference on your very next change. Start with a build, inspect your releases, and promote one by hand to see the safety gate in action.
Ready to replace your ad hoc deploy script with a release model you can trust? The Getting Started Guide walks through installation and your first build, release, and promotion. The deploying changes guide and rollback guide cover the day-to-day flow in depth.
Create a free account and ship your first release with an explicit promote gate in minutes. For questions about adopting this workflow on your own cloud account, reach out to our team.