Skip to main content
Version: Next (nightly)

Troubleshooting

When something goes wrong on a self-hosted instance, three questions answer most of it: is the stack running, what do the logs say, and which piece is unhappy. Everything here assumes the two-file setup from Install: a .env, a docker-compose.yml, and docker compose in the folder that holds them.

The loop:

  1. docker compose ps shows which containers are up, and their health.
  2. docker compose logs <service> prints why one is unhappy.
  3. curl -fsS http://localhost:8088/api/v1/healthz confirms the API answers.
  4. Match the symptom below and follow it to the fix.

Find your fix​

  • Install and startup: a container won't start, a migration failed on boot, a port is already in use, image pulls are denied.
  • HTTPS and access: the certificate won't issue, the phone camera is blocked, a name resolves but won't load.
  • Data and backups: restoring, the encryption key, a backup that didn't run.
  • Common error messages: a table from the exact text you see to its cause and fix.
  • The disk filled up: Docker's container logs, usually. The fix is below.

Day-two operations (updating, routine backups) live in Operating.

Read the logs​

Logs are the first stop for almost every problem.

docker compose ps # which containers are up, and their health
docker compose logs api # one service
docker compose logs -f caddy # follow it live
docker compose logs --tail 50 api
  • Each container names itself in its log lines.
  • A crash-looping container shows in docker compose ps as restarting. Its logs almost always print the reason on the last few lines before it exits.

The "is it up" check​

The API answers a health endpoint that needs no login:

curl -fsS http://localhost:8088/api/v1/healthz
  • Healthy returns JSON with "ok": true.
  • Changed WEB_PORT, or reach the box by an HTTPS address? Use that host and port instead.
  • Connection refused means the web or api container isn't up. Check docker compose ps and the api logs.

The disk filled up​

When df -h says the disk is full, check Docker's container logs first, because they are unbounded by default and the growth nobody planned for:

sudo du -sh /var/lib/docker/containers/*/*-json.log | sort -h | tail -5

Cap them once in /etc/docker/daemon.json and every future container rotates itself:

{ "log-opts": { "max-size": "20m", "max-file": "3" } }

Then restart Docker, and run docker compose up -d --force-recreate so the running containers pick the setting up.

What else grows on the box

Three things grow on a Cobblr box: photos in data/files, the nightly dumps in data/backups (rotation caps these at 30 daily plus 12 weekly), and Docker's own container logs. Once the logs are capped, disk planning is just photos plus dumps: budget for your data/files growth and keep the off-box backup copy pulling from the box rather than accumulating on it.

Where things live​

  • .env and docker-compose.yml are your whole configuration. Every setting is a line in .env. Changing one and running docker compose up -d applies it.
  • ./data/ holds all state, in one folder per service: db (the databases), files (uploads and images), backups (the stack's nightly dumps), caddy and caddy-config (issued certificates and proxy config), modules (installed module code). Copy this tree with the stack stopped and you have copied everything (see Data and backups).
  • The containers are db, api, web, and caddy (the last only when you run HTTPS). Nothing bind-mounts source code. The images are what runs.