Skip to content

Compose deploy serves stale content from a shared component despite fresh clone + fresh build + Next.js force-dynamic #5020

Description

@YounessFTN

Setup: Self-hosted Dokploy (recently upgraded to v0.29.14, around 2026-08-08), single Hetzner/Ubuntu VPS, deploying a Next.js 16 (Turbopack) app via a Compose service with a custom multi-stage Dockerfile. Auto-deploy on push to main via GitHub webhook.

Symptom: After pushing commits that changed a React component shared across ~23 routes, those specific routes kept serving the pre-change HTML, while other files changed in the exact same commit updated correctly and immediately.

What we ruled out, with evidence, in order:

  1. Multiple frontend replicas — docker ps (host-wide) confirms a single frontend container
  2. Redis-backed Next.js cache handler — none configured in the app
  3. Persistent Docker volume for .next — none declared in the compose file
  4. Docker build cache mount (--mount=type=cache) — none in the Dockerfile
  5. Stale/zombie Node process — ps aux inside the container shows a single next-server; kill -9 1 (SIGKILL on PID 1) was confirmed to actually recreate the process (CPU time reset to near-zero)
  6. Stale build artifact — the exact compiled SSR chunk on disk, and the route's page.js, were grepped directly and do contain the new code; file mtimes match the deployment timestamp to the second
  7. HTTP/CDN caching — added export const dynamic = "force-dynamic" to the affected routes to force per-request rendering; confirmed active via response header Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate; Cloudflare confirms cf-cache-status: DYNAMIC (not caching); curling the container directly on the host (127.0.0.1:<port>, bypassing Traefik and Cloudflare entirely) still returns the stale content

So: correct code on main → correct fresh git clone → correct fresh compiled artifact on disk (verified via direct file inspection inside the container) → correct route manifest → force-dynamic confirmed active at the HTTP layer → still serves the old content, with zero errors in docker logs.

Two things found while reading the relevant source, reported in case they're useful (though we couldn't fully connect either to this exact bug):

  • The Compose "Rebuild" action (rebuildCompose in packages/server/src/services/compose.ts) does not re-clone the repository — confirmed by PR fix: rename compose "Reload" action to "Rebuild"  #4847, which correctly renamed "Reload"→"Rebuild" but didn't change the underlying behavior. This explains some of our earlier confusion during troubleshooting, but our final, decisive deploy went through the webhook-triggered deployCompose path, which does re-clone (rm -rf + git clone --depth 1) before building — so this doesn't appear to be the root cause of the final failure.
  • getBuildComposeCommand runs docker compose up -d --build --remove-orphans with no --no-cache flag, relying entirely on Docker's default layer-cache invalidation. Given the source directory is always freshly rm -rf'd and re-cloned beforehand, this should be safe — but it's the one remaining layer between "confirmed fresh source on disk" and "confirmed stale served content" that we couldn't inspect directly (no access to the actual docker compose build logs for this specific deployment).

Question: is there a known issue in the v0.29.13/14 range (or the Compose build/deploy pipeline generally) that could cause the build step to silently reuse a stale layer despite fresh source, or cause a running container to serve content from a different image than the one just built? Access to the Docker build logs for a specific past deployment (if not already surfaced in the UI) would help a lot in narrowing this down.

Happy to provide more detail, container names, or run additional diagnostics if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions