Skip to main content
Version: Next (nightly)

Operating your instance

Day-two care: keeping it current, keeping copies of your data, and the three things that most often go wrong.

Updating​

docker compose pull && docker compose up -d

It pulls any newer images and recreates the containers. Database migrations run automatically when the api container starts. To have this happen on its own, see Updating.

Backups​

Sync data/backups/ (and data/files/) somewhere off the box on a schedule.

  • The stack already dumps the database nightly into data/backups/ (30 daily and 12 weekly files kept). Your job is getting a copy off the box, because a backup beside the data dies with the disk.
  • Your data lives in bind-mounted folders under one data root (COBBLR_DATA_ROOT, ./data by default): Postgres, uploaded files, nightly dumps, installed modules, and Caddy certs. If you pointed COBBLR_DATA_ROOT at a data disk or a NAS mount, that path is the one to protect.
  • A full-instance copy of the whole ./data/ tree is only a valid backup with the stack stopped. A live copy of a running Postgres directory is a torn snapshot.
  • For a hand-run dump at any moment:
    docker compose exec db pg_dumpall -U cobblr | gzip > cobblr-backup-$(date +%F).sql.gz

Workspace-level backups (restorable snapshots of one workspace, and off-box destinations like Google Drive) are covered in Backup and export.

Stopping, starting, restarting​

Ordinary operations are plain compose:

  • docker compose stop parks the stack with data untouched.
  • docker compose up -d brings it back.
  • docker compose restart api bounces one service.
  • down (without -v) also just stops. Your data is in bind mounts, so no compose command short of your own rm deletes it.

Moving to new hardware​

A move is a copy of three things: the .env, the docker-compose.yml, and the ./data/ tree, taken while the stack is stopped.

  1. On the old box: docker compose down, then copy the folder over (rsync -a the directory containing the compose file).
  2. On the new box: install Docker, docker compose up -d in the copied folder. The images re-pull by themselves, and they exist for both amd64 and arm64, so moving between a PC and a Pi is the same move.
  3. The .env matters most: TENANT_CREDS_ENCRYPTION_KEY must travel or stored integration credentials become unreadable, and POSTGRES_PASSWORD must travel or the copied database refuses the new stack.

If the new box also changes the address, see the next section.

Changing the address later​

  1. Edit COBBLR_SITE_ADDRESS in .env (and the HTTPS block if the mode changes).
  2. docker compose up -d. The proxy re-issues for the new name.

Two things do not follow automatically: bookmarks, and printed QR labels, which carry the old base URL. The QR pages cover printing against a redirect so a future move never costs a re-print. If labels already carry the old name, stand up a redirect at it.

Uninstalling​

  1. Take a copy of data/backups/ first if there is any chance you will want the records back.
  2. docker compose down stops and removes the containers.
  3. Delete the compose folder, ./data/ included. Everything Cobblr ever wrote lives in that folder, so this is the complete uninstall.
  4. docker image prune (or docker rmi on the cobblr images) reclaims the image space.

Troubleshooting​

  • A device can't reach it (Tailscale). First confirm that device is signed into the same tailnet. What to check next depends on which Tailscale setup you chose. On the own-hostname setup (COBBLR_TLS_MODE=tsnet), the bundled proxy is the tailnet node: check docker compose logs caddy for the one-time approval link on a first run, and confirm the node still shows as authorized in the admin console (a tsnet node signs itself out after about six months unless you disable its key expiry). On the server's-own-name setup, tailscale serve status on the box should show the app on 127.0.0.1:8088, and reaching it from off the tailnet needs tailscale funnel, not serve.
  • Phone can't reach the public (Cloudflare) name, laptop can. Some routers and Pi-hole setups block public names that resolve to private IPs ("DNS rebind protection"). Add an exception for the name, or add a local DNS entry mapping it to the LAN IP. This only applies to the internet-facing paths, since Tailscale doesn't use public DNS at all.
  • Certificate didn't issue (Cloudflare, or a legacy DuckDNS setup). Check docker compose logs caddy. The usual causes: a wrong CLOUDFLARE_API_TOKEN (or DUCKDNS_TOKEN), or the box has no outbound internet to answer the ACME challenge. Certificates cache under ./data/caddy and renew themselves about every 60 days with the same DNS token, so a revoked token surfaces weeks later as an expiry. Set COBBLR_ACME_EMAIL and Let's Encrypt emails you before a failed renewal becomes an outage.
  • Locked out: signup is closed and nobody can get in. Closed signup plus no operator is recoverable, because you own the box: everything is one .env edit away. No accounts at all? Set PUBLIC_SIGNUP_ENABLED=true, run docker compose up -d, register, then close it again. Have an account but no /admin? Put that account's email in SUPERADMIN_EMAILS and docker compose up -d. The operator check reads the list live, so the existing account is blessed retroactively. Once you're the operator, add people through signup invites instead of reopening signup.
  • Camera still blocked. Confirm the address bar shows https:// with no warning. On the offline CA option, the certificate must be trusted, not just installed. On iOS that's the extra toggle under Certificate Trust Settings.