Skip to content
merran
GitHubInstall

Merran documentation Run Merran

Back up and restore a server

A server backup includes the database, uploaded files, configuration, and the secret used to read stored credentials. A household export serves a different purpose and is not a substitute.

The 0.1.0-alpha.1 image passed a stopped SQLite directory restore and a Postgres 18 pg_dump/pg_restore smoke test with synthetic data. Accounts, API access, and recipes were checked after both; the SQLite test also checked an uploaded image and remaining pantry stock. Real provider credentials, large libraries, and disaster recovery on another physical host remain untested.

You need operator access, enough backup storage, the exact image version, and a maintenance window. Commands below use a Linux/POSIX shell in the directory containing the supplied compose.yaml. Adapt paths for your backup system; do not run shell redirections through a tool that rewrites binary data.

Record the image tag/digest and database version. Include compose.yaml, .env if used, any Compose override files, externally supplied MERRAN_SECRET, and relevant proxy configuration. If the secret is generated by Merran, it is the secret file inside data/app. Protect backups as sensitive data and keep a copy off the server.

Stop writes before copying the complete app directory. Choose a new backup filename each time:

Terminal window
umask 077
backup="$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p backups
docker compose stop merran
tar -czf "backups/app-$backup.tgz" -C data app
cp compose.yaml "backups/compose-$backup.yaml"
# If present, securely copy .env and any override files into the same backup set.
tar -tzf "backups/app-$backup.tgz" >/dev/null
docker compose start merran

Run each step only if the previous one succeeded. Check the archive exit status and contents, then confirm readiness after restarting. Include the entire directory, not just merran.db; stopping first also makes any SQLite journal/WAL files part of a consistent stopped snapshot. The standalone docker run installation can use a different host directory; archive the path actually mounted at /data.

Postgres: dump the database and archive app files

Section titled “Postgres: dump the database and archive app files”

Keep Merran stopped while taking both parts so uploaded files and database records agree. Leave the database running. For the supplied Compose database:

Terminal window
backup="$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p backups
docker compose stop merran
docker compose --profile postgres exec -T db \
pg_dump -U merran -d merran -Fc -f /tmp/merran-backup.dump
docker compose --profile postgres cp db:/tmp/merran-backup.dump \
"backups/database-$backup.dump"
tar -czf "backups/app-$backup.tgz" -C data app
cp compose.yaml "backups/compose-$backup.yaml"
# Securely copy .env, overrides, and external secrets into the same backup set.
docker compose --profile postgres exec -T db \
pg_restore --list /tmp/merran-backup.dump
docker compose start merran

The temporary dump is inside the database container; remove that temporary copy once a verified protected backup exists. A custom-format dump needs pg_restore, not piping it into psql. For an external database, use its supported backup tools and compatible client versions. Copying a running data/postgres directory is not this procedure and does not provide a consistent logical backup.

Run the backup commands with a restrictive file-creation mask (umask 077) and an account able to read all app files. Keep the application stopped if either backup component fails; resolve the failure before treating the backup set as complete.

Expected result: One dated backup set containing app files, deployment configuration/secrets, the image version, and (for Postgres) a readable database dump. Listing an archive proves it is readable, not that the application can recover from it; perform the restore below.

  1. Create a new empty installation directory and a distinct Compose project name, such as merran-restore. Keep the original data untouched.

  2. Restore the saved Compose files and .env. Pin the backed-up Merran version. Choose an unused loopback-only port, for example MERRAN_PORT=127.0.0.1:33082, and update MERRAN_PUBLIC_URL for the test address.

  3. Restore the app archive into the new data directory:

    Terminal window
    mkdir -p data
    tar -xzf /secure/backups/app-TIMESTAMP.tgz -C data

    This archive layout contains app/, so the result must be data/app/secret and the rest of the saved files, not data/app/app/. Restore external secret values exactly. Check file ownership matches the container’s configured user.

  4. For SQLite, start the restored app:

    Terminal window
    docker compose -p merran-restore up -d
  5. For Postgres, first initialize an empty destination database with the saved credentials, leaving Merran stopped:

    Terminal window
    docker compose -p merran-restore --profile postgres up -d db
    docker compose -p merran-restore --profile postgres exec -T db \
    pg_isready -U merran -d merran
    docker compose -p merran-restore --profile postgres cp \
    /secure/backups/database-TIMESTAMP.dump db:/tmp/merran-backup.dump
    docker compose -p merran-restore --profile postgres exec -T db \
    pg_restore -U merran -d merran --no-owner --no-privileges \
    --exit-on-error /tmp/merran-backup.dump
    docker compose -p merran-restore --profile postgres up -d merran

    Wait for pg_isready to succeed before restoring. This assumes an empty destination database, not an overwrite of a running installation. Restore to the database named in DATABASE_URL.

Prevent the restored copy from receiving public traffic or contacting real integrations while testing. Restored jobs and household schedules can resume. MERRAN_OUTBOUND=minimal reduces automatic outbound work but is not a network firewall and does not stop all requested/resumed work. Use network isolation and disable unnecessary services/schedules for a real-data restore test.

  1. Check /api/health/ready, database type, and startup logs for migration or file-access errors.
  2. Sign in with an existing account. Confirm the expected household and recipe counts.
  3. Open recipes and photos; inspect a saved plan, shopping list, pantry amount, and cooking note.
  4. Check a scoped API key. Confirm the restored secret matches the backup before testing saved external credentials in a controlled way.
  5. Review Settings → This server → Jobs for resumed or failed work.
  6. Record the image/digest, database version, backup time, commands, and results. Stop the test copy when finished.

If setup reappears, check mounts and database selection before creating anything. If secrets cannot be decrypted, restore the original secret rather than generating a new one. Keep the damaged installation and logs until recovery is confirmed.

For an update rollback, restore the complete pre-update backup and its matching image together; an older image may not understand a newer schema. Continue with updating and backups or troubleshooting.

© 2026 Kyle Johnson. Documentation: CC BY 4.0. Merran: AGPL-3.0-or-later.