When you deploy with Burrow, you do not hand it your source code. You hand it a name: a reference to a container image sitting in a registry. Burrow reads that reference and runs it. That single design choice shapes how deploys feel, how safe they are, and how an AI agent can operate your cluster without ever touching your code. This guide unpacks what it means and why we built it this way.
The one line version
A deploy looks like this:
burrow app deploy web --image ghcr.io/you/web:1.4.0
ghcr.io/you/web:1.4.0 is the image reference. It points to a specific, already built container image in a registry. Burrow does not build it, does not receive it, and never sees your source. It takes the reference, tells your cluster to pull that image, and rolls it out. That is the whole contract.
How the pieces fit together
There are three actors, and keeping them separate is the point.
You build the image. With docker build, or your CI pipeline, or whatever tooling you already use. This is where your source code lives and stays.
A registry holds the image. GitHub Container Registry, your cloud's registry, or any registry your cluster can pull from. The image travels from your build to the registry the normal way.
Burrow deploys by reference. It carries only the small stuff: the image name and tag, your ordinary config, a command to run. It tells the cluster to pull and run the image. Your code moves through the registry, never through Burrow.
Think of it as a remote control and a conveyor belt. Burrow is the remote control: it says run this, roll that back, scale this up. The registry is the conveyor belt that actually carries the built image to your cluster. The two never get mixed up, and that separation is what makes the rest of the safety story hold.
Why not just push code and let the platform build it?
That is the model a lot of platforms use: connect a repo, push, and their build system turns your code into something runnable on their side. It is convenient, and for a while it feels magical. But it comes with strings.
It means your code flows through their infrastructure. It means the build happens in an environment you do not fully control, with its own versions and its own quirks, so "works on my machine" and "works in their builder" can drift apart. And it means the thing that runs is a step removed from something you can hold in your hand and inspect.
Deploying by image reference flips all of that. The artifact you built is the artifact that runs. Byte for byte, the image you tested locally or in CI is the image on your cluster. There is no second build in a different place to surprise you.
Why this matters more when an agent is driving
Here is where the choice really earns its keep. When you point an AI coding agent at your cluster, you want it to be able to deploy, roll back, and operate your app. What you very much do not want is your source code streaming through the agent's connection, sitting in a conversation, getting sent again on every later message.
Because Burrow deploys by reference, none of that happens. The agent builds an image with its own tooling and pushes it to your registry, exactly as you would. Then it deploys by naming the reference. The only things that cross the agent's channel are the reference and a little metadata. Your code takes the registry path, the same path it always takes. The agent operates your app without ever carrying it.
That is not a happy accident. It is a hard line in Burrow's design: code never travels over the agent's control channel. The channel is the remote control; the registry is the conveyor belt. Keeping them separate means an agent can have real operational power over your app while your source stays exactly where it belongs.
What an agent actually does, step by step
It helps to trace the whole loop, because it makes clear how little crosses the channel. When you ask your agent to ship a change, here is what happens:
- The agent builds an image from your source with its own tooling (
docker build), on the machine where the code already lives. - It pushes that image to your registry with a fresh, climbing tag, the same registry your cluster pulls from.
- It deploys by naming the reference through its scoped surface, roughly
burrow-agent deploy web --image ghcr.io/you/web:1.4.1. - It reads the result back:
burrow-agent status webto confirm the workload is available,burrow-agent logs webif it is not.
Only steps 3 and 4 touch Burrow's control channel, and all they carry is a reference and a little metadata. The code moved in steps 1 and 2, over the registry path, exactly the way your own docker push moves it. The agent also holds no cluster credentials of its own: it asks the control plane to act, and the control plane, which does hold the credentials, decides whether to. So the agent has real reach without ever holding the keys or carrying the code.
Any registry your cluster can reach
Burrow does not care where the image lives, only that your cluster can pull it. GitHub Container Registry, your cloud provider's registry, Docker Hub, a self hosted registry, they all work the same way: you push there, and you deploy by naming the reference.
If the registry is public, there is nothing to configure. If it is private, you tell Burrow how to authenticate to it once:
burrow registry login ghcr.io
You run that yourself, at your own terminal, because it involves a credential, and it is a one time setup rather than something you repeat per deploy. From then on, Burrow can pull your private images and roll them out. This is also the fix for the most common first deploy failure: an image that will not pull because the cluster has no credentials for a private registry. One login, and the pull succeeds.
The same image, everywhere
There is a quiet benefit to deploying by reference that pays off the moment you have more than one environment. Because a deploy names a specific image, promoting a change from staging to prod is not a rebuild, it is a redeploy of the exact same artifact.
burrow env use staging
burrow app deploy web --image ghcr.io/you/web:1.4.0
# looks good, promote the identical image
burrow env use prod
burrow app deploy web --image ghcr.io/you/web:1.4.0
The image you proved in staging is byte for byte the image that runs in prod. Nothing was rebuilt in between, so nothing new could have snuck in: no different base image, no dependency that resolved to a newer version, no build environment quirk. What you tested is what ships. Systems that rebuild per environment cannot make that promise, and the gap between "what I tested" and "what deployed" is where a surprising number of production incidents actually live.
The habit that makes it work: unique, climbing tags
Deploying by reference gives you one responsibility in return: tag your images uniquely, and let the numbers climb.
docker build -t ghcr.io/you/web:1.4.0 .
docker push ghcr.io/you/web:1.4.0
burrow app deploy web --image ghcr.io/you/web:1.4.0
Next change, 1.4.1. A feature, 1.5.0. Never reuse a tag, and never deploy a floating tag like latest. Here is why it matters so much.
A tag like latest is a moving target. Deploy it today and deploy it next week and you may get two different images with no record of the change. Worse, "roll back to the previous version" becomes meaningless, because the previous version and the current version might point at the same tag. When every deploy names a distinct image, your release history is real: each release is a specific, reproducible artifact, and going back to the last one actually goes back to something different and known.
Burrow keeps that previous release warm precisely so a rollback is fast. Unique tags are what make the rollback point at the right thing.
What Burrow does carry
If code stays out of the channel, what does a deploy actually send? Small, legible metadata:
- The image reference itself.
- Ordinary config, which you set separately with
burrow app config set web KEY=VALUE. - Optionally a command to run in the image, and how many copies to run.
Secrets are their own story and deliberately do not ride along in the open. You set them once, at your own terminal, and Burrow stores them safely for the app. An agent can confirm a secret key exists but can never read or set its value. The image reference plus this thin metadata is everything a deploy needs, and it is all small enough to read at a glance.
Seeing exactly what shipped
Because every deploy names a specific image, Burrow can always tell you precisely what is running, not "roughly the latest" but the exact reference:
burrow app status web
app: web
release: 9f2c1a04... (image ghcr.io/you/web:1.4.0, deployed)
workload: 1/1 replicas ready, available
Every deploy is release stamped and rolls the workload, so the running release and the image behind it are never a guess. If you want the fuller history of who deployed what and when, the audit trail has it:
burrow audit
That record is only meaningful because the tags are unique. If every deploy named latest, the history would be a wall of identical lines. When each release names a distinct artifact, the audit reads like an actual changelog of your infrastructure, and a rollback points at something real:
burrow app rollback web
Burrow keeps the previous release warm, so that command shifts traffic back to a known image in a moment, no rebuild required.
Explicit, not automatic
One more deliberate choice worth naming. Burrow deploys when you call deploy. It does not sit and watch a tag, silently rolling out whatever appears there. That kind of automatic pickup can exist as an option, but it is never the spine, because the explicit call is where the good stuff lives: the guardrails that can hold a prod deploy for your sign off, the structured result you can read, the record of exactly what shipped and when.
An explicit burrow app deploy web --image ... is a decision with your name on it. That is what you want a deploy to be, especially when an agent might be the one proposing it. A deploy that happens because a tag quietly moved is a deploy nobody decided, and "nobody decided that" is a bad thing to discover during an incident. Naming the image every time keeps every rollout attributable to a moment and an intent.
Common questions
A few things that come up once people start deploying this way.
Do I have to use Docker to build? No. Burrow only cares that a runnable image lands in a registry your cluster can pull from. However you get it there, docker build, a CI pipeline, a different build tool, the deploy is the same: name the reference.
What if I really want to deploy latest? You can, Burrow will run whatever reference you give it, but you lose the two things that make this model worth it: a real release history and a meaningful rollback. If two deploys can name the same moving tag, "the previous version" stops meaning anything. Unique, climbing tags are cheap, and they buy you a rollback you can trust.
Where do secrets go, if not in the deploy? Not over the channel, and not in the image. You set them once at your own terminal with burrow app secret set web KEY, Burrow prompts for the value privately, and it stores it for the app. An agent can confirm a key exists but can never read or set the value. The deploy carries only the reference and small config, never the sensitive material.
Can I promote the same image across environments? Yes, and that is the point. burrow env use staging then deploy, prove it, then burrow env use prod and deploy the identical reference. No rebuild sits between staging and prod to let something new sneak in.
What actually crosses the agent's channel? The image reference, ordinary config, an optional command, and how many copies to run. That is it. Your source takes the registry path every time.
The takeaway
Deploy by image reference sounds like a technical detail and is really a design philosophy. Build the artifact once, with your own tooling. Store it in a registry, the normal way. Deploy it by name, and let Burrow run the exact thing you built. Your code never passes through Burrow or through your agent, your releases are real and reproducible, and going back is always possible because every version has a name. Convenience that costs you clarity is a bad trade. This keeps both.