This article covers how to back up and restore a self-hosted Hudu instance. A complete backup has two separate parts: your PostgreSQL database and your uploaded files. Both are covered below, along with how to verify that your backups are actually usable and how to restore from them.
A complete backup has two parts. The database stores your documentation's structure, records, and text, but it only stores references to your uploaded files, not the files themselves. If you restore the database without also restoring your files, Hudu will load with broken images, missing attachments, and documents that can't be downloaded. Always back up both.
Visit Self-Hosted Setup: File Storage for details on how file storage is configured, and Self-Hosted: Updating your Hudu Version for related maintenance tasks.
Backups of Hudu
Manual backup of Hudu Postgres
- Log in to the server you want to back up.
- Run
cd ~/hudu2 - Make sure the server is up and running.
- Run:
sudo docker compose exec -T db pg_dump -U postgres hudu_production > NAME-OF-DUMP.sql - This file can now be moved onto a new server to be used. Make sure to keep the same
.envvariables.
A manual dump like this captures your database only. It does not include your uploaded files. See Backing up and restoring files below to back those up as well.
Auto-backup script to S3 (Hudu Software - Doubletake)
The first thing to automate is your database. There are multiple ways to automatically back up Postgres databases. One simple way is to add the Doubletake service to the end of your docker-compose.yml. On the schedule you set, it dumps your database, verifies and optionally compresses or encrypts the dump, and uploads it to your S3-compatible bucket. It can also download, verify, and restore that dump when you need it.
postgres3:
image: hudusoftware/doubletake:latest
restart: unless-stopped
links:
- db
environment:
S3_HOST_BASE: 's3.us-west-1.wasabisys.com'
S3_REGION: 's3.us-west-1'
S3_BUCKET: 'bucketname'
S3_FOLDER: 'foldername'
S3_ACCESS_KEY_ID: 'XXXX'
S3_SECRET_ACCESS_KEY: 'XXXXXXXX'
CRON_SCHEDULE: '0 */6 * * *'
DB_NAME: 'hudu_production'
DB_USER: 'postgres'
POSTGRES_PASSWORD: # Optional — DB_PASSWORD is deprecated; only needed if your db isn't using trust-mode auth
DB_HOST: 'db'
DB_BACKUP_VERIFY: '1'
DB_BACKUP_COMPRESS: '0' # Optional, default is 0
DB_BACKUP_COMPRESS_LEVEL: # Optional, default is 4
POSTGRES_EXTRA_OPTS: '--schema=public --blobs'
DISK_SPACE_THRESHOLD: 'XX' # Optional, minimum percentage of free disk space required to run a backupA few of the key options:
-
CRON_SCHEDULE— how often the backup runs. The example above (0 */6 * * *) runs every six hours. -
DB_BACKUP_VERIFY— set to1to enable Doubletake's built-in integrity check (see the verification section below). -
DB_BACKUP_COMPRESS— set to1to compress dumps before upload. Compressed dumps are written as.xzfiles. -
DB_BACKUP_ENCRYPT_KEY/DB_BACKUP_ENCRYPT_IV— optional, encrypts dumps before upload. Generate withopenssl rand -hex 32andopenssl rand -hex 16respectively. -
S3_MD5_REQUIRED— set to1if your bucket has file locking or versioning enabled; required in that case. -
AUTO_LIFECYCLE_POLICY_TAGS— set to1to automatically tag backups (monthly/weekly/hourly) for use with your S3 provider's lifecycle policies. -
DISK_SPACE_THRESHOLD— optional. Before each backup, Doubletake checks how much disk space is free, and by default it won't run unless more than 50% of the disk is available. If your database dumps are small compared to your free space, you can lower this requirement by setting it to the percentage that fits your environment.
Doubletake also supports additional advanced options — including custom
S3
request
headers (S3_REQUEST_HEADERS) and extra pg_dump
arguments
(PG_DUMP_USER_ARGS) — see the full list on the Doubletake
page
linked
below.
To run a backup on demand rather than waiting for the schedule:
sudo docker compose exec -T postgres3 /backup.sh
To view the log from the most recent run (useful for confirming a backup
succeeded
or troubleshooting a failure), replace postgres3 with your
container
name if you changed it:
sudo docker compose exec -T postgres3 /bin/sh -c "cat /doubletake.log"
Verifying the integrity of your backups
There are two layers of verification, and it's important to understand what each one does and does not prove.
1. Automated file verification (built in to Doubletake)
When DB_BACKUP_VERIFY is set to 1, Doubletake
calculates
a BLAKE2 hash and byte size for each dump and stores them in a small
.meta file next to the dump in your bucket. When you download
or
restore that dump, Doubletake recalculates the hash and compares it to
the
.meta file to confirm the dump has not changed or become
corrupted
since it was written. On a successful restore or download you'll see
confirmation
in the output, for example:
metadata downloaded to /dump/backup.meta Metadata is the same. Metadata is identical. Download successful.
Visit the Hudu Software - Doubletake page for the full list of configuration options and manual backup, restore, and verification commands.
2. Manual verification (recommended on a schedule)
The only way to be confident a backup is usable is to periodically restore it into a separate, non-production environment and confirm that Hudu comes up and your data is present. We recommend doing this on a recurring basis rather than only when you need it.
Between full test restores, you can run these quick sanity checks on a dump file:
-
Confirm the file exists and isn't empty. A backup that failed part-way can leave a zero-byte or truncated file:
ls -lh NAME-OF-DUMP.sql
-
For an uncompressed plain SQL dump, confirm it completed. A complete
pg_dumpends with a completion marker on the last line. If it's missing, the dump was cut short:tail -n 1 NAME-OF-DUMP.sql # expected: -- PostgreSQL database dump complete
-
For a compressed dump, test the archive. This confirms the compressed file isn't corrupted:
xz -t backup.sql.xz
To run a full test restore, restore the dump into a scratch database or a throwaway Hudu instance using the steps in Restoring a database backup below, then log in and spot-check that your records, passwords, and attachments are present. If the restore completes without errors and the data looks correct, you have a verified, usable backup.
Schedule a recurring test restore, for example monthly. A backup you have never restored is a backup you cannot rely on.
Restoring a database backup
Once you have a database backup, you can restore your Hudu instance to the old backup by following these steps:
- Make sure you have an up-to-date backup of your documentation before you begin.
-
Move the
.sqldatabase dump file into the~/hudu2directory. Typically, the easiest way to move files is via SCP or SFTP. -
Run
sudo docker compose downto bring your instance down. -
Run
sudo docker compose up -d db -
Run the command:
sudo docker compose exec db dropdb hudu_production -U postgres -
Run the command:
sudo docker compose exec db createdb hudu_production -U postgres -
Run the command:
cat NAME-OF-DUMP.sql | sudo docker compose exec -T db psql -d hudu_production -U postgres -
Run
sudo docker compose down -
Run
sudo docker compose up -dto get your instance back up and running.
If you back up with
Doubletake,
you can also restore directly from your bucket by running
sudo docker compose exec -T postgres3 /bin/bash -c "/restore.sh",
which downloads the latest dump, verifies it against its
.meta
file, and restores it.
Backing up and restoring files
Your uploaded files (photos, attachments, document files, and logos) are stored separately from the database. Where they live depends on your file storage configuration: either on the local Docker volume or in an S3-compatible object storage bucket. Back up whichever one you use, on a schedule, alongside your database backups.
Local storage
When using local storage, files are located here:
/var/lib/docker/volumes/hudu2_app_data/_data/.
To restore, move this directory from the old server to the new server,
keeping
the same path.
S3 bucket storage
If your files live in an S3-compatible bucket (for example Wasabi or AWS S3), your backup job needs to copy the contents of that bucket somewhere else, on a schedule. Restoring the database alone will not bring your files back, so this step is required for a complete backup. You can use the AWS CLI, s3cmd, rclone, or a similar tool.
Sync the bucket to your local system with the AWS CLI
aws s3 sync s3://your-bucket-name /home/ubuntu/s3/your-bucket-name/
If your provider is not AWS (for example Wasabi), add the
--endpoint-url flag pointing at your provider:
aws s3 sync s3://your-bucket-name /home/ubuntu/s3/your-bucket-name/ --endpoint-url=https://s3.wasabisys.com
Sync the bucket with s3cmd
s3cmd sync s3://your-bucket-name /home/ubuntu/s3/your-bucket-name/
For an offsite copy, you can also sync one bucket to another (ideally with a different provider or region) rather than pulling everything to local disk. Whichever approach you use, automate it with a cron job so file backups run as regularly as your database backups, and keep the copy separate from your primary bucket.
We also recommend using lifecycle rules to move older S3 backups to lower-cost cold storage such as Amazon Glacier.
Keep your backup bucket private. No one should be able to list the contents of a bucket that holds your files. Use IAM users and per-folder access policies, and never make the bucket public.
Moving from object to local or vice versa
Both local and object storage use the same directory structure. When
moving
to
or from local storage, move the files from
/var/lib/docker/volumes/hudu2_app_data/_data/.
FAQ
Yes. The database stores your documentation's structure and text and points to your files, but it does not contain the files themselves. If you restore only the database, Hudu will load with broken images and missing attachments. Back up both your database and your file storage.
Doubletake's built-in verification confirms the backup file is intact in storage, but the only way to know a backup is truly usable is to restore it into a separate, non-production environment and confirm Hudu comes up with your data intact. See Verifying the integrity of your backups above. We recommend running a test restore on a recurring schedule.
If you are using Docker Compose V1, you will need to add
a dash
to
docker-compose (for example, docker-compose
instead of docker compose).