The admin version stamp reports commit: "unknown" everywhere, and #237 established that moving builds to CI does not fix it.
Why the obvious fix does not work
#233 read the commit out of .git during the build. #235 removed that, because Portainer's build context has no repository history and COPY .git failed every deploy — the version stamp became the thing that stopped deployments, which is the one outcome it must never be.
#237 then tested whether building in Gitea Actions would help, since an Actions checkout genuinely does have .git. Run 672 built and pushed the real image successfully, and the result still said:
The Dockerfile no longer copies .git, so where the build runs is irrelevant. Build location was never the problem.
The fix
A build argument. Whoever builds passes the commit in; the Dockerfile does not go looking for it.
ARG GIT_COMMIT in the backend build stage, defaulting to empty
writeBuildInfo prefers that value and falls back to reading .git as it does today
Anything that builds and knows the SHA passes --build-arg GIT_COMMIT="$(git rev-parse --short HEAD)"
Portainer cannot supply it, so a Portainer-built image keeps reporting unknown — unchanged from today, and it still deploys, which is the property #235 was bought with. Nothing regresses.
What this does not do
It does not make Portainer-built images report a commit. That needs the deployed image to come from somewhere that knows the SHA, which is the registry question in #237 — feasible, measured at 9m14s per build, and worth doing per release rather than per merge if it is done at all.
This issue is independent of that decision and worth doing either way. It costs a few lines, and without it the registry migration would deliver nothing.
Verification
Build locally with and without the arg and confirm dist/buildInfo.json reports the passed commit in the first case and unknown in the second. Build from a context with no .git — git archive HEAD produces one — and confirm the arg still works, since that is the case #235 was about.
The admin version stamp reports `commit: "unknown"` everywhere, and #237 established that moving builds to CI does not fix it.
## Why the obvious fix does not work
#233 read the commit out of `.git` during the build. #235 removed that, because Portainer's build context has no repository history and `COPY .git` failed **every** deploy — the version stamp became the thing that stopped deployments, which is the one outcome it must never be.
#237 then tested whether building in Gitea Actions would help, since an Actions checkout genuinely does have `.git`. Run 672 built and pushed the real image successfully, and the result still said:
```
git says: 3085970
image says: { "commit": "unknown", "builtAt": "2026-08-30T22:12:32Z" }
```
The Dockerfile no longer copies `.git`, so where the build runs is irrelevant. Build location was never the problem.
## The fix
A build argument. Whoever builds passes the commit in; the Dockerfile does not go looking for it.
- `ARG GIT_COMMIT` in the backend build stage, defaulting to empty
- `writeBuildInfo` prefers that value and falls back to reading `.git` as it does today
- Anything that builds and knows the SHA passes `--build-arg GIT_COMMIT="$(git rev-parse --short HEAD)"`
Portainer cannot supply it, so a Portainer-built image keeps reporting `unknown` — unchanged from today, and it still deploys, which is the property #235 was bought with. Nothing regresses.
## What this does not do
It does not make Portainer-built images report a commit. That needs the deployed image to come from somewhere that knows the SHA, which is the registry question in #237 — feasible, measured at 9m14s per build, and worth doing per release rather than per merge if it is done at all.
**This issue is independent of that decision and worth doing either way.** It costs a few lines, and without it the registry migration would deliver nothing.
## Verification
Build locally with and without the arg and confirm `dist/buildInfo.json` reports the passed commit in the first case and `unknown` in the second. Build from a context with no `.git` — `git archive HEAD` produces one — and confirm the arg still works, since that is the case #235 was about.
Follows #237, #235, #233.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The admin version stamp reports
commit: "unknown"everywhere, and #237 established that moving builds to CI does not fix it.Why the obvious fix does not work
#233 read the commit out of
.gitduring the build. #235 removed that, because Portainer's build context has no repository history andCOPY .gitfailed every deploy — the version stamp became the thing that stopped deployments, which is the one outcome it must never be.#237 then tested whether building in Gitea Actions would help, since an Actions checkout genuinely does have
.git. Run 672 built and pushed the real image successfully, and the result still said:The Dockerfile no longer copies
.git, so where the build runs is irrelevant. Build location was never the problem.The fix
A build argument. Whoever builds passes the commit in; the Dockerfile does not go looking for it.
ARG GIT_COMMITin the backend build stage, defaulting to emptywriteBuildInfoprefers that value and falls back to reading.gitas it does today--build-arg GIT_COMMIT="$(git rev-parse --short HEAD)"Portainer cannot supply it, so a Portainer-built image keeps reporting
unknown— unchanged from today, and it still deploys, which is the property #235 was bought with. Nothing regresses.What this does not do
It does not make Portainer-built images report a commit. That needs the deployed image to come from somewhere that knows the SHA, which is the registry question in #237 — feasible, measured at 9m14s per build, and worth doing per release rather than per merge if it is done at all.
This issue is independent of that decision and worth doing either way. It costs a few lines, and without it the registry migration would deliver nothing.
Verification
Build locally with and without the arg and confirm
dist/buildInfo.jsonreports the passed commit in the first case andunknownin the second. Build from a context with no.git—git archive HEADproduces one — and confirm the arg still works, since that is the case #235 was about.Follows #237, #235, #233.