How to back up a Linux server with Kopia
Kopia is free, open-source backup software that splits files into chunks, stores each chunk once, encrypts everything on the server and writes it to S3-compatible storage, an SFTP server or a local disk. On Ubuntu, install the kopia package from Kopia's apt repository, create a repository with kopia repository create s3, set a retention and compression policy, and run kopia snapshot create from a systemd timer. The repository password is the only way to decrypt the backups, so store it off the server before the first one.
What Kopia is and how it stores backups
Kopia is open source (Apache 2.0), written in Go and shipped as one kopia binary; the current release in October 2026 is 0.23.1, from 16 June 2026. It saves directories as snapshots into a repository (a bucket prefix, an SFTP folder or a local directory), and policies stored in the repository decide what to keep, skip and compress. It uploads only the content-defined chunks the repository does not hold yet, so every snapshot after the first is incremental, and it always encrypts on the server first (AES256-GCM-HMAC-SHA256 by default).
KopiaUI (a desktop app) and kopia server (a web UI) exist, but a server needs neither. Other tools for the same job: restic, BorgBackup and Duplicati.
Install Kopia from its apt repository
sudo install -d -m 0755 /etc/apt/keyringscurl -s https://kopia.io/signing-key | sudo gpg --dearmor -o /etc/apt/keyrings/kopia-keyring.gpgecho "deb [signed-by=/etc/apt/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main" | sudo tee /etc/apt/sources.list.d/kopia.listsudo apt updatesudo apt install kopiakopia --version/etc/apt/keyringsexists on recent Ubuntu releases; the first command only makes sure.signed-bytrusts the key for this repository only.gpg --show-keys /etc/apt/keyrings/kopia-keyring.gpgshould show7FB99DFD47809F0D5339D7D92273699AFD56A556andKopia Builder <[email protected]>, the key Kopia's docs show signing its releases. The URL is plainhttp://, as Kopia gives it; apt refuses any package that does not match the signed index.stablecarries releases (testingandunstablealso exist). Skipkopia-ui, the desktop app. The binary is/usr/bin/kopiaand updates withapt upgrade.
Pin Kopia's files under /etc/kopia
Kopia finds its files through $HOME (~/.config/kopia, ~/.cache/kopia), and a shell, a cron job and a systemd service can disagree about $HOME. Name every path once, in a root-only file they all read:
sudo install -d -m 700 /etc/kopiaKOPIA_CONFIG_PATH=/etc/kopia/repository.config
KOPIA_CACHE_DIRECTORY=/var/cache/kopia
KOPIA_LOG_DIR=/var/log/kopia
KOPIA_CHECK_FOR_UPDATES=false
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-keysudo chmod 600 /etc/kopia/envKOPIA_CONFIG_PATHis the connection file. On create or connect, Kopia saves the bucket, endpoint and keys in it, and the password beside it inrepository.config.kopia-password(base64, not encrypted), so keep/etc/kopiaat mode 700.KOPIA_CACHE_DIRECTORYandKOPIA_LOG_DIRplace the local cache and the log files.KOPIA_CHECK_FOR_UPDATES=falsestops update checks; apt handles upgrades.AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEYhold the bucket key on any S3-compatible provider. The--access-keyand--secret-access-keyflags override them, but typed keys land in shell history.
Load the file in a root shell; set -a exports every variable it defines:
sudo -iset -a; . /etc/kopia/env; set +aCreate the repository in an S3-compatible bucket
Kopia does not create buckets. Make one and a key for it first (an S3 bucket for backups, Backblaze B2), then generate a password and put it in your password manager:
head -c 32 /dev/urandom | base64kopia repository create s3 --bucket=my-backups --prefix=web1/ --endpoint=s3.us-west-004.backblazeb2.com--bucketnames the existing bucket.--prefix=web1/keeps this server's repository in a folder; the trailing slash makes it one.--endpointis the S3 host name only, with nohttps://and no path. It defaults tos3.amazonaws.com, so on AWS leave it out and add--regionwith the bucket's region, as any provider that needs a region requires. B2 endpoints follows3.<region>.backblazeb2.com.
Kopia asks for the password twice, prints the formats it chose (BLAKE2B-256-128 hashing, AES256-GCM-HMAC-SHA256 encryption, scrypt key derivation, DYNAMIC-4M-BUZHASH splitting) and connects. It then suggests a provider test, which writes and reads test objects:
kopia repository validate-providerIn Kopia's words: "There is absolutely no way to restore any of your backed up files from a repository if you forget your password!" kopia repository change-password works only while a machine is still connected. Keep the password off this server.
On Backblaze B2, an S3 delete only hides a file and hidden versions are kept by default, so the bucket keeps growing after Kopia deletes data unless a lifecycle rule removes them.
Or use an SFTP server or a local disk
The backup server needs only SFTP, not Kopia. Create a user there (kopia) with a key made for this job (SSH key authentication), connect once to record the host key (then type bye), and create the repository:
sftp -i /root/.ssh/kopia_ed25519 [email protected]kopia repository create sftp --host=backup.example.com --username=kopia --keyfile=/root/.ssh/kopia_ed25519 --known-hosts=/root/.ssh/known_hosts --path=/srv/kopia/web1--keyfile is the private key, --known-hosts the file holding the server's host key (required) and --path the folder there. For a mounted disk, use kopia repository create filesystem --path=/mnt/backup/kopia; Kopia's docs warn that network filesystems without full POSIX behavior can corrupt a repository.
Set retention, compression and ignore rules
kopia policy set --global --compression=zstd --keep-latest=7 --keep-daily=14 --keep-weekly=8 --keep-monthly=12 --keep-annual=2--globalsets the policy every snapshot inherits;kopia policy set /var/www ...overrides it for one path.--compression=zstdcompresses file data, stored uncompressed by default. Kopia's FAQ calls zstd one of the top choices. It applies to new uploads only.--keep-latest=7keeps the 7 newest snapshots, whatever their age.- The period rules keep the newest snapshot per day, week, month or year, but only within that many periods before the newest snapshot:
--keep-daily=14looks back 14 days, not 14 days that have backups. Unset rules keep the defaults: latest 10, hourly 48, daily 7, weekly 4, monthly 24, annual 3.
Kopia applies the policy right after each snapshot of a source. kopia policy show --global prints it; kopia snapshot expire --all reports what it would remove and deletes only with --delete.
# rules apply to this directory and everything below it
*.log
node_modules/
/*/wp-content/cache/Rules work like .gitignore: a pattern with no slash (apart from a trailing one) matches at any depth, a leading or inner / anchors it here, a trailing / means directories only, and ! re-includes. Policies take the same rules with --add-ignore. Directories holding a CACHEDIR.TAG file are skipped by default, and --one-file-system=true in a policy stops snapshots crossing into other mounts.
Take and list snapshots
kopia snapshot create /etc /var/www /rootEach path becomes its own source (root@web1:/var/www) with its own history and retention, and later runs upload only new chunks. Unreadable files are reported as Error when processing lines; Kopia saves the snapshot without them, then exits with code 1.
kopia snapshot listIt shows each snapshot's time, root ID (starting with k), size and the rules keeping it, such as daily-3. --all includes every user and host. Database files change while Kopia reads them, so dump them first (pg_dump, mysqldump) and snapshot the dumps:
#!/bin/sh
set -eu
install -d -m 700 /var/backups/db
runuser -u postgres -- pg_dump -Fc mydb > /var/backups/db/mydb.dump.tmp
mv /var/backups/db/mydb.dump.tmp /var/backups/db/mydb.dumpchmod 700 /usr/local/sbin/dump-databases.shset -e stops at a failed dump, so it never replaces the last good one; the timer below runs the script before each snapshot. Kopia's built-in alternative is a --before-snapshot-root-action policy, which needs --enable-actions at connect.
Restore files
kopia snapshot restore /var/www/site /srv/restore/siteGiven a path, Kopia restores it from the newest snapshot containing it, creating the target and overwriting files already there, so use an empty folder. --snapshot-time takes yesterday, 3d-ago or a date such as 2026-10-01 (the newest snapshot on or before that day):
kopia snapshot restore --snapshot-time=2026-10-01 /var/www/site/wp-config.php /srv/restore/wp-config.phpA target ending in .zip, .tar or .tar.gz becomes an archive. To browse, mount the repository; the command holds the terminal until Ctrl-C unmounts it, and all shows every source:
mkdir -p /mnt/kopiakopia mount all /mnt/kopiaOn a replacement server, install Kopia, recreate and load /etc/kopia/env, and connect with the same bucket flags (Kopia asks for the password). A bare path only matches the current user@host, so on a new hostname name the old source. Full steps: restoring a server from backup.
kopia repository connect s3 --bucket=my-backups --prefix=web1/ --endpoint=s3.us-west-004.backblazeb2.comkopia snapshot restore root@web1:/var/www /var/wwwVerify the repository
kopia snapshot verify --verify-files-percent=5Without the flag, verify walks every snapshot and checks that each file's chunks are indexed and stored, without downloading file data. --verify-files-percent=5 also downloads, decrypts and checks a random 5% of files, which costs egress where providers charge for it; Kopia's docs suggest 1% daily or 100% now and then. A verify proves the data is intact; a test restore proves you can use it.
Maintenance: who runs it, and when
Expired snapshots free no space until maintenance deletes data nothing uses: quick maintenance hourly by default, full maintenance (which removes unreferenced data and compacts packs) every 24 hours. Kopia runs whichever is due at the end of a successful command such as snapshot create, but only for the maintenance owner, the user@host that created the repository (here root@web1). Other machines never run it, and a backup that fails every night never triggers it.
kopia maintenance infoIt shows the owner and recent runs. kopia maintenance run --full runs it now; kopia maintenance set --owner=me makes this machine the owner, for example after a rebuild under a new hostname. Full maintenance waits out safety delays, so space can take hours to come back, and it refuses to run when the clock is more than 5 minutes off.
Kopia's cache may grow to 5,000 MB of data plus 5,000 MB of metadata. On a small VPS, kopia cache set --content-cache-size-mb=1000 --metadata-cache-size-mb=1000 lowers both soft limits.
Run it nightly with a systemd timer
[Unit]
Description=Kopia backup
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
EnvironmentFile=/etc/kopia/env
ExecStart=/usr/local/sbin/dump-databases.sh
ExecStart=/usr/bin/kopia snapshot create /etc /var/www /root /var/backups/db
ExecStart=/usr/bin/kopia snapshot verify --verify-files-percent=1[Unit]
Description=Nightly Kopia backup
[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=15m
Persistent=true
[Install]
WantedBy=timers.targetType=oneshotruns theExecStartlines in order and stops at the first failure, so a failed dump skips the snapshot and the unit shows as failed. Drop the dump line on servers without a database.EnvironmentFilereads the same/etc/kopia/env.Persistent=trueruns a missed backup at boot;RandomizedDelaySec=15mspreads servers that share a bucket.
sudo systemctl daemon-reloadsudo systemctl enable --now kopia-backup.timersudo systemctl start kopia-backup.serviceThe last command runs it once now; journalctl -u kopia-backup.service shows the result. With cron instead:
30 2 * * * root set -a; . /etc/kopia/env; set +a; /usr/bin/kopia snapshot create /etc /var/www /root >> /var/log/kopia-cron.log 2>&1Schedules in a policy (--snapshot-interval, --snapshot-time) are carried out only by a running kopia server or KopiaUI, not by the plain command line.
Stop the server from deleting its own backups
Root on web1 holds the password and a key that can delete. Kopia can lock everything it writes with S3 Object Lock, in a bucket created with Object Lock enabled:
kopia repository create s3 --bucket=my-locked-backups --prefix=web1/ --endpoint=s3.us-west-004.backblazeb2.com --retention-mode COMPLIANCE --retention-period 30dkopia maintenance set --extend-object-locks trueIn COMPLIANCE mode nobody, not even the account's root user, can delete an object before its 30-day --retention-period ends; Kopia's docs strongly recommend it over GOVERNANCE. --extend-object-locks true makes full maintenance renew the locks, which needs a retention period at least a day longer than the full maintenance interval. Locked data cannot be removed early even by you, so set a bucket quota and a lifecycle rule; see protecting backups from ransomware.
Common errors
| Error | Fix |
|---|---|
repository is not connected. See https://kopia.io/docs/repositories/ | KOPIA_CONFIG_PATH is not set in this shell or job. Load /etc/kopia/env. |
required flag(s) '--access-key', '--secret-access-key' not provided | The bucket keys are not in the environment. Load /etc/kopia/env with set -a. |
Endpoint url cannot have fully qualified paths. | --endpoint contains https:// or a path. Give the host name only. |
found existing data in storage location | That bucket and prefix already hold data. Use kopia repository connect s3, or a new --prefix. |
invalid repository password | Wrong password for this repository. |
Found 1 fatal error(s) while snapshotting | Files listed above it could not be read. Fix permissions, or accept gaps with kopia policy set <path> --ignore-file-errors=true. |
maintenance must be run by designated user: root@web1 | Run it on the owner, or take ownership with kopia maintenance set --owner=me. |
clock skew detected: local clock is out of sync with repository timestamp by more than allowed 5m0s | Fix the clock; timedatectl shows whether time sync is on. |
must provide either --known-hosts or --known-hosts-data | SFTP needs --known-hosts=/root/.ssh/known_hosts. |
Frequently asked questions
- Is Kopia free?
- Yes. Kopia is open source under the Apache 2.0 licence. You pay only for the storage you use.
- Does Kopia compress backups?
- Not file data by default. Turn it on in a policy, such as
kopia policy set --global --compression=zstd; it applies to later uploads. - What happens if I forget my Kopia repository password?
- Nothing can be restored. A machine still connected can set a new one with
kopia repository change-password; otherwise there is no way in. - Why does my Kopia bucket not shrink after snapshots expire?
- Space returns only when full maintenance runs on the maintenance owner, after safety delays.
kopia maintenance infoshows the owner and recent runs. - Do I need KopiaUI or kopia server on a Linux server?
- No. The
kopiacommand does everything; schedule it with a systemd timer or cron.
How this was checked
Commands, limits and prices were checked against these official pages, on October 4, 2026:
- Kopia documentation: Download & Installation
- Kopia documentation: Getting Started Guide
- Kopia documentation: Repositories
- Kopia command reference: repository create s3
- Kopia command reference: global flags
- Kopia command reference: policy set
- Kopia command reference: snapshot restore
- Kopia command reference: snapshot verify
- Kopia documentation: Maintenance
- Kopia documentation: Ignoring files (.kopiaignore)
- Kopia documentation: Compression
- Kopia documentation: Mounting
- Kopia documentation: Consistency (snapshot verify)
- Kopia documentation: Ransomware protection
- Kopia documentation: Actions
- Kopia documentation: FAQs
- Kopia v0.23.1 release (GitHub)
- Kopia source at v0.23.1: CLI flags, defaults, retention and error messages