Duplicacy is a backup tool that splits files into content-defined chunks, deduplicates them and uploads them, encrypted, to cloud or remote storage. Its lock-free design lets several servers back up to the same storage at once and share deduplicated data. In this tutorial you will install the Duplicacy command-line client on Ubuntu 24.04, back up /var/www to an S3-compatible bucket, keep a second copy on an SFTP server, restore files, and schedule backups and pruning with a systemd timer.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS, for example a CubePath VPS. The steps are the same on Debian 12.
  • A non-root user with sudo privileges. Backups run as root so they can read every file.
  • An S3-compatible bucket (AWS S3, Wasabi, Backblaze B2 S3 API, MinIO and similar), with its endpoint, region, access key and secret key.
  • Optional, for the second copy: an SFTP server with a user that can log in with an SSH key.

Key concepts

Duplicacy uses its own vocabulary:

TermMeaning
RepositoryThe local directory being backed up, here /var/www
StorageWhere backups are stored (a bucket, an SFTP path, a local disk). A repository can have several
Snapshot IDA name that identifies this repository's backups inside a storage, for example web01-www
RevisionOne backup run. Each revision is a full, restorable snapshot

Step 1 - Installing the Duplicacy CLI

Duplicacy is distributed as a single static binary on GitHub. Check the latest version on the releases page and set it in a variable (use arm64 instead of x64 on ARM servers):

DUPLICACY_VERSION=3.2.4
curl -fL -o duplicacy "https://github.com/gilbertchen/duplicacy/releases/download/v${DUPLICACY_VERSION}/duplicacy_linux_x64_${DUPLICACY_VERSION}"

Install the binary into your PATH:

sudo install -m 0755 duplicacy /usr/local/bin/duplicacy
rm duplicacy

Verify it:

duplicacy -version
duplicacy version 3.2.4 (1A6B2E)

Step 2 - Initializing the repository

By default Duplicacy stores its settings, including credentials, in a .duplicacy directory inside the folder being backed up. For a web root that is a bad idea, because the web server could expose it. Keep the settings in a root-only directory instead and point to the data with -repository:

sudo mkdir -p /etc/duplicacy/www
sudo chmod 700 /etc/duplicacy
cd /etc/duplicacy/www

Initialize an encrypted repository. The storage URL format for S3-compatible storage is s3://region@endpoint/bucket/path. Replace the values with yours:

sudo duplicacy init -e -repository /var/www web01-www s3://[email protected]_provider.com/your_bucket/duplicacy

Duplicacy asks for the access key, the secret key and a storage password. The storage password encrypts all data and metadata in the bucket.

/var/www will be backed up to s3://[email protected]_provider.com/your_bucket/duplicacy with id web01-www

Step 3 - Saving credentials for unattended runs

Scheduled backups cannot answer prompts, so save the credentials in the repository preferences with duplicacy set:

sudo duplicacy set -key password -value "your_storage_password"
sudo duplicacy set -key s3_id -value "your_access_key"
sudo duplicacy set -key s3_secret -value "your_secret_key"

These values are stored in plain text in /etc/duplicacy/www/.duplicacy/preferences. Make sure only root can read it:

sudo chmod 600 /etc/duplicacy/www/.duplicacy/preferences
sudo ls -l /etc/duplicacy/www/.duplicacy/preferences
-rw------- 1 root root 612 Sep 25 10:12 /etc/duplicacy/www/.duplicacy/preferences

Step 4 - Excluding files you do not need

Create a filters file to skip caches, logs and dependencies that can be rebuilt:

sudo nano /etc/duplicacy/www/.duplicacy/filters
-*.log
-*/cache/
-*/node_modules/
-*/.git/

Each line starts with - (exclude) or + (include). Paths are relative to /var/www, directories end with /, and the first pattern that matches a path wins. Adjust the list to your own sites.

Step 5 - Running the first backup

Run a backup and print statistics at the end. -threads uploads several chunks in parallel, which speeds up the first run over the network:

cd /etc/duplicacy/www
sudo duplicacy backup -stats -threads 4
...
Files: 18342 total, 1,204M bytes; 18342 new, 1,204M bytes
All chunks: 312 total, 1,210M bytes; 312 new, 1,210M bytes, 402M bytes uploaded
Total running time: 00:02:41
Backup for /var/www at revision 1 completed

Run it again. Only changed chunks are uploaded, so the second run takes seconds. List the revisions in the storage:

sudo duplicacy list
Snapshot web01-www revision 1 created at 2026-09-25 10:15 -hash
Snapshot web01-www revision 2 created at 2026-09-25 10:17

Step 6 - Adding a second storage over SFTP

One copy of your backups is not enough. Add an SFTP storage that is copy-compatible with the first one, so Duplicacy can copy chunks between them without re-reading /var/www. The double slash in the URL means an absolute path on the server:

cd /etc/duplicacy/www
sudo duplicacy add -e -copy default offsite web01-www sftp://[email protected]_domain//srv/backups/duplicacy

Use a different storage password for this storage if you like. Then save the password and the SSH key that root will use to log in:

sudo duplicacy set -storage offsite -key password -value "your_offsite_password"
sudo duplicacy set -storage offsite -key ssh_key_file -value /root/.ssh/id_ed25519

Make sure root already has the server's host key in /root/.ssh/known_hosts by connecting once with sudo ssh [email protected]_domain.

Copy all revisions from the default storage to the new one:

sudo duplicacy copy -from default -to offsite -threads 4

Confirm that the revisions are there:

sudo duplicacy list -storage offsite

Step 7 - Restoring files

Test a restore before you need one. There are three common cases.

To see which files a revision contains:

cd /etc/duplicacy/www
sudo duplicacy list -r 2 -files | head

To recover a single file without touching the live data, print it to a new location with cat:

sudo duplicacy cat -r 2 example.com/index.php > /tmp/index.php

To restore a whole revision to a different directory, initialize a second repository that uses the same snapshot ID and storage, then restore into it:

sudo mkdir -p /srv/restore-www
cd /srv/restore-www
sudo duplicacy init web01-www s3://[email protected]_provider.com/your_bucket/duplicacy
sudo duplicacy restore -r 2 -stats -threads 4

Duplicacy asks for the same credentials as in Step 2. Compare the result with the live site:

sudo diff -rq /var/www /srv/restore-www --exclude=.duplicacy

Running duplicacy restore -r N -overwrite from /etc/duplicacy/www restores in place over /var/www. Use it only when you really want to roll the live data back.

Step 8 - Pruning old revisions

Revisions accumulate forever unless you prune them. -keep n:m means "keep one revision every n days for revisions older than m days", and n = 0 means "delete all revisions older than m days". List the rules from the largest m to the smallest:

cd /etc/duplicacy/www
sudo duplicacy prune -dry-run -keep 0:360 -keep 30:90 -keep 7:30 -keep 1:7

This policy keeps every revision from the last 7 days, one per day up to 30 days, one per week up to 90 days, one per month up to a year, and deletes everything older. The -dry-run flag shows what would be removed without deleting anything. Remove it to apply the policy, and run the same command with -storage offsite for the second storage.

Step 9 - Scheduling backups with a systemd timer

A oneshot service can run several commands in order and stops at the first failure, which is all this job needs. Create the service:

sudo nano /etc/systemd/system/duplicacy-www.service
[Unit]
Description=Duplicacy backup of /var/www
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
WorkingDirectory=/etc/duplicacy/www
Nice=10
IOSchedulingClass=idle
ExecStart=/usr/local/bin/duplicacy -log backup -stats -threads 4
ExecStart=/usr/local/bin/duplicacy -log copy -from default -to offsite -threads 4
ExecStart=/usr/local/bin/duplicacy -log prune -keep 0:360 -keep 30:90 -keep 7:30 -keep 1:7
ExecStart=/usr/local/bin/duplicacy -log prune -storage offsite -keep 0:360 -keep 30:90 -keep 7:30 -keep 1:7

The global -log option adds timestamps and log levels to the output, which ends up in the journal. Create the timer:

sudo nano /etc/systemd/system/duplicacy-www.timer
[Unit]
Description=Nightly Duplicacy backup of /var/www

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=30min
Persistent=true

[Install]
WantedBy=timers.target

Enable the timer and run the service once by hand to test it:

sudo systemctl daemon-reload
sudo systemctl enable --now duplicacy-www.timer
sudo systemctl start duplicacy-www.service

Check the result and the next scheduled run:

sudo journalctl -u duplicacy-www.service -n 20 --no-pager
systemctl list-timers duplicacy-www.timer
NEXT                        LEFT     LAST                        PASSED  UNIT                 ACTIVATES
Fri 2026-09-26 03:12:40 UTC 16h left Thu 2026-09-25 10:31:02 UTC 2min ago duplicacy-www.timer  duplicacy-www.service

To be alerted when a nightly run fails or does not happen at all, add a ping to a monitoring service such as Healthchecks with ExecStopPost=.

Step 10 - Verifying the storage

Once a week or so, check that every chunk referenced by your revisions exists in the storage:

cd /etc/duplicacy/www
sudo duplicacy check -stats
sudo duplicacy check -storage offsite

check only verifies that chunks exist. The restore test in Step 7 is still the only proof that your backups work end to end, so repeat it from time to time.

Troubleshooting

The job stops and asks for a password. A credential is missing from the preferences file. Run the command by hand from /etc/duplicacy/www to see which prompt appears, then save the value with duplicacy set (add -storage offsite for the second storage).

SFTP storage fails with a host key error. Root does not know the server's host key yet. Connect once with sudo ssh [email protected]_domain and accept the key.

Access denied from S3. Check the region and endpoint in the storage URL and that the key has list, read, write and delete permissions on the bucket and prefix.

Backups are slow. Increase -threads for the upload, and add large directories that do not need backing up (caches, build output) to the filters file.

Conclusion

You now have encrypted, deduplicated backups of /var/www in an S3-compatible bucket, a second copy on an SFTP server, a retention policy, and a tested restore procedure, all running from a systemd timer. Next, create a repository for each other directory you need to protect (for example /etc and your database dumps), reuse the same storage from other servers with their own snapshot IDs to benefit from cross-server deduplication, and add monitoring so you hear about failed runs.