Rezepte

Data and backup

Where Rezepte stores its data and how to back it up safely

Everything Rezepte owns lives under one data directory (REZEPTE_DATA_DIR, /data in the Docker image):

rezepte.db
rezepte.db-wal
rezepte.db-shm
<imageId>.jpg
<imageId>_detail.jpg
<imageId>_thumb.jpg
<avatarId>.jpg

rezepte.db is the SQLite database: recipes with their ingredients, steps and step times, tags and photo records, comments and which of them you have seen, accounts with their email addresses, sessions, API tokens, open setup links, links for a new password and confirmation links, favorites and tasty marks, public links, the mail settings, and the household settings — editing rights, public sharing, link previews and the key that signs link previews. A mail server password set in the app under Settings → Email is stored in it unencrypted, so keep your backups as private as the password itself; one set with REZEPTE_SMTP_PASSWORD stays out of the database. While Rezepte runs, SQLite keeps its most recent writes in rezepte.db-wal (with rezepte.db-shm as its index) before folding them into rezepte.db, so the three files are one database: copy them together or not at all. Each recipe photo is stored as three JPEG variants: the original (resized to at most 2400 px), a _detail variant (1600 px) and a _thumb variant (960 px). The profile photos of the accounts are one small square JPEG each, in avatars/ beside images/; the database only records which file is whose, so a backup without avatars/ restores every account with its initial instead of its photo.

To pass recipes to another instance rather than back them up, see Import and export.

Backing up

The compose file keeps /data in a named volume, and the image has neither a shell nor the sqlite3 tool, so the backup runs in a throwaway container that borrows the volume. Stop Rezepte for the few seconds it takes, so the database cannot change mid-copy, and pack the whole data directory into one archive:

docker compose stop rezepte
docker run --rm --volumes-from "$(docker compose ps -aq rezepte)" \
  -v "$PWD/backups:/backup" alpine:3 \
  tar czf "/backup/rezepte-$(date +%F).tar.gz" -C /data .
docker compose start rezepte

Run it next to your docker-compose.yaml; the archive lands in backups/. --volumes-from finds the volume through the container, which also works while it is stopped, so you never need the volume's full name, and the same commands work for a bind mount. The archive holds the database with any -wal and -shm files beside it, and every photo, profile photos included.

Started with docker run --name rezepte instead of compose? Use docker stop rezepte and docker start rezepte, and --volumes-from rezepte.

Never copy rezepte.db alone with cp while the app is running. SQLite's WAL mode keeps recent writes in rezepte.db-wal, so a plain file copy of rezepte.db can miss them or capture the database mid-write. Use sqlite3 .backup, or stop the app first, as the Docker variant does.

Restoring

Stop Rezepte, empty the volume, unpack the archive you want into it and give every file back to the container's user, UID 65532 — the throwaway container runs as root, and Rezepte cannot write to files root owns:

docker compose stop rezepte
docker run --rm --volumes-from "$(docker compose ps -aq rezepte)" \
  -v "$PWD/backups:/backup:ro" alpine:3 \
  sh -c 'rm -rf /data/* && tar xzf /backup/rezepte-2026-01-31.tar.gz -C /data && chown -R 65532:65532 /data'
docker compose start rezepte

Replace rezepte-2026-01-31.tar.gz with the name of your archive. The -wal and -shm files in it belong to the database next to them, so they are restored with it.

On this page