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.
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.
| What | Where | How |
|---|---|---|
| Application files | /var/www, /srv, /opt, /home | tar or rsync |
| Databases | PostgreSQL, MySQL, MongoDB, Redis, SQLite | A dump, never the live data directory |
| Docker volumes | /var/lib/docker/volumes | Dump databases; copy other volumes with the container stopped |
| System configuration | /etc | All of it |
| Per-user crontabs | /var/spool/cron/crontabs | tar |
| Root's home, local programs | /root, /usr/local | tar |
| Installed packages | dpkg and apt (dnf on RHEL) | A text file |
| Enabled services, timers, firewall | systemd, ufw, nftables | Text 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:
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:
- PostgreSQL with pg_dump, plus its roles
- MySQL and MariaDB with mysqldump
- MongoDB with mongodump
- Redis and SQLite
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 intoarchive/; tar andrsync -akeep 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.rulesanduser6.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/keyringsis also used, and that is outside/etc. Check thesigned-bypaths 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.
sudo ls -l /var/spool/cron/crontabssystemd timers do the same job as cron. List them, so you know what should be running after a restore:
systemctl list-timers --allAnd record which services start at boot, as a text file next to the backup:
systemctl list-unit-files --state=enabled > enabled-units.txtThe installed package list
You can reinstall software, but only if you know what was installed. Save two lists:
dpkg --get-selections > packages-selections.txtapt-mark showmanual > packages-manual.txtdpkg --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:
xargs -a packages-manual.txt apt-get install -ssudo xargs -a packages-manual.txt apt-get install -yxargs -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:
sudo ufw status verbose > firewall-ufw.txtsudo nft list ruleset > firewall-nftables.txtsudo iptables-save > firewall-iptables.txtufw status verboseshows 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 rulesetprints every nftables rule in a formnft -fcan load again.iptables-savedoes the same for iptables;iptables-restoreloads it. On Ubuntu 24.04,iptables --versionreports(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:
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $3, $6}' /etc/passwdRecreate 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 out | Why |
|---|---|
/proc, /sys, /dev | Virtual filesystems the kernel and udev create at boot. They are not files on disk. |
/run, /tmp | Temporary, cleared or recreated at boot. |
/var/cache, ~/.cache | Rebuilt 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 output | Recreated 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 directories | Back up dumps instead (above). |
The OS: /usr (except /usr/local), /boot, /lib | The 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:
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
--excludeis matched against those relative names. A pattern without a slash, likenode_modules, matches at any depth;home/*/.cachematches 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:
tar -tzf /var/backups/server-2026-10-03.tar.gz | grep node_modulesExit 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:
sudo mkdir -p /root/restore && sudo tar -xzf /var/backups/server-2026-10-03.tar.gz -C /root/restoreThen 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 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: