fix(ci): deploy with a durable GHCR token so swarm can self-heal - #47
fix(ci): deploy with a durable GHCR token so swarm can self-heal#47vreshch wants to merge 1 commit into
Conversation
The deploy logs in with secrets.GITHUB_TOKEN and then runs "stack deploy --with-registry-auth", which freezes that credential into every service spec. GITHUB_TOKEN expires when the run ends, so from that moment Swarm cannot pull. Verified on a host: with the node itself logged in and able to pull manually, deleting a service image and scaling up still produced repeated "No such image" rejections. Swarm uses the spec credential, not config.json. GHCR_READ_TOKEN is a classic PAT scoped read:packages only, set as an agentage org secret. The build/push login keeps GITHUB_TOKEN, which needs write and is correctly ephemeral.
|
Closing: not worth the trade. All three swarms are single-node, so a rescheduled task lands on the same node where the image is already present. Against that, this change would bake a durable credential into every service spec on three hosts, add a secret dependency to 8 pipelines, and cost 8 production deploys to land. The finding stands and is worth remembering: The hosts are now logged in to GHCR with a read-only ( |
Problem
The deploy step logs in with
secrets.GITHUB_TOKEN, then runsstack deploy --with-registry-auth, which freezes that credential into every service spec.GITHUB_TOKENexpires when the workflow run ends - so from that moment the stack cannot pull from GHCR.Deploys still work (each run logs in fresh). What breaks is self-healing: a rebooted host, a rescheduled task, or a pruned image leaves the service down until someone manually re-runs a deploy.
Verified, not assumed
On a host, with the node itself logged in and able to
docker pullthe image by hand:docker service scale <svc>=0docker rmithe local imagedocker service scale <svc>=1Result: four consecutive
Rejectedtasks,No such image. Swarm uses the spec credential, not the node'sconfig.json. Recovered with a manual pull plusdocker service update --with-registry-auth.Fix
Use
GHCR_READ_TOKENfor the login that precedes--with-registry-auth, so the credential baked into the spec is valid for a year rather than minutes.GHCR_READ_TOKENis a classic PAT scopedread:packagesonly - it cannot read source, push, or delete. Set as an org secret onagentage(visibility: all).The build/push login is unchanged and still uses
GITHUB_TOKEN; that one needs write access and is correctly ephemeral.Risk
Low. Same command shape, different secret. A missing secret would fail the login loudly at deploy time rather than silently later.