Skip to content
Betts BoardHome
Docs navigation

Operating it

Backup & restore

What lives in the volume, how to back it up and restore it, and how to reset a forgotten password.

What lives in the volume

Everything the board owns is in one named volume, betts-data, mounted at /data:

Path What it is
betts.db The SQLite database — calendar, chores, recipes, meal plans, lists, profiles, settings
uploads/ Photos and recipe images
.session-secret The key that seals session cookies
.finance-key Present only if you use Money; decrypts stored bank credentials

There is no second database and no external state. Copy that volume and you have copied the board.

Backup

# Excludes the cookie-sealing secret on purpose — see below
docker compose stop betts-board
docker run --rm -v betts-board_betts-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/betts-backup.tar.gz -C /data --exclude=./.session-secret .
docker compose start betts-board

Stopping the container first means SQLite is not mid-write when the tarball is made. The board is down for the few seconds the tar takes.

The volume name is prefixed with the Compose project name, which defaults to the directory you cloned into. If you renamed that directory, run docker volume ls and use the name you actually see.

Why the --exclude

.session-secret is the key that seals session cookies. It is generated on first boot and reused forever, so a backup containing it lets anyone holding that backup mint a cookie the live board accepts — no password needed, from anywhere the board is reachable.

Backups get copied to laptops and cloud drives. The running board's login key should not ride along. Leaving it out costs nothing: a restored install generates a fresh one automatically, and everyone simply unlocks once with the household password.

The backup is still sensitive

It contains every photo, the whole calendar, and — if you use Money — .finance-key, which decrypts your stored bank credentials. That file has to stay in the backup: restore without it and you will be asked to reconnect the bank, though Money still works and your history is intact.

So store the tarball somewhere you would be willing to store your family's records, and encrypt it if it leaves the house.

Restore

Into a fresh install:

docker run --rm -v betts-board_betts-data:/data -v "$PWD":/backup alpine \
  tar xzf /backup/betts-backup.tar.gz -C /data
docker compose up -d

The container generates a new .session-secret on boot, so everyone unlocks once with the household password. Migrations run automatically, which means a backup from an older version restores cleanly into a newer image.

Scheduled backups

A cron line on the host is enough. This one keeps a dated tarball every night at half past two:

30 2 * * * cd /srv/betts-board && docker compose stop betts-board && docker run --rm -v betts-board_betts-data:/data -v /srv/backups:/backup alpine tar czf /backup/betts-$(date +\%F).tar.gz -C /data --exclude=./.session-secret . && docker compose start betts-board

Note the escaped \%F — cron treats a bare % as a newline. Point the backup directory at something that leaves the house, and prune old tarballs on whatever schedule suits you.

Reset the password

Forgotten the household password? Start one boot with BETTS_RESET_PASSWORD=1 and the unlock screen lets you set a new one.

services:
  betts-board:
    environment:
      BETTS_RESET_PASSWORD: "1"
docker compose up -d

It clears the password once and then refuses to act again until you remove the variable, so a copy left in docker-compose.yml cannot quietly reopen the board on every restart. Remove it anyway once you are back in — the log says so too.

Reset a Money PIN

Same pattern, different variable: BETTS_RESET_FINANCE_PIN=1 clears every Money PIN on the next boot. Bank connections and history are untouched, and whoever sets a PIN first afterwards becomes the Money owner.

This needs access to the server itself, which is the point — Money's PIN is meant to hold against someone standing at the tablet, and the escape hatch should require more than that. Remove the variable once you are back in.

About the unlock rate limit

Repeated wrong passwords slow the unlock screen down. After ten consecutive failures — counted household-wide, across every device together, so guessing from many addresses buys nothing — the board refuses unlock attempts for a minute, escalating to at most fifteen.

It never locks permanently, and one correct password clears it. The counter lives in the data volume, so restarting the container does not reset it.

Edit this page on GitHub