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:
docker compose psshows which containers are up, and their health.docker compose logs <service>prints why one is unhappy.curl -fsS http://localhost:8088/api/v1/healthzconfirms the API answers.- 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 psas 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
weborapicontainer isn't up. Checkdocker compose psand theapilogs.
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
.envanddocker-compose.ymlare your whole configuration. Every setting is a line in.env. Changing one and runningdocker compose up -dapplies it../data/holds all state, in one folder per service:db(the databases),files(uploads and images),backups(the stack's nightly dumps),caddyandcaddy-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, andcaddy(the last only when you run HTTPS). Nothing bind-mounts source code. The images are what runs.