No release has ever published a container image. The v5.0.0 release run surfaced it, but it is not new and 5.0.0 did not cause it.
What this affects
The all-in-one image is Stratos packaged as a single container — a Debian base holding the compiled jetstream backend, the prebuilt UI, templates and certs. It exists so someone can run the console with one docker run, with no Cloud Foundry deployment and no building from source. The same image also works with cf push -o <image>: it detects VCAP_APPLICATION at runtime and serves HTTP on $PORT instead of HTTPS on 5443.
If it published, operators would get ghcr.io/cloudfoundry/stratos:5.0.0. Today the newest thing under that name is a single tag, stable, pushed by some earlier process that predates the current workflows.
The GitHub release itself is unaffected — v5.0.0 has its nine downloadable artifacts and correct notes. Only the container distribution channel is missing.
What happens
Build All-in-One Image builds the image successfully — all layers, manifest exported — and then fails on the upload:
#16 pushing layers 0.8s done
#16 ERROR: failed to push ghcr.io/cloudfoundry/stratos:5.0.0: denied: permission_denied: write_package
Step outcomes for that job show where the line falls:
Log in to GitHub Container Registry: success
Extract metadata: success
Build and push all-in-one image: failure
Authentication succeeds. Authorization fails. It is not a missing or expired credential — a valid, logged-in token is being told it may not write that particular package.
It has never worked
The job was added on 2026-07-20 (48efb46493). It has run in exactly two releases and failed identically in both:
| Release |
Date |
Result |
v5.0.0-dev.147 |
2026-07-29 |
failure — denied: permission_denied |
v5.0.0 |
2026-08-06 |
failure — denied: permission_denied |
Every earlier release run predates the job, which is why they look clean. The same failure makes each release run report failure overall even though Create GitHub Release succeeded and the release is fine — so the red tick is misleading rather than informative.
This is not limited to releases. Every workflow that pushes to GHCR fails the same way:
| Workflow |
Credential |
Recent result |
container-push-base-images-develop.yaml |
GH_PACKAGES_USER / GH_PACKAGES_TOKEN |
steps skipped — secrets not set |
release.yml (all-in-one) |
GITHUB_TOKEN |
failure x2 |
docker.yml |
GITHUB_TOKEN |
failure x3 |
ci-image.yml |
GITHUB_TOKEN |
failure |
The one workflow written to use a personal access token skips silently: it checks whether the secrets exist, warns "Stratos containers are not supported at this time", and does nothing. It has been reporting green for a no-op. The repository's only configured secret is GH_PAGES_DEPLOY, from 2020.
Why the push is refused
On GitHub a container image is a package with its own access control, separate from the repository. release.yml correctly requests packages: write, but GITHUB_TOKEN is a per-run token belonging to the repository — it can write packages the repository owns, not one that already exists without the repository having been granted write on it. ghcr.io/cloudfoundry/stratos is public and predates these workflows, so the token has no standing on it.
The username in the login step (github.actor) is incidental; GHCR authorizes on the token, so triggering a release from a different account changes nothing.
Two ways to fix it
- Grant the
cloudfoundry/stratos repository the Write role on the existing package, in the package's settings under Manage Actions access. No new credential, nothing to rotate. Requires admin on the package.
- Supply a personal access token with
write:packages, belonging to an account that can write that package, and point the login steps at it. Works regardless of the package's Actions-access configuration, but adds a long-lived secret to own and rotate.
Either way the workflow logic is unchanged apart from which credential the login step uses.
Unresolved
Which of the two situations applies — package access not granted, or an organization policy restricting who may publish packages — cannot be determined without read:packages on the organization. Both produce this identical error.
Also worth deciding separately: container-push-base-images-develop.yaml targets ghcr.io/anynines/… rather than cloudfoundry, so it wants a different credential than the release image does. Reusing one secret name for both would point a token at a namespace it was not meant for.
Worth noting
The skip-with-a-warning guard in container-push-base-images-develop.yaml is the better pattern for an optional credential. The other three build for minutes and then die at the upload. Whatever is decided here, that difference is worth making consistent.
No release has ever published a container image. The
v5.0.0release run surfaced it, but it is not new and 5.0.0 did not cause it.What this affects
The all-in-one image is Stratos packaged as a single container — a Debian base holding the compiled
jetstreambackend, the prebuilt UI, templates and certs. It exists so someone can run the console with onedocker run, with no Cloud Foundry deployment and no building from source. The same image also works withcf push -o <image>: it detectsVCAP_APPLICATIONat runtime and serves HTTP on$PORTinstead of HTTPS on 5443.If it published, operators would get
ghcr.io/cloudfoundry/stratos:5.0.0. Today the newest thing under that name is a single tag,stable, pushed by some earlier process that predates the current workflows.The GitHub release itself is unaffected —
v5.0.0has its nine downloadable artifacts and correct notes. Only the container distribution channel is missing.What happens
Build All-in-One Imagebuilds the image successfully — all layers, manifest exported — and then fails on the upload:Step outcomes for that job show where the line falls:
Authentication succeeds. Authorization fails. It is not a missing or expired credential — a valid, logged-in token is being told it may not write that particular package.
It has never worked
The job was added on 2026-07-20 (
48efb46493). It has run in exactly two releases and failed identically in both:v5.0.0-dev.147denied: permission_deniedv5.0.0denied: permission_deniedEvery earlier release run predates the job, which is why they look clean. The same failure makes each release run report
failureoverall even thoughCreate GitHub Releasesucceeded and the release is fine — so the red tick is misleading rather than informative.This is not limited to releases. Every workflow that pushes to GHCR fails the same way:
container-push-base-images-develop.yamlGH_PACKAGES_USER/GH_PACKAGES_TOKENrelease.yml(all-in-one)GITHUB_TOKENdocker.ymlGITHUB_TOKENci-image.ymlGITHUB_TOKENThe one workflow written to use a personal access token skips silently: it checks whether the secrets exist, warns "Stratos containers are not supported at this time", and does nothing. It has been reporting green for a no-op. The repository's only configured secret is
GH_PAGES_DEPLOY, from 2020.Why the push is refused
On GitHub a container image is a package with its own access control, separate from the repository.
release.ymlcorrectly requestspackages: write, butGITHUB_TOKENis a per-run token belonging to the repository — it can write packages the repository owns, not one that already exists without the repository having been granted write on it.ghcr.io/cloudfoundry/stratosis public and predates these workflows, so the token has no standing on it.The
usernamein the login step (github.actor) is incidental; GHCR authorizes on the token, so triggering a release from a different account changes nothing.Two ways to fix it
cloudfoundry/stratosrepository the Write role on the existing package, in the package's settings under Manage Actions access. No new credential, nothing to rotate. Requires admin on the package.write:packages, belonging to an account that can write that package, and point the login steps at it. Works regardless of the package's Actions-access configuration, but adds a long-lived secret to own and rotate.Either way the workflow logic is unchanged apart from which credential the login step uses.
Unresolved
Which of the two situations applies — package access not granted, or an organization policy restricting who may publish packages — cannot be determined without
read:packageson the organization. Both produce this identical error.Also worth deciding separately:
container-push-base-images-develop.yamltargetsghcr.io/anynines/…rather thancloudfoundry, so it wants a different credential than the release image does. Reusing one secret name for both would point a token at a namespace it was not meant for.Worth noting
The skip-with-a-warning guard in
container-push-base-images-develop.yamlis the better pattern for an optional credential. The other three build for minutes and then die at the upload. Whatever is decided here, that difference is worth making consistent.