Ship and operate
Tag, push, and deploy the image
Publish an immutable application version to a registry and identify what a production container platform must configure around it.
A deployment starts with an image another machine can pull. So far our images live only in the local cache. To ship, we push one to a registry with a tag that names the version.
Tag and push
docker tag notes-api:prod YOUR_NAME/notes-api:1.0.0
docker login
docker push YOUR_NAME/notes-api:1.0.0
docker tag doesn’t copy anything. It adds a second name to the same image, in the format the registry expects: your Docker Hub username, the repository, the version. Replace YOUR_NAME with your username. For another registry, prefix the host, like ghcr.io/YOUR_NAME/notes-api:1.0.0.
docker login authenticates you, and docker push uploads the layers the registry doesn’t have yet. At the end it prints a line like:
1.0.0: digest: sha256:9f86d08... size: 1782
Write that digest down. We’ll come back to it.
Version tags, not latest
latest is a tag like any other, and it moves every time you push to it. If production runs notes-api:latest, you can’t tell which build is live, and you can’t roll back to “the previous latest” because it no longer exists. Give every release an explicit version, 1.0.0, 1.0.1, or the Git commit SHA.
Better still, deploy by digest when your platform allows it: YOUR_NAME/notes-api@sha256:9f86d08.... A digest is immutable. It’s the exact bytes you tested, and nobody can move it under you.
Also check the platform. An image built on an Apple Silicon Mac is arm64. A typical cloud server is amd64. Build with --platform linux/amd64 for that server, or build a multi-platform image.
The image is not the deployment
A successful pull proves that distribution works. It proves nothing else. Production still needs, around the image:
- secrets, delivered by the platform, not baked in
- published ports and a way to reach them
- persistent storage for the database volume
- health checks and resource limits
- log collection and backups
- a rollback plan
Test it in a clean environment. Pull the image on another machine, or after docker image rm locally, and run it with the production configuration.
Release records and rollback
For each release I’d keep: the image digest, the version of the configuration, the state of database migrations, and the result of a smoke test against a real path (curl on the notes endpoint counts). Keep the previous digest handy. Rolling the image back is one command.
The database is the catch. Rolling back the image doesn’t roll back a schema migration. Write migrations that the previous version of the app can still run against, at least until you’re sure the new release sticks.
Try this: push one versioned tag to a registry you control, then pull and run it on a machine that never built it.
Lesson completed