Duplicati is an open-source backup tool that splits your files into deduplicated blocks, compresses and encrypts them with AES-256 on the server, and uploads them to cloud storage. The provider only ever sees encrypted data. In this tutorial you will install Duplicati on an Ubuntu 24.04 server, back up directories to an S3-compatible bucket from the command line, run the backup every night with a systemd timer and a retention policy, and restore files to prove the backups work.
Prerequisites
To follow this guide you need:
- A server running Ubuntu 24.04 LTS (x86_64 or arm64), for example a CubePath VPS, with a non-root user that has
sudoprivileges. - A bucket on Amazon S3 or any S3-compatible object storage (Backblaze B2, Wasabi, Cloudflare R2, MinIO and similar), plus:
- The S3 endpoint hostname, such as
s3.eu-central-1.amazonaws.comors3.eu-central-003.backblazeb2.com. - An access key and secret key that can only read and write that bucket. Create a dedicated key for backups instead of reusing an account-wide one.
- The S3 endpoint hostname, such as
- Enough free disk space for Duplicati's local database and temporary upload files, typically a few hundred MB plus 1 GB of headroom.
The examples use your_bucket, your_s3_endpoint, your_access_key and your_secret_key. Replace them with your own values.
Step 1 - Installing Duplicati
Duplicati publishes Debian packages on its website rather than in an APT repository. Current releases are self-contained .NET builds, so no Mono or separate runtime is needed.
Open the download page at https://duplicati.com/download, choose Linux, and copy the link for the latest stable CLI .deb package for your architecture (linux-x64 or linux-arm64). The CLI package is the right choice for a headless server. Then download it on the server, replacing your_download_url with the link you copied:
cd /tmp
wget -O duplicati-cli.deb "your_download_url"
Install it with apt, which also resolves its dependencies. The ./ prefix tells apt to use the local file:
sudo apt install ./duplicati-cli.deb
Check that the command-line tool is available:
duplicati-cli help | head -n 5
The output lists the available commands, including backup, find, restore, test and repair. Run duplicati-cli help backup at any time to see every option for a command.
Step 2 - Storing credentials and the encryption passphrase
Duplicati needs three secrets: the S3 access key, the S3 secret key and the encryption passphrase. Keep them in a root-only file instead of typing them on the command line, where they would end up in your shell history.
Generate a strong passphrase:
openssl rand -base64 32
ImportantStore this passphrase somewhere outside the server too, for example in your password manager. Without it the backups cannot be decrypted, and there is no way to recover it.
Create the configuration directory and the credentials file:
sudo install -d -m 700 /etc/duplicati
sudo nano /etc/duplicati/backup.env
DUPLICATI_URL="s3://your_bucket/server01?s3-server-name=your_s3_endpoint&use-ssl=true"
S3_ACCESS_KEY="your_access_key"
S3_SECRET_KEY="your_secret_key"
DUPLICATI_PASSPHRASE="your_generated_passphrase"
The storage URL has three parts: the bucket (your_bucket), a folder inside it (server01, useful if several servers share one bucket), and the endpoint. For AWS in a specific region, you can also add &s3-location-constraint=eu-central-1.
Restrict the file so only root can read it:
sudo chmod 600 /etc/duplicati/backup.env
Create the directory for Duplicati's local database, which records what has already been uploaded so each run only sends changes:
sudo install -d -m 700 /var/lib/duplicati
Step 3 - Creating a wrapper for Duplicati commands
Every Duplicati command for this backup needs the same URL, credentials, passphrase and database path. A small wrapper loads them once so that backing up, listing and restoring all use identical settings.
sudo nano /usr/local/sbin/duplicati-run
#!/usr/bin/env bash
# Run a duplicati-cli command against the server backup.
# Usage: duplicati-run <command> [arguments and options]
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <backup|find|restore|test|repair|compare> [args]" >&2
exit 64
fi
# shellcheck source=/dev/null
source /etc/duplicati/backup.env
command="$1"
shift
exec duplicati-cli "$command" "$DUPLICATI_URL" "$@" \
--auth-username="$S3_ACCESS_KEY" \
--auth-password="$S3_SECRET_KEY" \
--passphrase="$DUPLICATI_PASSPHRASE" \
--dbpath=/var/lib/duplicati/server01.sqlite
Make it executable and readable only by root:
sudo chmod 700 /usr/local/sbin/duplicati-run
Step 4 - Running the first backup
Decide what to back up. For a typical web server, that is configuration in /etc, user data in /home and site files in /var/www. Databases should be backed up as dumps (for example with mysqldump or pg_dump to a directory you include here), not by copying their live data files.
Run the first backup. It uploads everything, so it takes longest:
sudo duplicati-run backup /etc /home /var/www \
--backup-name=server01 \
--retention-policy="1W:1D,4W:1W,12M:1M" \
--exclude="*/.cache/" \
--exclude="*/node_modules/"
The options mean:
--retention-policy="1W:1D,4W:1W,12M:1M"keeps one version per day for the last week, one per week for the last four weeks and one per month for the last twelve months. Older versions are deleted after each backup.--excludeskips folders that are large and easy to regenerate. A trailing/matches folders.
When the run finishes, the summary ends with a success line:
Examined files: 4312 (212.45 MB)
Opened files: 4312 (212.45 MB)
Added files: 4312 (212.45 MB)
...
Backup completed successfully!
Run the same command again. This time Duplicati only scans for changes and uploads almost nothing, which is how every following run behaves.
Check your bucket in the provider's console: under server01/ you will see files named like duplicati-20260925T021503Z.dlist.zip.aes and many duplicati-*.dblock.zip.aes files. Everything ends in .aes, meaning it is encrypted before leaving the server.
Step 5 - Scheduling nightly backups with systemd
A systemd timer runs the backup every night, catches up after downtime, and logs everything to the journal.
Create the service unit:
sudo nano /etc/systemd/system/duplicati-backup.service
[Unit]
Description=Duplicati backup to S3
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/duplicati-run backup /etc /home /var/www --backup-name=server01 --retention-policy=1W:1D,4W:1W,12M:1M --exclude=*/.cache/ --exclude=*/node_modules/
# duplicati-cli exits with 1 when the run found no changed files
SuccessExitStatus=1
Nice=10
IOSchedulingClass=idle
Create the timer, which runs the service every day at 02:30 with up to 15 minutes of random delay:
sudo nano /etc/systemd/system/duplicati-backup.timer
[Unit]
Description=Nightly Duplicati backup
[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=15min
Persistent=true
[Install]
WantedBy=timers.target
Persistent=true makes systemd run a missed backup as soon as the server boots if it was off at 02:30.
Load the units, enable the timer and trigger one run immediately to test the service:
sudo systemctl daemon-reload
sudo systemctl enable --now duplicati-backup.timer
sudo systemctl start duplicati-backup.service
Check the result and the next scheduled run:
systemctl status duplicati-backup.service --no-pager
systemctl list-timers duplicati-backup.timer
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sat 2026-09-26 02:38:12 UTC 17h left - - duplicati-backup.timer duplicati-backup.service
Read the log of the last run with:
sudo journalctl -u duplicati-backup.service -n 30 --no-pager
Step 6 - Listing versions and restoring files
A backup is only useful if you can restore from it. List the backup versions stored in the bucket:
sudo duplicati-run find
Listing filesets:
0 : 9/25/2026 2:36:05 AM (4312 files, 212.45 MB)
1 : 9/25/2026 2:15:03 AM (4312 files, 212.45 MB)
Version 0 is always the newest. Search for a file across the latest version, for example your Nginx configuration:
sudo duplicati-run find "*nginx.conf"
Restore a folder to a separate location instead of overwriting the live files. This restores /etc/nginx from the newest version into /tmp/restore-test:
sudo duplicati-run restore "/etc/nginx/*" --restore-path=/tmp/restore-test
Compare the restored copy with the original:
sudo diff -r /etc/nginx /tmp/restore-test/nginx && echo "Restore matches"
Restore matches
To restore from an older version, add --version=N using a number from the find listing. To put files back in their original location, omit --restore-path and add --overwrite=true. Remove the test copy when you are done:
sudo rm -rf /tmp/restore-test
Step 7 - Verifying backup integrity
Duplicati downloads and checks a sample of remote files after every backup. You can also run a larger check on demand. This downloads and verifies 10 sets of files:
sudo duplicati-run test 10
A clean result ends without errors or Failed lines. Run it now and then, for example monthly, to detect corrupted or missing files in the bucket early.
Recovering on a new server
If the server is lost, the bucket and your passphrase are all you need. On a fresh Ubuntu 24.04 server, repeat Steps 1 to 3 with the same URL, keys and passphrase. The local database no longer exists, so rebuild it from the remote files first:
sudo duplicati-run repair
After that, find and restore work as in Step 6. On large backups the rebuild downloads a lot of metadata and can take a while.
Troubleshooting
Errors about remote files that are missing or not recorded in the local database. The local database is out of sync with the bucket, often after an interrupted run or a server restore. Run sudo duplicati-run repair.
Decryption errors or an invalid passphrase message. The passphrase in /etc/duplicati/backup.env differs from the one used to create the backup. Restore the original passphrase; a new one cannot read old data.
AccessDenied or 403 errors from S3. Check the endpoint, bucket name and that the key has read, write, list and delete permissions on the bucket. Delete permission is needed for the retention policy to remove old versions.
Backups of very large data sets get slow and the local database grows. The default block size suits most servers. For backups of several TB, set a larger --blocksize (for example --blocksize=5MB) on the very first run; it cannot be changed for an existing backup.
Conclusion
Your Ubuntu 24.04 server now sends encrypted, deduplicated backups to S3-compatible storage every night, keeps a year of versions under a clear retention policy, and you have tested both a restore and an integrity check. As next steps, add database dump jobs that run before the backup timer, send the backup to a second provider for an off-site copy in another region, and alert on failed runs by adding an OnFailure= unit to the service.
