ByteBite Docs
Infrastructure

Troubleshooting

Symptom-oriented infrastructure troubleshooting guide.

Troubleshooting

Symptom → likely cause → diagnosis → resolution. For recovery procedures, see Disaster recovery.

DNS resolves, TLS fails

Symptomdig returns correct IP; browser/curl shows TLS error
Likely causeACME failure, expired cert, or wrong resolver for host type
Diagnosisopenssl s_client -connect HOST:443 -servername HOST; check docker logs dokploy-traefik for ACME errors
ResolutionExplicit hosts: check HTTP-01 (acme.json). Wildcard: check DNS-01 token and acme-cloudflare.json. See TLS, Disaster recovery

Wildcard TLS works, request returns Traefik 404

SymptomValid TLS on *.staging.getbytebite.co but Traefik 404 page
Likely causeContainer not on dokploy-network or Traefik labels missing
Diagnosisdocker inspect <storefront-container> --format '{{json .NetworkSettings.Networks}}' — check for dokploy-network
ResolutionRe-attach dokploy-network via Dokploy domain config or manual network connect. Verify labels in running container match compose.deploy.yml

Wildcard request reaches wrong service

SymptomWildcard host serves dashboard or docs instead of storefront
Likely causeRouter priority conflict or missing wildcard labels
DiagnosisCheck Traefik router priorities; explicit Dokploy routers should have higher priority than wildcard (10/20)
ResolutionVerify Dokploy explicit domains still configured. Redeploy compose.deploy.yml to restore wildcard labels

/api reaches storefront instead of API

Symptomcurl https://HOST/api/health returns HTML or storefront response
Likely causeMissing API wildcard router (priority 20) or StripPrefix middleware
DiagnosisCheck api container labels for bytebite-staging-wildcard-api router
ResolutionRedeploy from compose.deploy.yml. For explicit hosts, verify Dokploy /api path rule with StripPrefix

API receives /api prefix unexpectedly

SymptomAPI logs show requests to /api/health instead of /health
Likely causeStripPrefix middleware not applied
DiagnosisCheck Traefik middleware bytebite-staging-wildcard-strip-api on API router
ResolutionVerify compose.deploy.yml labels; redeploy

Tenant displays wrong restaurant

SymptomStorefront shows wrong branding for hostname
Likely causeDatabase restaurant_domains mismatch or stale SSR cache
Diagnosiscurl -H "Host: EXPECTED_HOST" -u "${STAGING_BASIC_AUTH_USER}:${STAGING_BASIC_AUTH_PASSWORD}" https://api.staging.getbytebite.co/storefront/restaurant and the same Host override against /storefront/menu
ResolutionCheck restaurant_domains table. Re-run seed-staging.js if needed. This is an application/data issue, not routing

Unknown tenant behavior

SymptomRandom *.staging.getbytebite.co shows "Restaurant not found"
ExpectedStorefront: HTTP 200 with not-found page. API tenant routes: HTTP 404. /api/health: HTTP 200
NoteStorefront HTTP 200 for unknown tenants is a known application gap — not an infrastructure failure

DB health fails

Symptom/health/db returns degraded
Likely causePostgreSQL container down, wrong DATABASE_URL, network issue
DiagnosisCheck Dokploy PostgreSQL service status; verify DATABASE_URL in api container env (name only, not value)
ResolutionRestart PostgreSQL via Dokploy. See Disaster recovery

Staging API never starts after deploy

Symptomapi stays down; storefront waits on API health
Likely causemigrate one-shot failed (service_completed_successfully blocks api)
Diagnosisdocker logs on the migrate container; confirm DATABASE_URL is set (name only). Exited (0) with ✓ migrations applied is success
ResolutionFix the migration or database connectivity, then redeploy. Manual fallback: pnpm deploy:db:migrate. Do not add a Dokploy domain to migrate

migrate cannot resolve PostgreSQL hostname

Symptommigrate logs getaddrinfo ENOTFOUND or similar for the DATABASE_URL host
Likely causemigrate is not on the same Docker network as the Dokploy PostgreSQL service
Diagnosisdocker inspect networks on migrate vs api; expect dokploy-network (compose external) plus bytebite and default
ResolutionConfirm dokploy-network exists and is declared external in compose.deploy.yml. Align migrate networks with api

Dokploy shows migrate as exited

SymptomCompose UI lists migrate as Exited (0) after a successful deploy
ExpectedOne-shot finished; restart: "no" keeps it stopped. This is success, not a failed deploy
ResolutionNo action if logs show ✓ migrations applied. Non-zero exit means migrate failed

Explicit routes work but wildcard does not

Symptomapi.staging… works; random *.staging… fails
Likely causeWildcard labels missing, dokploy-network not attached, or DNS-01 cert issue
DiagnosisInspect storefront/api container labels and networks; test wildcard TLS separately
ResolutionRedeploy compose; verify Traefik DNS-01 wiring. See Traefik post-upgrade checklist

Wildcard works but api/docs/dashboard are shadowed

SymptomReserved hosts serve storefront content
Likely causeWildcard priority higher than explicit Dokploy routers
DiagnosisCompare router priorities in Traefik dashboard or logs
ResolutionEnsure Dokploy explicit Host() routers exist and have higher priority than wildcard (10/20). This was verified working post-rollout — regression indicates Dokploy config loss

Certificate resolver missing

SymptomTraefik logs: unknown cert resolver letsencrypt-cloudflare
Likely causeTraefik recreated without custom traefik.yml
DiagnosisCheck /etc/dokploy/traefik/traefik.yml for letsencrypt-cloudflare block
ResolutionRestore from backup. See Disaster recovery

Cloudflare token failure

SymptomACME DNS challenge fails; Cloudflare API errors in Traefik logs
Likely causeInvalid/expired token, wrong permissions, or missing mount
DiagnosisVerify token file exists at /etc/dokploy/traefik/secrets/cloudflare-dns-api-token (mode 600); check container mount
ResolutionRotate token. See Operations

acme-cloudflare.json problems

SymptomWildcard cert not renewing or corrupt ACME state
Likely causeFile permissions, concurrent writes, or manual edit
DiagnosisCheck file exists and is valid JSON; compare with backup checksums
ResolutionRestore from /root/bytebite-traefik-post-wildcard-20260908-175333/

Dokploy recreated Traefik without secret wiring

SymptomAll explicit TLS may work (HTTP-01) but wildcard TLS fails after Dokploy upgrade
Likely causeContainer env/mount customization lost on recreate
Diagnosisdocker inspect dokploy-traefik — check for CF_DNS_API_TOKEN_FILE and secrets mount
ResolutionRe-apply customization from backup inspect. See Disaster recovery

Docker inspect permission denied

Symptomdocker inspect fails with permission denied
Likely causeUser not in docker group or not root
Diagnosisgroups; try sudo docker inspect
ResolutionUse sudo or add user to docker group (requires re-login)

Local .test request takes ~5 seconds (macOS)

SymptomLocal dev requests hang ~5s then succeed
Likely causeMissing ::1 entries in /etc/hosts — macOS tries IPv6 AAAA first
DiagnosisCheck /etc/hosts for both 127.0.0.1 and ::1 lines per host
ResolutionAdd IPv6 entries. See Local deployment

Local pnpm dev cannot find /app/pnpm

Symptomapi / docs / storefront crash with Error: Cannot find module '/app/pnpm'. dashboard exits 127 with /docker-entrypoint.sh: exec: line 47: pnpm: not found
Likely causeCompose started production images. Node's entrypoint turns a missing pnpm into node pnpm (cwd /app). Dashboard prod is nginx, so exec pnpm fails immediately. Common after a local docker compose -f compose.deploy.yml build without -p bytebite-staging
Diagnosisdocker compose -f compose.yml -f compose.dev.yml images — app images must be bytebite-<service>:dev, not :prod or untagged last-stage builds
Resolutionpnpm dev:down then pnpm dev (builds the :dev targets). For local staging tests always use -p bytebite-staging. See Local deployment

Dokploy Preview Compose has unexpected labels

SymptomPreview Compose in Dokploy UI shows labels not in Git
ExpectedDokploy injects domain labels and dokploy-network — this is normal
ResolutionNo action needed. Repository compose.deploy.yml + Dokploy injection combine at runtime. See Dokploy

Dashboard /.env returns SPA HTML

SymptomAuthenticated curl -u … https://dashboard.staging.getbytebite.co/.env returns HTTP 200 with HTML
ExpectedUnauthenticated requests are 401. With BasicAuth, nginx SPA fallback still serves index.html for dot-paths
ResolutionKnown gap. Fix requires nginx config change to reject dot-paths. See Security

Backup timer active but service shows inactive(dead)

Symptombytebite-postgres-backup.timer is active; bytebite-postgres-backup.service shows inactive (dead)
Likely causeType=oneshot service finished normally — this is expected after a successful run
Diagnosissystemctl status bytebite-postgres-backup.service; check Result: and journalctl -u bytebite-postgres-backup.service
ResolutionFailure only if result/exit code indicates failure. Successful oneshot services are always inactive (dead)

No PostgreSQL container found (backup)

SymptomBackup script or journal reports no matching PostgreSQL container
Likely causeDokploy PostgreSQL service stopped; container name changed (generated suffixes are unstable)
Diagnosisdocker ps --filter name=postgres; check Dokploy PostgreSQL service status
ResolutionRestart PostgreSQL via Dokploy. Do not hardcode old container suffixes — backup script discovers dynamically

Multiple matching PostgreSQL containers (backup)

SymptomBackup script aborts with multiple matching containers
Likely causeMore than one running postgres:18 container matches ByteBite staging criteria
Diagnosisdocker ps --filter ancestor=postgres:18
ResolutionStop or remove unintended containers. Backup intentionally aborts instead of guessing which DB to dump

pg_restore --list fails on dump

SymptomBackup validation fails at pg_restore --list
Likely causeCorrupt or incomplete dump
DiagnosisCheck backup journal for dump creation errors
ResolutionTreat dump as invalid — do not upload or use it. Investigate PostgreSQL container health and retry backup

Restic repository unavailable

SymptomBackup or maintenance fails during Restic upload, forget, prune, or check
Likely causeNetwork issue, B2 credentials invalid, or repository configuration problem
DiagnosisCheck journal for Restic error messages; verify network connectivity from APP VM
ResolutionDiagnose B2 credentials and repository configuration in /etc/bytebite/backups/ (do not print contents). Rotate B2 credentials if needed without changing Restic encryption password

restic check fails

SymptomWeekly maintenance reports repository check errors
Likely causeRepository corruption, incomplete upload, or storage backend issue
Diagnosisjournalctl -u bytebite-postgres-backup-maintenance.service
ResolutionTreat as backup-system incident. Investigate before relying on backups for recovery

Restore drill leaves container behind

Symptompostgres:18 container remains after restore drill
Likely causeScript cleanup/trap failure or interrupted execution
Diagnosisdocker ps --filter ancestor=postgres:18; check for work directories under /var/lib/bytebite-backups/
ResolutionManually remove orphaned container and temporary directories. Re-run drill to verify cleanup

Restic repository password lost

SymptomCannot decrypt or access Restic repository
Likely cause/etc/bytebite/backups/restic-password lost and no off-host copy available
ResolutionEncrypted repository cannot be recovered from B2 alone. Password must be stored in off-host credential manager

B2 credentials lost or rotated

SymptomRestic cannot authenticate to B2
Likely causeCredentials revoked, expired, or host file lost
DiagnosisCheck maintenance/backup journal for authentication errors
ResolutionRotate or recreate B2 credentials and update /etc/bytebite/backups/restic.env. Do not change Restic encryption password unless intentionally re-initializing the repository

Staging HTTPS returns 200 without a password

Symptomcurl -sI https://HOST/ on a staging app host returns 200 instead of 401
Likely causeA lower-priority unauthenticated Dokploy UI router is winning, or compose BasicAuth labels did not load
DiagnosisCompare Traefik router priorities; confirm bytebite-staging-auth exists; confirm STAGING_BASIC_AUTH_USERS is in the running container labels
ResolutionRedeploy after setting STAGING_BASIC_AUTH_USERS. Do not remove Dokploy domains (they attach dokploy-network). See Traefik

Staging HTTPS returns 401 after a correct password

SymptomBrowser/curl with the known password still gets 401
Likely causeHash interpolation ate $ (bcrypt/apr1), wrong user string, or labels not rewritten after env change
DiagnosisInspect the basicauth.users label (hash only — do not paste into tickets). SHA {SHA} hashes must contain {SHA} and no $
ResolutionRegenerate with ./infrastructure/scripts/staging-htpasswd.sh, update Dokploy, redeploy. Prefer SHA over bcrypt in Dokploy

Staging deploy fails mentioning STAGING_BASIC_AUTH_USERS

SymptomCompose interpolation error: STAGING_BASIC_AUTH_USERS is required
Likely causeVariable missing from the Dokploy compose environment
ResolutionSet the variable, then deploy. Previous containers keep running until a deploy succeeds

Dokploy UI shows ByteBite Staging BasicAuth

Symptomdokploy.getbytebite.co prompts with realm ByteBite Staging
Likely causeMiddleware attached too broadly (entrypoint or Dokploy router)
ResolutionRemove BasicAuth from the Dokploy router. The compose middleware must not target that host

On this page