{"course":{"slug":"cloud-and-shipping","title":"Shipping It","url":"https://addaly.com/learn/cloud-and-shipping","level":"intermediate","track":"build"},"lesson":{"slug":"what-is-actually-running","title":"Which commit is live right now","url":"https://addaly.com/learn/cloud-and-shipping/what-is-actually-running","position":12,"of":74,"minutes":8,"answer":"Stamp the commit SHA into the artifact and serve it from a /version route.","keyIdea":"Stamp the commit SHA into the artifact and serve it from a /version route, because \"which code is running on the machine that answered this request\" is the first question in every incident and the only one you can make free to answer."},"body":{"format":"markdown","text":"## The question you cannot answer at the worst moment\n\nSomething is wrong on the site. The first useful question is \"what changed\", and the second is \"is the fix already out\". Both need one fact: which commit is serving requests, right now, on the machine the user hit.\n\nMost small projects cannot answer it. The deploy log says a deploy finished. The dashboard says the service is healthy. Neither tells you whether the instance answering this particular request is running the build from twenty minutes ago or the one from Tuesday, and during a rolling deploy the honest answer is \"both\".\n\nThe fix takes about fifteen minutes and pays for itself the first time you use it.\n\n## Stamp the build, then serve the stamp\n\nAt build time, put the facts into the artifact. In a Dockerfile:\n\n```dockerfile\nARG GIT_SHA\nARG BUILT_AT\nENV GIT_SHA=$GIT_SHA BUILT_AT=$BUILT_AT\n```\n\nBuilt from CI:\n\n```bash\ndocker build \\\n  --build-arg GIT_SHA=$(git rev-parse --short HEAD) \\\n  --build-arg BUILT_AT=$(date -u +%FT%TZ) -t myapp:$(git rev-parse --short HEAD) .\n```\n\nThen expose it. One route, no authentication needed if it contains nothing secret:\n\n```json\nGET /version\n{ \"sha\": \"a41f9c2\", \"built\": \"2026-09-02T11:04:19Z\", \"env\": \"production\", \"host\": \"web-3\" }\n```\n\nNow \"which commit is live\" is a `curl`. Hit it repeatedly during a rolling deploy and you will watch the answer flip between two SHAs, which is the most direct demonstration of why two versions of your code run at once: the fact that makes migrations hard, three lessons from here.\n\nInclude the host or instance ID. \"It works for me but not for my colleague\" is very often one bad instance, and without an instance identifier in the response you cannot see it.\n\n## Tag the artifact with the commit, never with `latest`\n\n`myapp:latest` is a moving label. Two machines pulling `latest` an hour apart can run different code, and a rollback to `latest` rolls back to nothing in particular. Tag images with the commit SHA, and let `latest` be an extra alias if your tooling needs one.\n\nThe same applies to a release tag. `v1.4.0` is for humans; the SHA is the identity. Keep both, and make the deploy record say which SHA a version number referred to, because tags can be moved and SHAs cannot.\n\n## Tell your tools which release this is\n\nEvery observability tool has a field for the release, and almost nobody fills it in:\n\n- Send the SHA to your error tracker as the release. It will then tell you \"this error first appeared in a41f9c2\", which converts a mystery into a diff.\n- Add it as a label on your metrics, but only as a low-cardinality one: a release label is fine, a per-request label is not.\n- Log it once at startup, so the journal for a crashed instance says what it was running.\n\nThe payoff is a sentence you can say out loud in an incident: \"errors started at 14:02, the deploy of a41f9c2 finished at 14:01, here is the diff, roll it back.\"\n\n## A deploy log you can read\n\nOne append-only file or table, one line per deploy: time, SHA, who or what triggered it, and the result. Most platforms keep this for you; if yours does not, a GitHub Actions step that appends to a file is enough.\n\nThe reason to have it is that outages correlate with changes, and the changes are not only yours: a dependency updated, an environment variable edited by hand, a DNS record changed, a provider migrating your instance. A shared timeline of everything that touched production is the cheapest incident tool that exists.\n\n## Provenance, briefly and honestly\n\nA newer layer of this is the software bill of materials (a machine-readable list of what went into a build) and signed provenance attestations proving that this artifact came from that commit through that pipeline. Tools exist and are free: Syft generates an SBOM, Cosign signs artifacts, and GitHub's build attestations do both with a few lines in a workflow.\n\nFor a small site this is not urgent, and pretending otherwise wastes your attention. It matters when somebody else depends on your artifact, or when a compromised build server is in your threat model. Know that it exists, know the words, and reach for it when you have a reason rather than because a vendor page said supply chain.\n\n## The test\n\nAsk somebody to run one command and tell you what is live. If they have to open a dashboard, guess from a deploy time, or SSH somewhere, this lesson is not done."},"check":{"question":"During a rolling deploy you curl `/version` five times and get two different SHAs. What does that tell you?","options":[{"text":"The build is non-deterministic, producing two artifacts from one commit","correct":false,"tempting":"Confuses two versions being live with two builds of one version; the SHAs differ because they came from different commits, not different builds."},{"text":"The load balancer is caching the response and serving a stale copy","correct":false,"tempting":"A cache would return one stale answer consistently, not alternate between two, and a version route is normally uncacheable anyway."},{"text":"Old and new instances are both serving traffic, which is the normal state during a rolling deploy","correct":true},{"text":"The deploy has failed and rolled part of the fleet back","correct":false,"tempting":"Possible but far less likely than the ordinary case; a partial rollback and a deploy in progress look identical from outside, which is why the deploy log matters."}]},"prev":{"slug":"build-once-run-anywhere","title":"The same build, everywhere","url":"https://addaly.com/learn/cloud-and-shipping/build-once-run-anywhere"},"next":{"slug":"a-pipeline-you-trust","title":"The checks that run without you","url":"https://addaly.com/learn/cloud-and-shipping/a-pipeline-you-trust"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Which commit is live right now', in Shipping It. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/cloud-and-shipping/what-is-actually-running (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Which commit is live right now","courseTitle":"Shipping It","module":{"index":2,"slug":"making-a-deploy-boring","title":"Making a deploy boring"},"answer":null,"keyIdea":"Stamp the commit SHA into the artifact and serve it from a /version route, because “which code is running on the machine that answered this request” is the first question in every incident and the only one you can make free to answer.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"the-question-you-cannot-answer-at-the-worst-moment","runs":[{"text":"The question you cannot answer at the worst moment"}]},{"t":"p","runs":[{"text":"Something is wrong on the site. The first useful question is “what changed”, and the second is “is the fix already out”. Both need one fact: which commit is serving requests, right now, on the machine the user hit."}]},{"t":"p","runs":[{"text":"Most small projects cannot answer it. The deploy log says a deploy finished. The dashboard says the service is healthy. Neither tells you whether the instance answering this particular request is running the build from twenty minutes ago or the one from Tuesday, and during a rolling deploy the honest answer is “both”."}]},{"t":"p","runs":[{"text":"The fix takes about fifteen minutes and pays for itself the first time you use it."}]},{"t":"h2","id":"stamp-the-build-then-serve-the-stamp","runs":[{"text":"Stamp the build, then serve the stamp"}]},{"t":"p","runs":[{"text":"At build time, put the facts into the artifact. In a Dockerfile:"}]},{"t":"code","lang":"dockerfile","label":"Dockerfile","text":"ARG GIT_SHA\nARG BUILT_AT\nENV GIT_SHA=$GIT_SHA BUILT_AT=$BUILT_AT"},{"t":"p","runs":[{"text":"Built from CI:"}]},{"t":"code","lang":"bash","label":"Bash","text":"docker build \\\n  --build-arg GIT_SHA=$(git rev-parse --short HEAD) \\\n  --build-arg BUILT_AT=$(date -u +%FT%TZ) -t myapp:$(git rev-parse --short HEAD) ."},{"t":"p","runs":[{"text":"Then expose it. One route, no authentication needed if it contains nothing secret:"}]},{"t":"code","lang":"json","label":"JSON","text":"GET /version\n{ \"sha\": \"a41f9c2\", \"built\": \"2026-09-02T11:04:19Z\", \"env\": \"production\", \"host\": \"web-3\" }"},{"t":"p","runs":[{"text":"Now “which commit is live” is a "},{"text":"curl","code":true},{"text":". Hit it repeatedly during a rolling deploy and you will watch the answer flip between two SHAs, which is the most direct demonstration of why two versions of your code run at once: the fact that makes migrations hard, three lessons from here."}]},{"t":"p","runs":[{"text":"Include the host or instance ID. “It works for me but not for my colleague” is very often one bad instance, and without an instance identifier in the response you cannot see it."}]},{"t":"h2","id":"tag-the-artifact-with-the-commit-never-with-latest","runs":[{"text":"Tag the artifact with the commit, never with "},{"text":"latest","code":true}]},{"t":"p","runs":[{"text":"myapp:latest","code":true},{"text":" is a moving label. Two machines pulling "},{"text":"latest","code":true},{"text":" an hour apart can run different code, and a rollback to "},{"text":"latest","code":true},{"text":" rolls back to nothing in particular. Tag images with the commit SHA, and let "},{"text":"latest","code":true},{"text":" be an extra alias if your tooling needs one."}]},{"t":"p","runs":[{"text":"The same applies to a release tag. "},{"text":"v1.4.0","code":true},{"text":" is for humans; the SHA is the identity. Keep both, and make the deploy record say which SHA a version number referred to, because tags can be moved and SHAs cannot."}]},{"t":"h2","id":"tell-your-tools-which-release-this-is","runs":[{"text":"Tell your tools which release this is"}]},{"t":"p","runs":[{"text":"Every observability tool has a field for the release, and almost nobody fills it in:"}]},{"t":"ul","items":[[{"text":"Send the SHA to your error tracker as the release. It will then tell you “this error first appeared in a41f9c2”, which converts a mystery into a diff."}],[{"text":"Add it as a label on your metrics, but only as a low-cardinality one: a release label is fine, a per-request label is not."}],[{"text":"Log it once at startup, so the journal for a crashed instance says what it was running."}]]},{"t":"p","runs":[{"text":"The payoff is a sentence you can say out loud in an incident: “errors started at 14:02, the deploy of a41f9c2 finished at 14:01, here is the diff, roll it back.”"}]},{"t":"h2","id":"a-deploy-log-you-can-read","runs":[{"text":"A deploy log you can read"}]},{"t":"p","runs":[{"text":"One append-only file or table, one line per deploy: time, SHA, who or what triggered it, and the result. Most platforms keep this for you; if yours does not, a GitHub Actions step that appends to a file is enough."}]},{"t":"p","runs":[{"text":"The reason to have it is that outages correlate with changes, and the changes are not only yours: a dependency updated, an environment variable edited by hand, a DNS record changed, a provider migrating your instance. A shared timeline of everything that touched production is the cheapest incident tool that exists."}]},{"t":"h2","id":"provenance-briefly-and-honestly","runs":[{"text":"Provenance, briefly and honestly"}]},{"t":"p","runs":[{"text":"A newer layer of this is the software bill of materials (a machine-readable list of what went into a build) and signed provenance attestations proving that this artifact came from that commit through that pipeline. Tools exist and are free: Syft generates an SBOM, Cosign signs artifacts, and GitHub’s build attestations do both with a few lines in a workflow."}]},{"t":"p","runs":[{"text":"For a small site this is not urgent, and pretending otherwise wastes your attention. It matters when somebody else depends on your artifact, or when a compromised build server is in your threat model. Know that it exists, know the words, and reach for it when you have a reason rather than because a vendor page said supply chain."}]},{"t":"h2","id":"the-test","runs":[{"text":"The test"}]},{"t":"p","runs":[{"text":"Ask somebody to run one command and tell you what is live. If they have to open a dashboard, guess from a deploy time, or SSH somewhere, this lesson is not done."}]}],"check":{"question":[{"text":"During a rolling deploy you curl "},{"text":"/version","code":true},{"text":" five times and get two different SHAs. What does that tell you?"}],"options":[{"text":[{"text":"The build is non-deterministic, producing two artifacts from one commit"}],"correct":false,"tempting":[{"text":"Confuses two versions being live with two builds of one version; the SHAs differ because they came from different commits, not different builds."}]},{"text":[{"text":"The load balancer is caching the response and serving a stale copy"}],"correct":false,"tempting":[{"text":"A cache would return one stale answer consistently, not alternate between two, and a version route is normally uncacheable anyway."}]},{"text":[{"text":"Old and new instances are both serving traffic, which is the normal state during a rolling deploy"}],"correct":true,"tempting":null},{"text":[{"text":"The deploy has failed and rolled part of the fleet back"}],"correct":false,"tempting":[{"text":"Possible but far less likely than the ordinary case; a partial rollback and a deploy in progress look identical from outside, which is why the deploy log matters."}]}],"order":[1,3,0,2],"why":null},"prev":{"slug":"build-once-run-anywhere","title":"The same build, everywhere","minutes":9},"next":{"slug":"a-pipeline-you-trust","title":"The checks that run without you","minutes":9}}}