tar and gzip Backup Strategies on Linux

I still use tar constantly, even in environments with snapshotting, object storage, and dedicated backup platforms. It is not because tar is magical. It is because it is everywhere, predictable, scriptable, and surprisingly flexible once you stop treating it as just “that old archive command.”

For small servers, config backups, pre-maintenance snapshots, application exports, and hand-built retention jobs, tar remains one of the most useful tools on a Linux box. The real challenge is not creating an archive. It is creating one you can actually restore, verify, rotate, and explain to the next person.

This guide is the practical way I use tar and compression tools on Linux: basic operations, compression choices, incremental backups, SSH streaming, exclusions, verification, splitting, encryption, and automation. I will also call out the mistakes I keep seeing, because backup commands that “ran successfully” are not the same thing as backups you can trust.

If you want a broader synchronization-focused approach, the rsync backup guide is the natural comparison. For database-specific work, the PostgreSQL backup and restore article matters because filesystem archives are not a substitute for application-consistent database dumps.

The three tar operations I use most

Most of my tar use falls into three buckets:

  • create an archive
  • list what is inside
  • extract all or part of it

Create an archive

tar -cf etc-backup.tar /etc

Flags:

  • -c create
  • -f archive file name follows

List archive contents

tar -tf etc-backup.tar

Extract an archive

tar -xf etc-backup.tar

That is the core of it. Most of the real-world complexity comes from compression, paths, consistency, and verification.

The leading slash warning is not a bug

If you archive an absolute path like /etc, tar often prints a message such as:

tar: Removing leading '/' from member names

That is expected behavior. GNU tar stores paths relative by default for safety so extraction does not blindly overwrite absolute locations unless you explicitly handle that.

I see admins treat that as an error and try to “fix” it. It is not an error. It is a sensible default.

My usual habit is to archive from / with -C when I want controlled relative paths:

sudo tar -cf etc-backup.tar -C / etc

That avoids ambiguity and restores more cleanly.

Compression choices: speed versus ratio matters

tar archives do not have to be compressed, but most of mine are.

The main options I use are:

  • gzip (-z): fast, widely available
  • bzip2 (-j): better ratio than gzip in some cases, slower
  • xz (-J): strong compression, much slower
  • zstd (--zstd): very good speed-to-ratio balance on modern systems

gzip: my default for portability

tar -czf etc-backup.tar.gz -C / etc

Gzip is still the safest common denominator. If I am moving archives between different systems or recovering on an unfamiliar rescue image, .tar.gz is usually the least surprising format.

bzip2: valid, but not my first choice anymore

tar -cjf etc-backup.tar.bz2 -C / etc

I rarely choose bzip2 for new workflows unless I know exactly why. It works, but it is often slower than I want and no longer my best compromise.

xz: when smaller size matters more than speed

tar -cJf etc-backup.tar.xz -C / etc

I use xz when storage or transfer size matters more than CPU time, especially for archives that will be created occasionally and restored rarely.

zstd: my modern favorite when available

tar --zstd -cf etc-backup.tar.zst -C / etc

This gives an excellent balance of speed and compression ratio on current Linux systems. The catch is compatibility: older recovery environments may not have zstd installed.

I check before standardizing on it:

tar --help | grep zstd
command -v zstd

Practical rule for choosing compression

  • use gzip for maximum compatibility
  • use zstd when both ends are modern and you care about speed
  • use xz for cold archives where smaller size matters most
  • use bzip2 only if there is a specific reason

Calculate backup size before creating the archive

I like to estimate size before launching a backup, especially on busy or space-constrained systems.

For a quick directory size:

du -sh /etc

For a deeper sorted view:

du -sh /var/www /etc /home | sort -h

If destination space is tight, I also check the target filesystem first:

df -h /backup

This is basic, but it prevents silly failures where an archive fills the same disk it is trying to protect.

Excluding what should not be backed up

The biggest mistake with naïve full-filesystem archives is including virtual or runtime filesystems that should not be restored from backup.

I almost always exclude at least:

  • /proc
  • /sys
  • /dev
  • /run
  • temporary caches depending on the use case

Example:

sudo tar -czf root-backup.tar.gz \
  --exclude=/proc \
  --exclude=/sys \
  --exclude=/dev \
  --exclude=/run \
  --exclude=/tmp \
  --exclude=/mnt \
  --exclude=/media \
  /

I do not use that exact command casually on a live system without thinking through consistency and destination path. It is an example of scope, not a universal one-liner to paste into production.

Use –exclude-from for repeatability

For recurring jobs, I prefer an exclusion file.

Example /etc/backup-excludes.txt:

/proc
/sys
/dev
/run
/tmp
/mnt
/media
/var/cache

Then:

sudo tar -czf root-backup.tar.gz --exclude-from=/etc/backup-excludes.txt /

That is easier to review and maintain than a massive inline command.

Incremental backups with tar

GNU tar incremental backups work through a snapshot file used with --listed-incremental.

This is useful, but it is one of the least understood parts of tar.

First full backup with a snapshot file

sudo tar -czf /backup/etc-full-2026-07-22.tar.gz \
  --listed-incremental=/backup/etc.snar \
  -C / etc

The snapshot file (.snar) stores metadata about the state of the files during that backup.

Next incremental backup

sudo tar -czf /backup/etc-incr-2026-07-23.tar.gz \
  --listed-incremental=/backup/etc.snar \
  -C / etc

That second run backs up changes since the previous snapshot state.

What actually changes between levels

This is the important bit:

  • the first run with a new snapshot file behaves like a full backup
  • later runs with the same snapshot file capture changes since the prior run using that snapshot
  • if you delete or replace the snapshot file, you effectively reset the chain

I keep snapshot files safe and named clearly because losing them breaks the incremental logic.

Trade-offs of tar incremental mode

Incremental tar can be useful for simple server-side workflows, but it is not as elegant as purpose-built backup tools.

I use it when:

  • the dataset is modest
  • the restore model is clearly documented
  • I want a built-in tool with minimal dependencies

I avoid it when:

  • many people will need to manage the chain
  • restores must be extremely simple under pressure
  • the workload changes constantly and a deduplicating backup system would be better

tar over SSH: fast and effective for point-in-time copies

This is one of my favorite admin patterns for quick remote backups.

Stream an archive to another host

sudo tar -czf - -C / etc | ssh backup@example.net 'cat > /srv/backups/etc-backup-2026-07-22.tar.gz'

That creates the archive locally and streams it over SSH without storing an intermediate file on the source host.

Pull a remote archive locally

ssh app@example.net 'sudo tar -czf - -C / var/www' > var-www-2026-07-22.tar.gz

This is great for emergency captures before maintenance.

tar plus SSH versus rsync

I use tar | ssh when I want:

  • one archive file
  • a quick point-in-time copy
  • simple transport over SSH
  • no need to preserve an efficient incremental mirror on the remote side

I use rsync when I want:

  • repeat synchronization
  • delta transfer efficiency
  • easy browsing of the backup directory without extraction

Neither replaces the other. They solve different problems. The rsync guide covers the synchronization side in detail.

Verify a backup without extracting it fully

If I had to name the most common backup mistake, it would be this: people create archives and never verify them.

List the contents

tar -tzf etc-backup.tar.gz | head

At minimum, make sure the archive is readable and contains what you expected.

Use labels and test them

You can label an archive during creation:

tar -czf etc-backup.tar.gz --label='etc-backup-2026-07-22' -C / etc

Then verify the label exists:

tar -tzf etc-backup.tar.gz --test-label

I do not rely on labels alone, but they are useful for scripted sanity checks.

Compare archive contents to the filesystem

For an uncompressed archive:

sudo tar -cf etc-backup.tar -C / etc
sudo tar -df etc-backup.tar -C / etc

For gzip-compressed archives, I usually test decompression first and then do spot checks or extract to a staging path if I need full verification.

gzip -t etc-backup.tar.gz

tar --compare is powerful, but remember it compares against the current filesystem state. If files changed after the backup, differences are expected.

Restore-test a sample file

My favorite real-world validation is a small restore test:

mkdir -p restore-test
sudo tar -xzf etc-backup.tar.gz -C restore-test etc/hosts etc/ssh/sshd_config

If that works and the files look right, I trust the archive much more than after a simple “backup completed” log line.

Restoring specific files from an archive

I rarely restore entire archives wholesale. More often I want one file or one directory.

List matching entries first:

tar -tzf etc-backup.tar.gz | grep '^etc/ssh/'

Extract only what you need:

tar -xzf etc-backup.tar.gz etc/ssh/sshd_config

Extract to a separate location to inspect first:

mkdir -p restore-inspect
sudo tar -xzf etc-backup.tar.gz -C restore-inspect etc/ssh/sshd_config

I prefer restoring into an inspection directory before overwriting live files unless time pressure is severe and the situation is already well understood.

Splitting large archives

When I need to move very large archives across systems or storage that has file-size limits, I split them.

Create the archive first, then split it:

tar -czf app-backup.tar.gz -C /srv app
split -b 2G app-backup.tar.gz app-backup.tar.gz.part-

To reassemble:

cat app-backup.tar.gz.part-* > app-backup.tar.gz

Then validate:

gzip -t app-backup.tar.gz

This is not elegant, but it is dependable and easy to understand.

Encrypt backups

Compression is not encryption. I still see those confused.

If an archive contains secrets, private keys, credentials, or customer data, encrypt it before storing or transferring it.

tar plus gpg

tar -czf - -C / etc | gpg --symmetric --cipher-algo AES256 -o etc-backup.tar.gz.gpg

Decrypt and extract:

gpg -d etc-backup.tar.gz.gpg | tar -xzf -

tar plus age

If age is installed and your team uses it:

tar -czf - -C / etc | age -r age1examplepublickeyreplacewithrealkey -o etc-backup.tar.gz.age

Decrypt:

age -d -i ~/.config/age/keys.txt etc-backup.tar.gz.age | tar -xzf -

I like age for its simplicity, but GPG is still more common in some environments. Use what the team can support reliably.

Automate with cron or systemd timers

For simple scheduled backups, cron still works.

Example root crontab entry:

15 2 * * * /usr/local/sbin/backup-etc.sh

A basic script:

#!/bin/bash
set -euo pipefail

STAMP=$(date +%F)
DEST=/backup
mkdir -p "$DEST"

tar -czf "$DEST/etc-$STAMP.tar.gz" --label="etc-$STAMP" -C / etc
find "$DEST" -maxdepth 1 -name 'etc-*.tar.gz' -mtime +14 -delete

Install it safely:

sudo install -m 0755 backup-etc.sh /usr/local/sbin/backup-etc.sh

I increasingly prefer systemd timers for better logging and dependency control. The systemd timers guide and the older cron jobs guide cover both approaches well.

Retention policies: age and count both matter

Deleting old backups is easy to postpone and easy to forget.

Delete by age

find /backup -maxdepth 1 -name 'etc-*.tar.gz' -mtime +14 -delete

Keep only the newest N files

ls -1t /backup/etc-*.tar.gz | tail -n +8 | xargs -r rm -f

I use age-based retention when the schedule is stable. I use count-based retention when backups may run at uneven intervals.

For critical systems, I do not rely on a single local retention path anyway. Backups should exist in more than one failure domain.

Quiesce applications when consistency matters

A filesystem archive is only as good as the application state it captured.

For mostly static data like /etc, tar is straightforward. For busy application trees, I think about whether files may change while the archive is running. Log files rotating during backup are usually not a disaster. Databases and constantly written application data are a different story.

Typical options I use before a tar backup of active application data:

  • stop the application briefly
  • put it into maintenance mode
  • take an application-aware dump first
  • snapshot the filesystem or volume, then archive the snapshot

For example, stopping a systemd service before archiving its data:

sudo systemctl stop myapp
sudo tar -czf /backup/myapp-data-$(date +%F).tar.gz -C /var/lib myapp
sudo systemctl start myapp

I do not do this blindly on production systems, but I absolutely think through consistency before treating a tarball as a real backup.

Restore drills matter more than clever backup commands

A backup strategy is not proven when the archive exists. It is proven when you can restore under time pressure with boring, repeatable steps.

I like to document at least one quick restore drill per backup job:

  1. list the archive
  2. extract a sample file to a staging path
  3. verify permissions and content
  4. document full restore steps nearby

That discipline matters more than whether you used gzip or zstd. The teams that skip restore drills are usually the ones surprised by ownership issues, missing keys, or broken incremental chains later.

Common mistakes I keep seeing

Forgetting to verify

The archive exists, but nobody checked whether it can be listed, tested, or restored.

Backing up live databases as raw files only

For PostgreSQL, MariaDB, and similar systems, I prefer logical or engine-aware backup steps first, then archive those dumps. The PostgreSQL backup guide explains why raw file copies alone can be a bad plan.

Including virtual filesystems

Archiving /proc, /sys, /dev, and /run as if they were normal persistent data creates messy restores and unnecessary noise.

Writing the backup onto the same full disk

Always check free space first.

Not documenting restore steps

The backup command looked clever when written. Six months later, nobody remembers which snapshot file, decryption key, or split order is required.

Final thoughts

tar is old, but it is far from obsolete. It is still one of the most useful backup building blocks on Linux when you use it deliberately. The key is not memorizing flags. The key is understanding the restore path, excluding the right things, choosing compression based on reality instead of habit, and verifying what you create.

My practical defaults are straightforward. Use -C to keep paths clean. Prefer gzip for broad compatibility and zstd when I control both ends. Use --exclude-from for repeatable jobs. Stream over SSH when I want a quick remote archive. Encrypt anything sensitive. Test archives with listing, decompression checks, and small restore drills. And automate retention instead of letting backup directories turn into archaeological sites.

Do that, and tar stays what it has always been at its best: simple, flexible, and dependable enough to save your day when something breaks.

Scroll to Top