Skip to content

Backup and restore

A complete backup has five parts: the PostgreSQL databases, Redis, the uploads volume, the SeaweedFS volume, and your .env file. If you use HTTPS, also keep the Caddy volume. Before you rely on your backups, test a restore on a spare installation.

The current setup wizard does not create or download backups. Before an upgrade, use the manual steps below and keep a secure copy of your private environment file with the data. Test a restore on a spare installation.

If you retained an archive from an earlier development build, do not assume it contains the complete SeaweedFS volume. Check its contents before relying on it as a complete backup.

The volume archive commands below use the default named volumes. If you set POSTGRES_DATA, REDIS_DATA, UPLOADS_DATA or SEAWEEDFS_DATA to a host folder, record that path with your backup. Keep using the database export commands for PostgreSQL. For Redis, uploads and SeaweedFS, back up the selected folders while their services are stopped instead of archiving the default Docker volumes. Restore those folder backups to the paths selected in .env before starting the services. A backup of an unused default volume does not contain your data.

Run these commands in your lunarpaper folder, beside compose.yaml and .env. For HTTPS, replace docker compose in each command with docker compose --project-name lunarpaper --env-file .env -f release/compose.yaml -f release/compose.https.yaml. They work in both bash and PowerShell. Lunar Paper is unavailable for a few minutes while the backup runs. If you installed with docker run, read docker run installations first.

Create a folder for the backup. On Linux and macOS, also run chmod 700 backups so only you can read it.

Terminal window
mkdir backups

Stop Lunar Paper, but leave the database, Redis and file storage running:

Terminal window
docker compose stop lunarpaper

Export both databases to a file and copy it into the backups folder:

Terminal window
docker compose exec -T postgres sh -c 'pg_dumpall --username=$POSTGRES_USER --clean --if-exists > /tmp/postgres.sql'
docker compose cp postgres:/tmp/postgres.sql backups/postgres.sql
docker compose exec -T postgres rm /tmp/postgres.sql

Save the Redis data, stop Redis and SeaweedFS, and then archive their volumes and the uploads volume:

Terminal window
docker compose exec -T redis sh -c 'REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli SAVE'
docker compose stop redis seaweedfs
docker run --rm -v lunarpaper_redis-data:/source:ro -v "${PWD}/backups:/backup" alpine tar czf /backup/redis.tgz -C /source .
docker run --rm -v lunarpaper_uploads-data:/source:ro -v "${PWD}/backups:/backup" alpine tar czf /backup/uploads.tgz -C /source .
docker run --rm -v lunarpaper_seaweedfs-data:/source:ro -v "${PWD}/backups:/backup" alpine tar czf /backup/seaweedfs.tgz -C /source .

Start everything again:

Terminal window
docker compose up -d --wait

Finally, keep a copy of the Compose files and selected platform/PostgreSQL image versions, copy .env into the backups folder, and move the folder to encrypted storage away from the server. .env holds the keys that decrypt your data, so the backup is useless without it.

Restore into a new installation. If you want to restore over an existing installation, stop it and move its volumes aside first. Never restore into volumes that already hold data.

The restore has four stages:

  1. Restore compose.yaml and .env into lunarpaper and select the exact platform and PostgreSQL versions used by the backup. For HTTPS, restore the configuration files into release and .env into the parent lunarpaper folder. Keep the original project name and use the commands for your installation method.
  2. Unpack the archives into new volumes.
  3. Start PostgreSQL and Redis, and restore the database dump.
  4. Start everything, complete the database upgrade if one is needed, and check that everything works.

The commands for each stage follow. First, unpack the archives:

Terminal window
docker volume create lunarpaper_redis-data
docker volume create lunarpaper_uploads-data
docker volume create lunarpaper_seaweedfs-data
docker run --rm -v lunarpaper_redis-data:/target -v "${PWD}/backups:/backup:ro" alpine tar xzf /backup/redis.tgz -C /target
docker run --rm -v lunarpaper_uploads-data:/target -v "${PWD}/backups:/backup:ro" alpine tar xzf /backup/uploads.tgz -C /target
docker run --rm -v lunarpaper_seaweedfs-data:/target -v "${PWD}/backups:/backup:ro" alpine tar xzf /backup/seaweedfs.tgz -C /target

Then restore the database:

Terminal window
docker compose up -d --wait postgres redis
docker compose cp backups/postgres.sql postgres:/tmp/postgres.sql
docker compose exec -T postgres sh -c 'psql --username=$POSTGRES_USER --dbname postgres -f /tmp/postgres.sql'
docker compose exec -T postgres rm /tmp/postgres.sql

Then start everything:

Terminal window
docker compose up -d --wait

You can ignore warnings that the volumes “already exist”. To check the restore, sign in, open a file you know was uploaded, and check that /api/health/ready returns HTTP 200.

A docker run installation uses the same volume names, so the archive and unpack commands work without changes. Replace the Docker Compose commands with these:

Instead of Use
docker compose stop lunarpaper docker stop lunarpaper
docker compose exec -T postgres … docker exec lunarpaper-postgres …
docker compose cp postgres:/tmp/postgres.sql … docker cp lunarpaper-postgres:/tmp/postgres.sql …
docker compose exec -T redis … docker exec lunarpaper-redis …
docker compose stop redis seaweedfs docker stop lunarpaper-redis lunarpaper-seaweedfs
docker compose up -d --wait docker start lunarpaper-redis lunarpaper-seaweedfs lunarpaper

Your saved block of settings takes the place of .env, so keep it with the backup. To restore, paste the block, create the network, and unpack the volumes. Then start the database, restore the dump, and start Redis, file storage and Lunar Paper, following install steps 4 to 7. Never generate new secrets for a restore.