Skip to content

Deployment verification

How to prove a push actually reached Production — not "the pipeline went green" but "the bytes users load are the ones you meant". Written from the procedure used on every deploy in September 2026; the traps listed each hid a real failure once.

The documentation site (docs service, doc.addmedad.top) deploys with the same push, once the domain exists in Dokploy (compose app → Domains: host doc.addmedad.top, service docs, port 80, HTTPS Let's Encrypt) and in DNS (an A record to the production host): curl -sI https://doc.addmedad.top/ must answer 302 to /BIMZONE-USER-GUIDE/, and a page you changed must show its new text.

Topology in one paragraph

Production is a Dokploy Compose service whose compose path is docker-compose.dokploy.yml; that file includes the base docker-compose.yml and does exactly one thing more — attaches web to dokploy-network so Dokploy's Traefik can reach it. Dokploy autodeploys on every push to BIMzone_Master_push. The services are postgres, minio, redis, migrate (runs once, must complete before api starts), api, worker (+ /metrics on 9091), web (nginx: serves the SPA and proxies /api/, /storage/, /socket.io/, /bcf/, /scim/ to api:4000), and the dr profile's postgres-replica + backup.

The five checks, in order

Each check answers a different question; passing one says nothing about the next.

1. Did the web bundle change?

curl -s https://bim.addmedad.top/ | grep -oE "assets/index-[A-Za-z0-9_-]+\.js"

The hash is content-addressed. If it equals the previous deploy's hash, nginx is still serving the old image — the build failed or Dokploy has not finished. Record the new hash (e.g. index-C1kJfkSc.js on 2026-09-17); the browser-side fix you are verifying is only live once this changes. Then hard-refresh the app once: the SPA is versioned per deploy and a stale tab keeps the old bundle.

2. Is the API up, and is it the NEW api container?

curl -s https://bim.addmedad.top/api/health          # → {"ok":true,"ts":…}

ok:true proves an api answers, not which one. web proxies to api:4000 by name and nginx caches the resolved IP: after a redeploy it can keep talking to the old container and 502 — or worse, keep talking to an old container that still answers. Prove the new code with something only the new build says. Two ways:

  • call a route the new commit added or changed and read its own error text, e.g. POST /api/projects/<id>/integration-connections/<id>/conflicts/resolve with a bogus body returns the new route's validation message rather than a 404;
  • or, for header/CSP changes: curl -sI https://bim.addmedad.top/ | grep -i content-security-policy and compare to the conf you shipped.

If /api/health 502s for more than a minute after the deploy finished, restart web (nginx) from Dokploy: it re-resolves api.

3. Did the migration run?

The migrate service must exit 0 before api starts (depends_on: service_completed_successfully). In Dokploy, open the deployment log and look for Migrations applied.; a failure prints Migration failed: and the api never starts. See Migrations for what to do then.

4. Is the worker alive?

The worker is a separate container; the api can be perfectly healthy while nothing converts. Open any project → upload a small IFC (or press Reconvert on a model) and watch Cloud activity in the top bar: queued → converting → ready within a minute proves the worker, Redis and storage. If it sits at queued, the worker is down or cannot reach Redis — check its container log for [worker] startup lines and the Redis connection.

5. Does the UI actually render what changed?

Open the page you changed in a real browser session, with the console open. A React error boundary ("Something went wrong") or a Minified React error #NNN in the console is a shipped defect even when every curl above passed — the Integrations tab crash of 2026-09-16 passed every server-side check. For anything visual (viewer, map, PDFs) look at it; do not infer from a 200.

Traps this procedure exists for

Trap What it looked like Where it is written up
Traefik cannot reach web TLS fine, every path returns Traefik's plain 404 page not found header of docker-compose.dokploy.yml
nginx cached the old api IP 502 on /api/* after every deploy commit d72055b
.mjs served as octet-stream viewer blank, tree populated, .frag 200 apps/web/tests/structural/asset-mime.test.ts
/modeltiles, /pointclouds reached no nginx/Vite config tiles inert everywhere commit ec74357
env var not in the compose allowlist feature silently off in the container while set in Dokploy apps/server/tests/structural/compose-env-allowlist.test.ts
CSP connect-src missing the basemap hosts Map tab black, "Failed to fetch" in the console, nothing in server logs apps/web/tests/structural/csp-tile-hosts.test.ts
stale SPA tab old bundle keeps running after a deploy hard-refresh once

After verifying

Write the bundle hash, the commit, and what you looked at into docs/PHASE-EXECUTION-LOG-2026-09.md (the running log); the entry is the evidence that Production == HEAD.