VPS Snaps

What to back up on a Linux server: a checklist

Back up your application data (/var/www, /srv, /opt, /home), database dumps instead of database files, Docker volumes, all of /etc, the crontabs in /var/spool/cron, /root, /usr/local, and text lists of your installed packages, enabled services and firewall rules. Leave out /proc, /sys, /dev, /run, /tmp, caches, swap and anything a build step recreates. A one-page checklist to print is at the end.

10 min readUpdated Tested on Ubuntu 24.04 LTS, GNU tar 1.35

The short version

A server holds data you cannot recreate (uploads, databases, user files) and configuration you could recreate from memory, slowly (web server sites, cron jobs, firewall rules, installed packages). Back up both: the second is the difference between a two-hour rebuild and a two-day one.

WhatWhereHow
Application files/var/www, /srv, /opt, /hometar or rsync
DatabasesPostgreSQL, MySQL, MongoDB, Redis, SQLiteA dump, never the live data directory
Docker volumes/var/lib/docker/volumesDump databases; copy other volumes with the container stopped
System configuration/etcAll of it
Per-user crontabs/var/spool/cron/crontabstar
Root's home, local programs/root, /usr/localtar
Installed packagesdpkg and apt (dnf on RHEL)A text file
Enabled services, timers, firewallsystemd, ufw, nftablesText files, plus /etc

Application data

Web roots usually live in /var/www, deployed services in /srv or /opt, and many apps in a user's home directory. Uploads, generated files and .env files exist nowhere else. Your code is probably in Git too, but back it up anyway, so a restore needs nothing else.

Look in /var/lib too. Services keep state there, and only some of it is a database. This shows what is large:

Terminal
sudo du -sh /var/lib/* | sort -h

-s prints one total per directory and -h makes sizes readable; sort -h understands those sizes, so the biggest come last. For the copying itself, see tar and rsync.

Databases: dumps, not files

Copying /var/lib/postgresql or /var/lib/mysql while the database runs gives you files that may not start. Dump each database with its own tool, then back up the dump:

Once the dumps exist, leave the data directories out of the file backup: they are large, change every second, and a torn copy is worse than none.

Docker keeps volumes under /var/lib/docker/volumes on a standard install. The same rule applies: a database in a container gets dumped, not copied. See Docker volume backups, and keep your compose files and their .env files wherever they live.

/etc: all of it

/etc is a few megabytes on a typical server and holds almost every setting you ever changed: web server sites, PHP and database config, /etc/hosts, sysctl tweaks, logrotate rules, apt sources. Back up the whole directory instead of guessing which files matter. Inside it:

  • Users and groups: /etc/passwd, /etc/group, /etc/shadow, /etc/gshadow.
  • Your systemd units, and the links that enable them: /etc/systemd/system.
  • System cron jobs: /etc/crontab, /etc/cron.d, and /etc/cron.hourly, cron.daily, cron.weekly, cron.monthly.
  • Certbot's certificates and private keys: /etc/letsencrypt. live/ holds symbolic links into archive/; tar and rsync -a keep them as links.
  • SSH host keys: /etc/ssh/ssh_host_*. Put them back on a rebuilt server and clients see no changed-host-key warning.
  • Firewall files: ufw keeps your rules in /etc/ufw (user.rules and user6.rules), and the nftables service loads /etc/nftables.conf.
  • Third-party apt repositories in /etc/apt/sources.list.d. Their keys belong in /etc/apt/keyrings, but /usr/share/keyrings is also used, and that is outside /etc. Check the signed-by paths in your sources.

/etc holds secrets: password hashes in /etc/shadow, TLS private keys, database passwords in app config. Encrypt the archive before it leaves the server, and guard it like the server itself.

Never extract a backed-up /etc over a new server's /etc. /etc/fstab, /etc/hostname, the network configuration and /etc/machine-id describe the old machine. Extract into an empty directory and copy the files you need from there.

To see what changed in /etc and when, not just keep a copy, put it under version control: see how to track /etc changes with etckeeper.

Crontabs, timers and services

Per-user crontabs, the ones crontab -e edits, are not in /etc. On Debian and Ubuntu they are files in /var/spool/cron/crontabs, one per user, in a directory only root can list; on RHEL-family systems they are in /var/spool/cron.

Terminal
sudo ls -l /var/spool/cron/crontabs

systemd timers do the same job as cron. List them, so you know what should be running after a restore:

Terminal
systemctl list-timers --all

And record which services start at boot, as a text file next to the backup:

Terminal
systemctl list-unit-files --state=enabled > enabled-units.txt

The installed package list

You can reinstall software, but only if you know what was installed. Save two lists:

Terminal
dpkg --get-selections > packages-selections.txt
Terminal
apt-mark showmanual > packages-manual.txt

dpkg --get-selections lists every installed package with its state (install, hold and so on). apt-mark showmanual lists only the packages someone asked for, without the dependencies they pulled in. That second list is the one to replay on a new server, even one on a newer release.

On the new server, add your third-party repositories first, then simulate the install with -s, which changes nothing, and run it for real:

Terminal
xargs -a packages-manual.txt apt-get install -s
Terminal
sudo xargs -a packages-manual.txt apt-get install -y

xargs -a reads the names from the file and passes them to apt-get. If a name does not exist in the new release, apt-get stops with E: Unable to locate package and the name; delete that line and run it again.

For an exact clone on the same release, the dpkg manual uses the full selections instead: apt-cache dumpavail | sudo dpkg --merge-avail, then sudo dpkg --clear-selections, sudo dpkg --set-selections < packages-selections.txt and sudo apt-get dselect-upgrade. --clear-selections marks every package not in the list for removal, so use it only on a fresh install of the same release.

On Rocky Linux, AlmaLinux, RHEL and Fedora, dnf repoquery --userinstalled gives the equivalent of apt-mark showmanual. It works in dnf 4 and dnf 5; dnf 5 dropped the older dnf history userinstalled.

Firewall rules

Save the live rules as text, whichever tool manages them:

Terminal
sudo ufw status verbose > firewall-ufw.txt
Terminal
sudo nft list ruleset > firewall-nftables.txt
Terminal
sudo iptables-save > firewall-iptables.txt
  • ufw status verbose shows ufw's rules and default policies. It does not show rules added by hand to the files in /etc/ufw, so those files are the real backup and this is for reading.
  • nft list ruleset prints every nftables rule in a form nft -f can load again.
  • iptables-save does the same for iptables; iptables-restore loads it. On Ubuntu 24.04, iptables --version reports (nf_tables), so these rules also appear in the nft output.

Users, groups and SSH keys

The account files are in /etc, but on a rebuild the numbers are what matter: files belong to a UID, not a name. List the accounts you created, from UID 1000 up to just below nobody (65534), with their home directories:

Terminal
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $3, $6}' /etc/passwd

Recreate them with the same numbers (useradd -u) before restoring files. SSH access lives in each user's ~/.ssh/authorized_keys, including /root/.ssh/authorized_keys, which is one reason /root belongs in the backup. It also tends to hold admin scripts and credential files such as .pgpass or .my.cnf.

On the new server, set up key login and turn off passwords: see SSH key authentication.

What not to back up

Leave outWhy
/proc, /sys, /devVirtual filesystems the kernel and udev create at boot. They are not files on disk.
/run, /tmpTemporary, cleared or recreated at boot.
/var/cache, ~/.cacheRebuilt on demand. apt keeps its downloaded packages in /var/cache, for example.
Swap (/swapfile or a swap partition)Overflow memory, meaningless after a reboot.
node_modules, build outputRecreated by npm ci or your build from the lock file. Keep the lock files. Skip vendor too only if your deploy runs composer install.
Live database directoriesBack up dumps instead (above).
The OS: /usr (except /usr/local), /boot, /libThe package list reinstalls it.

One tar command for the lot

This writes one compressed archive of everything above except the databases, which you dump separately. Run it as root so it can read every file and record owners:

Terminal
sudo tar --create --gzip --file=/var/backups/server-$(date +%F).tar.gz \
  --exclude='node_modules' \
  --exclude='home/*/.cache' \
  --exclude='var/www/*/cache' \
  -C / etc root home srv opt usr/local var/www var/spool/cron
  • --create --gzip --file= are the long forms of -czf.
  • -C / changes to the root directory first, so names in the archive are relative (etc/ssh/...) and you can extract them anywhere. The paths that follow have no leading slash.
  • Each --exclude is matched against those relative names. A pattern without a slash, like node_modules, matches at any depth; home/*/.cache matches every user's cache.
  • Excludes go before the paths. GNU tar ignores one written after them.
  • The archive is written to /var/backups, which is not in the list, so tar never reads its own output.

Our test run against a copy of this layout kept vendor/ and dropped node_modules, the cache directories and ~/.cache. Check yours; this prints nothing if the exclude worked:

Terminal
tar -tzf /var/backups/server-2026-10-03.tar.gz | grep node_modules

Exit status 1 means a file changed while tar read it (it prints file changed as we read it), typically a log; the archive is written, but that one file may be inconsistent. Exit status 2 is a real failure. If you use ACLs (setfacl), add --acls both when creating and when extracting.

To restore, extract into an empty directory and copy what you need from there:

Terminal
sudo mkdir -p /root/restore && sudo tar -xzf /var/backups/server-2026-10-03.tar.gz -C /root/restore

Then get a copy off the server: a backup on the same disk dies with it. rclone sends it to any object storage, the 3-2-1 rule says how many copies to keep, and cron runs it every night.

The printable checklist

server-backup-checklist.txt
SERVER BACKUP CHECKLIST          host: ______________   date: __________

DATA
[ ] /var/www  /srv  /opt          web roots, deployed apps, uploads
[ ] /home                         users' files and apps
[ ] /root                         root's scripts, ~/.ssh, credential files
[ ] /usr/local                    your own scripts and binaries
[ ] App config and .env files     wherever the app keeps them
[ ] /var/lib/<service>            state kept by services that are not databases

DATABASES (dumps, never the live data directory)
[ ] PostgreSQL     pg_dump per database + pg_dumpall --globals-only
[ ] MySQL/MariaDB  mysqldump --single-transaction
[ ] MongoDB        mongodump
[ ] Redis          copy of dump.rdb or the AOF directory
[ ] SQLite         sqlite3 .backup
[ ] Docker         volumes, compose files and their .env files

SYSTEM
[ ] /etc                          all of it; holds secrets, so encrypt
[ ] /var/spool/cron/crontabs      per-user crontabs
[ ] /etc/systemd/system           your units and timers
[ ] /etc/letsencrypt              certificates and keys (keep symlinks)
[ ] /etc/ssh/ssh_host_*           host keys
[ ] /usr/share/keyrings           only the repository keys your sources use

LISTS (text files kept with the backup)
[ ] dpkg --get-selections                       > packages-selections.txt
[ ] apt-mark showmanual                         > packages-manual.txt
[ ] systemctl list-unit-files --state=enabled   > enabled-units.txt
[ ] ufw status verbose / nft list ruleset / iptables-save
[ ] Accounts with UID 1000 and up, with their numbers

LEAVE OUT
    /proc /sys /dev /run /tmp  /var/cache ~/.cache  swap
    node_modules and build output, live database directories

CHECK
[ ] A copy is off the server, and encrypted
[ ] Last test restore: __________

The last line matters most: test a restore on a spare server and write the date in.

Frequently asked questions

Should I back up the whole Linux filesystem?
You can, and a cloud snapshot does exactly that. A file backup of the paths in this checklist is smaller, restores onto any server, and skips what the package manager reinstalls. Many people keep both.
Do I need to back up /etc?
Yes, all of it. It is small and holds nearly every setting you changed. Encrypt the archive, because /etc contains password hashes and private keys.
Can I back up /var/lib/mysql or /var/lib/postgresql directly?
Not while the database runs. The copy can be inconsistent and fail to start. Dump the database and back up the dump.
How do I back up the list of installed packages?
On Debian and Ubuntu, apt-mark showmanual > packages-manual.txt. On RHEL-family systems, dnf repoquery --userinstalled.
What should I exclude from a Linux server backup?
/proc, /sys, /dev, /run, /tmp, caches, swap, node_modules and other build output, and live database data directories.

How this was checked

The commands were run on Ubuntu 24.04 LTS, GNU tar 1.35 on October 3, 2026. Any that need something this test server does not have, such as a second server, a cloud account or another database engine, were checked against the official pages below instead.

Sources, on October 3, 2026: