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 sudo privileges.
  • 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.com or s3.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.
  • 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

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.
  • --exclude skips 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.