QA Team — sweep 2026-07-14 ~21:15Z. Filed against closed#548 (Multi-arch create_manifest amends MUTABLE :amd64/:arm64v8 tags). #548 named the mechanism. This is the damage it left behind, and it is still live.
The bug
14 deployed services have a :latest manifest list whose child manifests do not exist in the registry. Pulling any of them fails. They keep running only because the image is cached on the node they happen to be sitting on.
Worked example — spikersoft-coderunner:latest:
$ GET /v2/spikerj/spikersoft-coderunner/manifests/latest
INDEX (manifest.list.v2)
linux/amd64 sha256:8b2dad884ba68...
linux/arm64 sha256:e91db1f3be049...
$ GET /v2/.../manifests/sha256:8b2dad884ba68... -> HTTP 404
$ GET /v2/.../manifests/sha256:e91db1f3be049... -> HTTP 404
$ GET /v2/.../blobs/sha256:e91db1f3be049... -> HTTP 404
Both children 404. Meanwhile the standalone per-arch tag is healthy and points somewhere else entirely:
$ GET /v2/.../manifests/arm64v8 -> HTTP 200, digest sha256:6ce3457e4bd0a...
index says arm64 = sha256:e91db1f3be049... <-- different, and gone
Not a media-type artifact: the 404 reproduces with Accept: */*, and the :arm64v8 control returns 200 with identical headers.
create_manifest stitches :latest from the mutable:amd64 / :arm64v8 tags, capturing their digests at that moment.
A later build overwrites:amd64 / :arm64v8 with new digests.
The old manifests are now untagged, and the registry garbage-collects them.
:latest still references the deleted digests → dangling index.
#548 identified the mutable-tag race. What nobody wrote down is the consequence: when the per-arch tag moves, the old digest is GC'd and :latest rots. Closing #548 did not repair the already-rotted indexes.
#562 ("dreamstream nodes reject fresh tasks with No such image") is not a UDM hairpin, not a slow pull, not registry auth. Verified:
coderunner passes --with-registry-auth✅
jetson disk usage is 16–36% across all 7 nodes (via InfluxDB jetsons bucket) — not a disk problem ✅
ds1/ds4/ds5 are the same architecture✅
The asymmetry is entirely explained: ds5 has the image cached (its task has been Running 6 h). ds1 and ds4 must actually pull:latest, they follow the index to a 404, and Docker reports the only thing it can — No such image. They will reject forever; there is nothing to converge to. That is why coderunner has sat at 3/4 all day rather than self-healing in "1-3 minutes" as #562 assumed.
Blast radius — this is a latent cluster-wide outage
Every one of these is unpullable right now. They survive only on cached images:
service
replicas
spikersoft-coderunner
3/4 — already failing
spikersoft-backend (the API)
1/1
spikersoft-notifications
1/1
spikersoft-gpu-coordinator
1/1
spikersoft-file-movement
1/1
spikersoft-book-management
1/1
spikersoft-blog-media-processor
1/1
spikersoft-game-events
1/1
spikersoft-keycloak-events
1/1
spikersoft-influx-dashboard
1/1
spikersoft-calendar-reminders
1/1
spikersoft-artstudio-metrics
1/1
spikersoft-docker-monitor
1/1
spikersoft-gameserver-init
0/1
Any event that forces a fresh pull — a node reboot, a failover, a rescheduling, a scale-out, docker system prune on a node — takes that service to zero and it cannot come back. The API is on this list.
This also retro-explains the "No such image" reject-loops in #511 and the ones seen during the ds4/ds6 outages (#552): those nodes were being asked to pull images whose :latest was already dangling.
Immediate remediation
Re-stitch :latest for all 14 from their current, healthy per-arch tags — i.e. re-run create_manifest (or docker buildx imagetools create -t <img>:latest <img>:amd64 <img>:arm64v8). That is a registry-side fix and needs no code change. Do coderunner first — it is the one already degraded.
Real fix
create_manifest must not stitch from mutable tags whose digests can be GC'd out from under it. Options:
Tag per-arch images immutably (:amd64-<commit-sha>) and stitch :latest from those, so the referenced digests are never orphaned; or
Push the arch manifests by digest and build the index in the same job that pushed them (buildx --push with a single multi-platform build does this atomically — one build, one index, no window); or
At minimum, add a post-push verification step: after create_manifest, resolve every child of :latest and fail the job if any 404s. That turns this from a silent time-bomb into a red build.
The verification step is worth adding regardless — it is ~5 lines and it would have caught all 14 of these at the moment they broke, instead of months later via a stuck replica.
Related: closed #548 (the mechanism), #562 (root-caused by this — should be closed in favour of this ticket or linked), #511, #552, #584.
**QA Team** — sweep 2026-07-14 ~21:15Z. Filed against **closed #548** (*Multi-arch create_manifest amends MUTABLE :amd64/:arm64v8 tags*). #548 named the mechanism. This is the damage it left behind, and it is still live.
## The bug
**14 deployed services have a `:latest` manifest list whose child manifests do not exist in the registry.** Pulling any of them fails. They keep running *only* because the image is cached on the node they happen to be sitting on.
Worked example — `spikersoft-coderunner:latest`:
```
$ GET /v2/spikerj/spikersoft-coderunner/manifests/latest
INDEX (manifest.list.v2)
linux/amd64 sha256:8b2dad884ba68...
linux/arm64 sha256:e91db1f3be049...
$ GET /v2/.../manifests/sha256:8b2dad884ba68... -> HTTP 404
$ GET /v2/.../manifests/sha256:e91db1f3be049... -> HTTP 404
$ GET /v2/.../blobs/sha256:e91db1f3be049... -> HTTP 404
```
**Both children 404.** Meanwhile the standalone per-arch tag is healthy and points somewhere else entirely:
```
$ GET /v2/.../manifests/arm64v8 -> HTTP 200, digest sha256:6ce3457e4bd0a...
index says arm64 = sha256:e91db1f3be049... <-- different, and gone
```
Not a media-type artifact: the 404 reproduces with `Accept: */*`, and the `:arm64v8` control returns **200** with identical headers.
## Mechanism (this is #548, playing out)
1. `create_manifest` stitches `:latest` from the **mutable** `:amd64` / `:arm64v8` tags, capturing their digests *at that moment*.
2. A later build **overwrites** `:amd64` / `:arm64v8` with new digests.
3. The old manifests are now untagged, and the registry garbage-collects them.
4. `:latest` still references the deleted digests → **dangling index**.
#548 identified the mutable-tag race. What nobody wrote down is the consequence: **when the per-arch tag moves, the old digest is GC'd and `:latest` rots.** Closing #548 did not repair the already-rotted indexes.
## This is the root cause of #562
#562 ("dreamstream nodes reject fresh tasks with `No such image`") is not a UDM hairpin, not a slow pull, not registry auth. Verified:
- coderunner passes `--with-registry-auth` ✅
- jetson disk usage is 16–36% across all 7 nodes (via InfluxDB `jetsons` bucket) — **not** a disk problem ✅
- ds1/ds4/ds5 are the **same architecture** ✅
The asymmetry is entirely explained: **ds5 has the image cached** (its task has been Running 6 h). ds1 and ds4 must actually *pull* `:latest`, they follow the index to a 404, and Docker reports the only thing it can — `No such image`. They will reject forever; there is nothing to converge to. That is why coderunner has sat at 3/4 all day rather than self-healing in "1-3 minutes" as #562 assumed.
## Blast radius — this is a latent cluster-wide outage
Every one of these is **unpullable right now**. They survive only on cached images:
| service | replicas |
|---|---|
| spikersoft-coderunner | **3/4 — already failing** |
| spikersoft-backend (the API) | 1/1 |
| spikersoft-notifications | 1/1 |
| spikersoft-gpu-coordinator | 1/1 |
| spikersoft-file-movement | 1/1 |
| spikersoft-book-management | 1/1 |
| spikersoft-blog-media-processor | 1/1 |
| spikersoft-game-events | 1/1 |
| spikersoft-keycloak-events | 1/1 |
| spikersoft-influx-dashboard | 1/1 |
| spikersoft-calendar-reminders | 1/1 |
| spikersoft-artstudio-metrics | 1/1 |
| spikersoft-docker-monitor | 1/1 |
| spikersoft-gameserver-init | 0/1 |
**Any** event that forces a fresh pull — a node reboot, a failover, a rescheduling, a scale-out, `docker system prune` on a node — takes that service to **zero and it cannot come back**. The API is on this list.
This also retro-explains the "No such image" reject-loops in **#511** and the ones seen during the ds4/ds6 outages (**#552**): those nodes were being asked to pull images whose `:latest` was already dangling.
## Immediate remediation
Re-stitch `:latest` for all 14 from their current, healthy per-arch tags — i.e. re-run `create_manifest` (or `docker buildx imagetools create -t <img>:latest <img>:amd64 <img>:arm64v8`). That is a registry-side fix and needs no code change. **Do coderunner first** — it is the one already degraded.
## Real fix
`create_manifest` must not stitch from mutable tags whose digests can be GC'd out from under it. Options:
- Tag per-arch images **immutably** (`:amd64-<commit-sha>`) and stitch `:latest` from those, so the referenced digests are never orphaned; or
- Push the arch manifests **by digest** and build the index in the same job that pushed them (buildx `--push` with a single multi-platform build does this atomically — one build, one index, no window); or
- At minimum, add a **post-push verification** step: after `create_manifest`, resolve every child of `:latest` and fail the job if any 404s. That turns this from a silent time-bomb into a red build.
The verification step is worth adding regardless — it is ~5 lines and it would have caught all 14 of these at the moment they broke, instead of months later via a stuck replica.
Related: closed **#548** (the mechanism), **#562** (root-caused by this — should be closed in favour of this ticket or linked), **#511**, **#552**, **#584**.
QA Team — still live, still causing #562. Re-verified 2026-07-14.
The dangling manifests have not self-healed. Spot-check from a swarm node:
docker manifest inspect git.spikersoft.com/spikerj/spikersoft-quiz-generation:latest -> OK (re-pushed by a recent build)
docker manifest inspect git.spikersoft.com/spikerj/spikersoft-coderunner:latest -> FAIL (still dangling)
docker manifest inspect git.spikersoft.com/spikerj/spikersoft-security-monitor:latest -> FAIL (still dangling)
Note the pattern: quiz-generation recovered only because it happened to get rebuilt and re-pushed. Nothing repaired it deliberately. Every image that has not been rebuilt since the corruption is still broken — which means this is silently waiting to bite any service that next needs a fresh pull.
#562 is not a separate incident — it is this ticket, happening.spikersoft-coderunner is stuck at 3/4 right now, in an active reject loop on dreamstream1:
$ docker service ps spikersoft-coderunner_spikersoft-coderunner
dreamstream1 Preparing 28 seconds ago
dreamstream1 Rejected 31 seconds ago "No such image: git.spikersoft…"
dreamstream1 Rejected about a minute ago "No such image: git.spikersoft…"
dreamstream1 Rejected 2 minutes ago "No such image: git.spikersoft…"
Swarm has been retrying that 4th replica continuously. It cannot ever succeed, because the manifest it is trying to pull 404s.
Fix options, in order of preference:
Re-run each affected service's CI workflow. Rebuilds and re-pushes a correct manifest index. Cleanest — no direct registry surgery, and it is provably what fixed quiz-generation. Downside: 14 workflow runs.
Re-stitch the index in place with docker buildx imagetools create -t <img>:latest <arch-digest> …. Fast, but writes directly to the production registry.
Either way, this needs doing deliberately — it is not going to fix itself, and the 3/4 reject loop is burning scheduler cycles on dreamstream1 continuously.
**QA Team — still live, still causing #562. Re-verified 2026-07-14.**
The dangling manifests have **not** self-healed. Spot-check from a swarm node:
```
docker manifest inspect git.spikersoft.com/spikerj/spikersoft-quiz-generation:latest -> OK (re-pushed by a recent build)
docker manifest inspect git.spikersoft.com/spikerj/spikersoft-coderunner:latest -> FAIL (still dangling)
docker manifest inspect git.spikersoft.com/spikerj/spikersoft-security-monitor:latest -> FAIL (still dangling)
```
Note the pattern: **quiz-generation recovered only because it happened to get rebuilt and re-pushed.** Nothing repaired it deliberately. Every image that has *not* been rebuilt since the corruption is still broken — which means this is silently waiting to bite any service that next needs a fresh pull.
**#562 is not a separate incident — it is this ticket, happening.** `spikersoft-coderunner` is stuck at **3/4** right now, in an active reject loop on dreamstream1:
```
$ docker service ps spikersoft-coderunner_spikersoft-coderunner
dreamstream1 Preparing 28 seconds ago
dreamstream1 Rejected 31 seconds ago "No such image: git.spikersoft…"
dreamstream1 Rejected about a minute ago "No such image: git.spikersoft…"
dreamstream1 Rejected 2 minutes ago "No such image: git.spikersoft…"
```
Swarm has been retrying that 4th replica continuously. It cannot ever succeed, because the manifest it is trying to pull 404s.
**Fix options, in order of preference:**
1. **Re-run each affected service's CI workflow.** Rebuilds and re-pushes a correct manifest index. Cleanest — no direct registry surgery, and it is provably what fixed quiz-generation. Downside: 14 workflow runs.
2. **Re-stitch the index in place** with `docker buildx imagetools create -t <img>:latest <arch-digest> …`. Fast, but writes directly to the production registry.
Either way, this needs doing deliberately — it is not going to fix itself, and the 3/4 reject loop is burning scheduler cycles on dreamstream1 continuously.
QA Team — RETRACTION. This ticket's claim that it is "the root cause of #562" is WRONG. I filed that; I'm correcting it.
#562 is a pull-path problem, not a manifest problem. git.spikersoft.com resolves to a public IP (204.197.150.99) from inside the cluster, so every node pull hairpins out to the WAN and back. That — not manifest corruption — is what makes fresh pulls fail on the Jetsons. Full evidence and the split-horizon DNS fix are now on #562. The reporter's original hairpin theory was correct.
Specifically, spikersoft-coderunner (the service I cited as proof) is not dangling at all:
its :latest index resolves, and both amd64 and arm64 children resolve;
the registry serves it anonymously, HTTP 200;
three aarch64 Jetsons are running that exact image right now.
Docker surfaces a failed pull as No such image, which is what led me to conflate the two. My mistake.
I've retitled this ticket accordingly. What remains below is still real and still worth fixing.
What IS real: 14 images have no :latest manifest
Deterministic — I probed each 5×, and every result was stable. The failure is a clean manifest unknown (a true 404), not a flake and not an auth error (19 other images resolve fine over the same anonymous path):
These are currently harmless only because the running tasks were pulled long ago. Any of these services that gets rescheduled, scaled, or drained onto a new node will fail to start, and there is no fallback. It is a landmine, not an outage — but it is 14 landmines.
Note spikersoft-quiz-generation was on this list earlier today and is now OK — not because anything repaired it, but because it happened to get rebuilt and re-pushed by infra #81's deploy. That is the fix, and it confirms the remedy below works.
Fix
Re-run each affected service's CI workflow to rebuild and re-push a correct manifest index. No registry surgery required.
⚠️ Sequencing: the #562 DNS fix should land first. Several of these are arm64 builds, and the ARM runner on dreamstream6 is still crash-looping (#579), so rebuilds may be slow or need retries until both of those are resolved.
**QA Team — RETRACTION. This ticket's claim that it is "the root cause of #562" is WRONG. I filed that; I'm correcting it.**
#562 is a **pull-path** problem, not a manifest problem. `git.spikersoft.com` resolves to a **public IP (204.197.150.99) from inside the cluster**, so every node pull hairpins out to the WAN and back. That — not manifest corruption — is what makes fresh pulls fail on the Jetsons. Full evidence and the split-horizon DNS fix are now on **#562**. The reporter's original hairpin theory was correct.
Specifically, `spikersoft-coderunner` (the service I cited as proof) is **not** dangling at all:
- its `:latest` index resolves, and **both** amd64 and arm64 children resolve;
- the registry serves it **anonymously**, HTTP 200;
- **three aarch64 Jetsons are running that exact image right now**.
Docker surfaces a failed *pull* as `No such image`, which is what led me to conflate the two. My mistake.
**I've retitled this ticket accordingly.** What remains below is still real and still worth fixing.
---
## What IS real: 14 images have no `:latest` manifest
Deterministic — I probed each 5×, and every result was stable. The failure is a clean `manifest unknown` (a true 404), not a flake and not an auth error (19 other images resolve fine over the same anonymous path):
```
jetson-tx2-influxdb-grafana spikersoft-metadata-extractor
sonar-scanner-datacenter spikersoft-node-agent
spikersoft-backend spikersoft-notifications
spikersoft-docker-monitor spikersoft-security-monitor
spikersoft-gameserver spikersoft-security-scanner
spikersoft-influx-dashboard spikersoft-system-remediation
spikersoft-keycloak-events spikersoft-upload-coordinator
```
These are currently harmless **only** because the running tasks were pulled long ago. Any of these services that gets rescheduled, scaled, or drained onto a new node will fail to start, and there is no fallback. It is a landmine, not an outage — but it is 14 landmines.
Note `spikersoft-quiz-generation` was on this list earlier today and is now OK — **not** because anything repaired it, but because it happened to get rebuilt and re-pushed by infra #81's deploy. That is the fix, and it confirms the remedy below works.
## Fix
Re-run each affected service's CI workflow to rebuild and re-push a correct manifest index. No registry surgery required.
⚠️ Sequencing: the **#562 DNS fix should land first**. Several of these are arm64 builds, and the ARM runner on dreamstream6 is still crash-looping (**#579**), so rebuilds may be slow or need retries until both of those are resolved.
spikerj
changed title from [Bug][Prod][Registry] 14 services have DANGLING :latest manifests — the index points at child digests that 404. Any fresh pull fails. This is the root cause of #562 to [Bug][Prod][Registry] 14 images have no :latest manifest (manifest unknown / 404) — any reschedule onto a fresh node will fail to start2026-07-14 21:07:07 +00:00
QA Team — ROOT CAUSE FOUND, and it is NOT registry corruption. The images are all fine.
I probed each dangling image for its architecture tags as well as :latest. The result is decisive:
:latest :amd64 :arm64v8
spikersoft-backend MISSING ok ok
spikersoft-docker-monitor MISSING ok ok
spikersoft-influx-dashboard MISSING ok ok
spikersoft-metadata-extractor MISSING ok ok
spikersoft-node-agent MISSING ok ok
spikersoft-notifications MISSING ok ok
spikersoft-security-monitor MISSING ok ok
spikersoft-security-scanner MISSING ok ok
spikersoft-system-remediation MISSING ok ok
spikersoft-upload-coordinator MISSING ok ok
Both architecture builds succeeded and pushed. Only the multi-arch index (:latest) was never created. Nothing is corrupt, nothing was garbage-collected, and no registry surgery is needed. The layers are sitting in the registry, healthy and pullable by digest.
The mechanism — this is #590, and it does more damage than that ticket says
Every affected workflow has:
concurrency:group:${{ github.workflow }}-${{ github.ref }}cancel-in-progress:true...create_manifest:needs:[build_and_publish] # <-- the job that stitches :latest
build_and_publish pushes :amd64 and :arm64v8. create_manifest then runs docker manifest create ... :latest from those tags. Because cancel-in-progress: true is set, a back-to-back push to master cancels the in-flight run. If the cancellation lands after the arch builds have pushed but beforecreate_manifest executes, you get exactly the state above: healthy arch tags, no :latest.
#590 describes this concurrency setting killing deploy jobs. It also silently kills manifest stitching — which is a distinct and arguably worse failure, because a skipped deploy is a no-op, whereas a skipped create_manifest leaves :latest pointing at nothing and breaks every future pull on every node. #590 should be widened to cover this.
Corroboration
Two images on the original list have since recovered on their own — spikersoft-gameserver and spikersoft-keycloak-events now resolve :latest fine. Neither was repaired by hand. They simply got rebuilt by a workflow run that was not cancelled, and create_manifest completed. Same as spikersoft-quiz-generation earlier today. That is the mechanism confirming itself.
Two entries are not part of this at all — jetson-tx2-influxdb-grafana and sonar-scanner-datacenter have no tags whatsoever (:latest, :amd64, :arm64v8 all missing). They are third-party/externally-sourced images that were never pushed to this registry. They should be dropped from this ticket's scope.
Fix
Cheap and immediate: re-run each affected workflow. create_manifest will stitch :latest from the arch tags that are already in the registry — no rebuild of the layers is strictly required, so these runs should be fast.
Durable — fix the cause, or this recurs indefinitely: address #590. Either drop cancel-in-progress: true on master, or (better) exclude the publish/manifest/deploy jobs from cancellation so that a superseded run still finishes stitching and shipping what it already built. Cancelling a build is fine; cancelling after a partial push is what corrupts the tag state.
Related: #548 (closed) — the --amend against mutable :amd64/:arm64v8 tags. That fix never actually landed; all 23 of 23 multi-arch workflows still use the mutable pattern. Filed separately. It compounds this: because the arch tags are mutable, a cancelled run can leave :amd64 and :arm64v8 pointing at different commits, so a later create_manifest can stitch a mixed-commit :latest that looks healthy but is not.
**QA Team — ROOT CAUSE FOUND, and it is NOT registry corruption. The images are all fine.**
I probed each dangling image for its **architecture** tags as well as `:latest`. The result is decisive:
```
:latest :amd64 :arm64v8
spikersoft-backend MISSING ok ok
spikersoft-docker-monitor MISSING ok ok
spikersoft-influx-dashboard MISSING ok ok
spikersoft-metadata-extractor MISSING ok ok
spikersoft-node-agent MISSING ok ok
spikersoft-notifications MISSING ok ok
spikersoft-security-monitor MISSING ok ok
spikersoft-security-scanner MISSING ok ok
spikersoft-system-remediation MISSING ok ok
spikersoft-upload-coordinator MISSING ok ok
```
**Both architecture builds succeeded and pushed. Only the multi-arch index (`:latest`) was never created.** Nothing is corrupt, nothing was garbage-collected, and no registry surgery is needed. The layers are sitting in the registry, healthy and pullable by digest.
## The mechanism — this is #590, and it does more damage than that ticket says
Every affected workflow has:
```yaml
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
...
create_manifest:
needs: [build_and_publish] # <-- the job that stitches :latest
```
`build_and_publish` pushes `:amd64` and `:arm64v8`. `create_manifest` then runs `docker manifest create ... :latest` from those tags. Because `cancel-in-progress: true` is set, a back-to-back push to master **cancels the in-flight run**. If the cancellation lands *after* the arch builds have pushed but *before* `create_manifest` executes, you get exactly the state above: **healthy arch tags, no `:latest`.**
**#590** describes this concurrency setting killing *deploy* jobs. It also silently kills **manifest stitching** — which is a distinct and arguably worse failure, because a skipped deploy is a no-op, whereas a skipped `create_manifest` leaves `:latest` pointing at nothing and **breaks every future pull on every node**. #590 should be widened to cover this.
## Corroboration
Two images on the original list have since **recovered on their own** — `spikersoft-gameserver` and `spikersoft-keycloak-events` now resolve `:latest` fine. Neither was repaired by hand. They simply got rebuilt by a workflow run that was *not* cancelled, and `create_manifest` completed. Same as `spikersoft-quiz-generation` earlier today. That is the mechanism confirming itself.
Two entries are **not** part of this at all — `jetson-tx2-influxdb-grafana` and `sonar-scanner-datacenter` have **no tags whatsoever** (`:latest`, `:amd64`, `:arm64v8` all missing). They are third-party/externally-sourced images that were never pushed to this registry. They should be dropped from this ticket's scope.
## Fix
**Cheap and immediate:** re-run each affected workflow. `create_manifest` will stitch `:latest` from the arch tags that are *already in the registry* — no rebuild of the layers is strictly required, so these runs should be fast.
**Durable — fix the cause, or this recurs indefinitely:** address **#590**. Either drop `cancel-in-progress: true` on `master`, or (better) exclude the publish/manifest/deploy jobs from cancellation so that a superseded run still finishes stitching and shipping what it already built. Cancelling a *build* is fine; cancelling *after* a partial push is what corrupts the tag state.
Related: **#548** (closed) — the `--amend` against mutable `:amd64`/`:arm64v8` tags. That fix never actually landed; **all 23 of 23 multi-arch workflows still use the mutable pattern**. Filed separately. It compounds this: because the arch tags are mutable, a cancelled run can leave `:amd64` and `:arm64v8` pointing at *different commits*, so a later `create_manifest` can stitch a mixed-commit `:latest` that looks healthy but is not.
QA Team — live swarm evidence, and this is materially worse than the ticket title suggests.
We swept every service on the swarm. The :latest tag problem is actively causing outages right now, not just a latent reschedule risk.
Confirmed from a swarm manager, with a control
ref
result
spikersoft-coderunner:amd64
valid manifest returned
spikersoft-coderunner:latest
manifest unknown
spikersoft-coderunner:arm64
manifest unknown
spikersoft-node-agent:latest
manifest unknown
spikersoft-backend:latest
manifest unknown
:amd64 resolves from the same client with the same credentials, so this is a genuine tag-specific 404 — not an auth artifact. CI appears to be publishing only :amd64: no :latest, and no arm64 image at all, while 7 of 10 nodes are aarch64.
It is already breaking things
spikersoft-coderunner is 3/4. Replica .1 has been bouncing between dreamstream1/4/6 for 15+ hours, never starting: Rejected: "No such image: git.spikersoft.com/spikerj/spikersoft-coderunner:latest". RestartPolicy: any, so it never gives up. (We tested and discarded an arch-mismatch theory — all 7 dreamstreams are aarch64 and coderunner runs fine on ds2/3/5.)
spikersoft-node-agent reports a green 7/7 but has ZERO coverage on dreamstream1, dreamstream4 and dreamstream6 — all Rejected: "No such image: ...node-agent:latest" (18h / 11h / 14h). Global-mode services only count nodes with placed tasks, so docker service ls shows 7/7 while 30% of the fleet is unmonitored and the dashboard is green. Filing separately.
The scale of the exposure
24 of 32 spikersoft-* services are deployed with a bare :latest tag and no digest pin. They are running only because each node still holds a stale locally-cached :latest from when the tag existed (~3 days ago). Nodes without that cache — ds1/4/6 — reject instantly.
Any node reboot, docker image prune, or reschedule onto a cold node turns into an unrecoverable outage. This is a loaded gun pointed at most of the platform, and the trigger is routine maintenance.
Corroborating signal from Jaeger
traefik error spans in the last hour include registry manifest 404s for spikersoft-artpipe-processor, spikersoft-coderunner/manifests/arm64, and spikersoft-backend itself (trace ids 249b8c91f1e4ed6cddde19c8521f274b, 28cbe31162c6e2b3358f94b5081e0221).
This is also the most likely mechanism behind the recurring "merged but never deployed" pattern (#553, #582, and #588 — which we just re-opened as #602 after confirming its fix never reached a running container). If :latest cannot be pulled, a merged fix simply never becomes a running process.
Suggested priority
P1. We would treat this as the highest-severity open item on the board: it is both an active partial outage (coderunner, node-agent) and the root cause of fixes silently not shipping.
**QA Team** — live swarm evidence, and this is materially worse than the ticket title suggests.
We swept every service on the swarm. The `:latest` tag problem is **actively causing outages right now**, not just a latent reschedule risk.
## Confirmed from a swarm manager, with a control
| ref | result |
|---|---|
| `spikersoft-coderunner:amd64` | **valid manifest returned** |
| `spikersoft-coderunner:latest` | `manifest unknown` |
| `spikersoft-coderunner:arm64` | `manifest unknown` |
| `spikersoft-node-agent:latest` | `manifest unknown` |
| `spikersoft-backend:latest` | `manifest unknown` |
`:amd64` resolves from the same client with the same credentials, so this is a genuine tag-specific 404 — **not** an auth artifact. CI appears to be publishing only `:amd64`: no `:latest`, and **no arm64 image at all**, while **7 of 10 nodes are aarch64**.
## It is already breaking things
- **`spikersoft-coderunner` is 3/4.** Replica `.1` has been bouncing between dreamstream1/4/6 for **15+ hours**, never starting: `Rejected: "No such image: git.spikersoft.com/spikerj/spikersoft-coderunner:latest"`. `RestartPolicy: any`, so it never gives up. (We tested and *discarded* an arch-mismatch theory — all 7 dreamstreams are aarch64 and coderunner runs fine on ds2/3/5.)
- **`spikersoft-node-agent` reports a green `7/7` but has ZERO coverage on dreamstream1, dreamstream4 and dreamstream6** — all `Rejected: "No such image: ...node-agent:latest"` (18h / 11h / 14h). Global-mode services only count nodes with placed tasks, so `docker service ls` shows `7/7` while **30% of the fleet is unmonitored and the dashboard is green.** Filing separately.
## The scale of the exposure
**24 of 32 `spikersoft-*` services are deployed with a bare `:latest` tag and no digest pin.** They are running *only* because each node still holds a stale locally-cached `:latest` from when the tag existed (~3 days ago). Nodes without that cache — ds1/4/6 — reject instantly.
Any node reboot, `docker image prune`, or reschedule onto a cold node turns into an unrecoverable outage. This is a loaded gun pointed at most of the platform, and the trigger is routine maintenance.
## Corroborating signal from Jaeger
traefik error spans in the last hour include registry manifest 404s for `spikersoft-artpipe-processor`, `spikersoft-coderunner/manifests/arm64`, and **`spikersoft-backend`** itself (trace ids `249b8c91f1e4ed6cddde19c8521f274b`, `28cbe31162c6e2b3358f94b5081e0221`).
This is also the most likely **mechanism** behind the recurring "merged but never deployed" pattern (#553, #582, and #588 — which we just re-opened as #602 after confirming its fix never reached a running container). If `:latest` cannot be pulled, a merged fix simply never becomes a running process.
## Suggested priority
**P1.** We would treat this as the highest-severity open item on the board: it is both an active partial outage (coderunner, node-agent) and the root cause of fixes silently not shipping.
Related: #596 (mutable arch tags / `--amend`), #548, #590 (cancelled deploy jobs), #602.
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.
QA Team — sweep 2026-07-14 ~21:15Z. Filed against closed #548 (Multi-arch create_manifest amends MUTABLE :amd64/:arm64v8 tags). #548 named the mechanism. This is the damage it left behind, and it is still live.
The bug
14 deployed services have a
:latestmanifest list whose child manifests do not exist in the registry. Pulling any of them fails. They keep running only because the image is cached on the node they happen to be sitting on.Worked example —
spikersoft-coderunner:latest:Both children 404. Meanwhile the standalone per-arch tag is healthy and points somewhere else entirely:
Not a media-type artifact: the 404 reproduces with
Accept: */*, and the:arm64v8control returns 200 with identical headers.Mechanism (this is #548, playing out)
create_manifeststitches:latestfrom the mutable:amd64/:arm64v8tags, capturing their digests at that moment.:amd64/:arm64v8with new digests.:lateststill references the deleted digests → dangling index.#548 identified the mutable-tag race. What nobody wrote down is the consequence: when the per-arch tag moves, the old digest is GC'd and
:latestrots. Closing #548 did not repair the already-rotted indexes.This is the root cause of #562
#562 ("dreamstream nodes reject fresh tasks with
No such image") is not a UDM hairpin, not a slow pull, not registry auth. Verified:--with-registry-auth✅jetsonsbucket) — not a disk problem ✅The asymmetry is entirely explained: ds5 has the image cached (its task has been Running 6 h). ds1 and ds4 must actually pull
:latest, they follow the index to a 404, and Docker reports the only thing it can —No such image. They will reject forever; there is nothing to converge to. That is why coderunner has sat at 3/4 all day rather than self-healing in "1-3 minutes" as #562 assumed.Blast radius — this is a latent cluster-wide outage
Every one of these is unpullable right now. They survive only on cached images:
Any event that forces a fresh pull — a node reboot, a failover, a rescheduling, a scale-out,
docker system pruneon a node — takes that service to zero and it cannot come back. The API is on this list.This also retro-explains the "No such image" reject-loops in #511 and the ones seen during the ds4/ds6 outages (#552): those nodes were being asked to pull images whose
:latestwas already dangling.Immediate remediation
Re-stitch
:latestfor all 14 from their current, healthy per-arch tags — i.e. re-runcreate_manifest(ordocker buildx imagetools create -t <img>:latest <img>:amd64 <img>:arm64v8). That is a registry-side fix and needs no code change. Do coderunner first — it is the one already degraded.Real fix
create_manifestmust not stitch from mutable tags whose digests can be GC'd out from under it. Options::amd64-<commit-sha>) and stitch:latestfrom those, so the referenced digests are never orphaned; or--pushwith a single multi-platform build does this atomically — one build, one index, no window); orcreate_manifest, resolve every child of:latestand fail the job if any 404s. That turns this from a silent time-bomb into a red build.The verification step is worth adding regardless — it is ~5 lines and it would have caught all 14 of these at the moment they broke, instead of months later via a stuck replica.
Related: closed #548 (the mechanism), #562 (root-caused by this — should be closed in favour of this ticket or linked), #511, #552, #584.
QA Team — still live, still causing #562. Re-verified 2026-07-14.
The dangling manifests have not self-healed. Spot-check from a swarm node:
Note the pattern: quiz-generation recovered only because it happened to get rebuilt and re-pushed. Nothing repaired it deliberately. Every image that has not been rebuilt since the corruption is still broken — which means this is silently waiting to bite any service that next needs a fresh pull.
#562 is not a separate incident — it is this ticket, happening.
spikersoft-coderunneris stuck at 3/4 right now, in an active reject loop on dreamstream1:Swarm has been retrying that 4th replica continuously. It cannot ever succeed, because the manifest it is trying to pull 404s.
Fix options, in order of preference:
docker buildx imagetools create -t <img>:latest <arch-digest> …. Fast, but writes directly to the production registry.Either way, this needs doing deliberately — it is not going to fix itself, and the 3/4 reject loop is burning scheduler cycles on dreamstream1 continuously.
QA Team — RETRACTION. This ticket's claim that it is "the root cause of #562" is WRONG. I filed that; I'm correcting it.
#562 is a pull-path problem, not a manifest problem.
git.spikersoft.comresolves to a public IP (204.197.150.99) from inside the cluster, so every node pull hairpins out to the WAN and back. That — not manifest corruption — is what makes fresh pulls fail on the Jetsons. Full evidence and the split-horizon DNS fix are now on #562. The reporter's original hairpin theory was correct.Specifically,
spikersoft-coderunner(the service I cited as proof) is not dangling at all::latestindex resolves, and both amd64 and arm64 children resolve;Docker surfaces a failed pull as
No such image, which is what led me to conflate the two. My mistake.I've retitled this ticket accordingly. What remains below is still real and still worth fixing.
What IS real: 14 images have no
:latestmanifestDeterministic — I probed each 5×, and every result was stable. The failure is a clean
manifest unknown(a true 404), not a flake and not an auth error (19 other images resolve fine over the same anonymous path):These are currently harmless only because the running tasks were pulled long ago. Any of these services that gets rescheduled, scaled, or drained onto a new node will fail to start, and there is no fallback. It is a landmine, not an outage — but it is 14 landmines.
Note
spikersoft-quiz-generationwas on this list earlier today and is now OK — not because anything repaired it, but because it happened to get rebuilt and re-pushed by infra #81's deploy. That is the fix, and it confirms the remedy below works.Fix
Re-run each affected service's CI workflow to rebuild and re-push a correct manifest index. No registry surgery required.
⚠️ Sequencing: the #562 DNS fix should land first. Several of these are arm64 builds, and the ARM runner on dreamstream6 is still crash-looping (#579), so rebuilds may be slow or need retries until both of those are resolved.
[Bug][Prod][Registry] 14 services have DANGLING :latest manifests — the index points at child digests that 404. Any fresh pull fails. This is the root cause of #562to [Bug][Prod][Registry] 14 images have no :latest manifest (manifest unknown / 404) — any reschedule onto a fresh node will fail to startQA Team — ROOT CAUSE FOUND, and it is NOT registry corruption. The images are all fine.
I probed each dangling image for its architecture tags as well as
:latest. The result is decisive:Both architecture builds succeeded and pushed. Only the multi-arch index (
:latest) was never created. Nothing is corrupt, nothing was garbage-collected, and no registry surgery is needed. The layers are sitting in the registry, healthy and pullable by digest.The mechanism — this is #590, and it does more damage than that ticket says
Every affected workflow has:
build_and_publishpushes:amd64and:arm64v8.create_manifestthen runsdocker manifest create ... :latestfrom those tags. Becausecancel-in-progress: trueis set, a back-to-back push to master cancels the in-flight run. If the cancellation lands after the arch builds have pushed but beforecreate_manifestexecutes, you get exactly the state above: healthy arch tags, no:latest.#590 describes this concurrency setting killing deploy jobs. It also silently kills manifest stitching — which is a distinct and arguably worse failure, because a skipped deploy is a no-op, whereas a skipped
create_manifestleaves:latestpointing at nothing and breaks every future pull on every node. #590 should be widened to cover this.Corroboration
Two images on the original list have since recovered on their own —
spikersoft-gameserverandspikersoft-keycloak-eventsnow resolve:latestfine. Neither was repaired by hand. They simply got rebuilt by a workflow run that was not cancelled, andcreate_manifestcompleted. Same asspikersoft-quiz-generationearlier today. That is the mechanism confirming itself.Two entries are not part of this at all —
jetson-tx2-influxdb-grafanaandsonar-scanner-datacenterhave no tags whatsoever (:latest,:amd64,:arm64v8all missing). They are third-party/externally-sourced images that were never pushed to this registry. They should be dropped from this ticket's scope.Fix
Cheap and immediate: re-run each affected workflow.
create_manifestwill stitch:latestfrom the arch tags that are already in the registry — no rebuild of the layers is strictly required, so these runs should be fast.Durable — fix the cause, or this recurs indefinitely: address #590. Either drop
cancel-in-progress: trueonmaster, or (better) exclude the publish/manifest/deploy jobs from cancellation so that a superseded run still finishes stitching and shipping what it already built. Cancelling a build is fine; cancelling after a partial push is what corrupts the tag state.Related: #548 (closed) — the
--amendagainst mutable:amd64/:arm64v8tags. That fix never actually landed; all 23 of 23 multi-arch workflows still use the mutable pattern. Filed separately. It compounds this: because the arch tags are mutable, a cancelled run can leave:amd64and:arm64v8pointing at different commits, so a latercreate_manifestcan stitch a mixed-commit:latestthat looks healthy but is not.QA Team — live swarm evidence, and this is materially worse than the ticket title suggests.
We swept every service on the swarm. The
:latesttag problem is actively causing outages right now, not just a latent reschedule risk.Confirmed from a swarm manager, with a control
spikersoft-coderunner:amd64spikersoft-coderunner:latestmanifest unknownspikersoft-coderunner:arm64manifest unknownspikersoft-node-agent:latestmanifest unknownspikersoft-backend:latestmanifest unknown:amd64resolves from the same client with the same credentials, so this is a genuine tag-specific 404 — not an auth artifact. CI appears to be publishing only:amd64: no:latest, and no arm64 image at all, while 7 of 10 nodes are aarch64.It is already breaking things
spikersoft-coderunneris 3/4. Replica.1has been bouncing between dreamstream1/4/6 for 15+ hours, never starting:Rejected: "No such image: git.spikersoft.com/spikerj/spikersoft-coderunner:latest".RestartPolicy: any, so it never gives up. (We tested and discarded an arch-mismatch theory — all 7 dreamstreams are aarch64 and coderunner runs fine on ds2/3/5.)spikersoft-node-agentreports a green7/7but has ZERO coverage on dreamstream1, dreamstream4 and dreamstream6 — allRejected: "No such image: ...node-agent:latest"(18h / 11h / 14h). Global-mode services only count nodes with placed tasks, sodocker service lsshows7/7while 30% of the fleet is unmonitored and the dashboard is green. Filing separately.The scale of the exposure
24 of 32
spikersoft-*services are deployed with a bare:latesttag and no digest pin. They are running only because each node still holds a stale locally-cached:latestfrom when the tag existed (~3 days ago). Nodes without that cache — ds1/4/6 — reject instantly.Any node reboot,
docker image prune, or reschedule onto a cold node turns into an unrecoverable outage. This is a loaded gun pointed at most of the platform, and the trigger is routine maintenance.Corroborating signal from Jaeger
traefik error spans in the last hour include registry manifest 404s for
spikersoft-artpipe-processor,spikersoft-coderunner/manifests/arm64, andspikersoft-backenditself (trace ids249b8c91f1e4ed6cddde19c8521f274b,28cbe31162c6e2b3358f94b5081e0221).This is also the most likely mechanism behind the recurring "merged but never deployed" pattern (#553, #582, and #588 — which we just re-opened as #602 after confirming its fix never reached a running container). If
:latestcannot be pulled, a merged fix simply never becomes a running process.Suggested priority
P1. We would treat this as the highest-severity open item on the board: it is both an active partial outage (coderunner, node-agent) and the root cause of fixes silently not shipping.
Related: #596 (mutable arch tags /
--amend), #548, #590 (cancelled deploy jobs), #602.