Everybody knows how to create a .tar.gz. The RHCSA and LFCS level demands more. Incremental backups after midnight, excluding /proc and caches, sending straight to a backup server over SSH without an intermediate file, restoring a single file from a 50 GB archive: these are the moves this guide covers.
This topic is a direct RHCSA objective: "Archive, compress, unpack, and uncompress files using tar, gzip, and bzip2". The current LFCS curriculum does not name tar, but backup and restore come back in its maintenance tasks.
What you will learn
- Create a full backup then incremental backups with
--listed-incremental - Exclude directories or patterns with
--excludeand--exclude-from - Choose between gzip, bzip2, xz and zstd for the speed/ratio trade-off
- Extract a precise file from a large archive without decompressing everything to disk
- Send an archive over SSH in a pipeline without a temporary file
- Check the integrity of an archive before restoring
In which context?
The table below links a real operations situation to the tar option that
solves it. Read it from left to right: the right column gives the keyword to
remember, not the full command. Each technique is detailed further down with its
check command, because a backup you have never read back is not a backup.
| Situation | tar technique |
|---|---|
| Daily backup of a config directory | --listed-incremental |
Back up /home without caches and .git | --exclude |
| Archive a large directory on a server short of space | xz or zstd |
Restore a single /etc/nginx/nginx.conf file from an archive | selective extraction |
| Send a backup to a remote server without mounting NFS | tar + ssh pipeline |
| Check that a backup archive is readable | tar -tvf or --compare |
Quick reminder: the fundamental options
A single syntax rule avoids most beginner mistakes: the -f option must be the
last letter of the group, because it consumes the next argument as the archive
name. Typing tar -fcz archive.tar.gz makes it read cz as a file name, and
tar answers You must specify one of the '-Acdtrux' … options. The other
letters fall into two families: the action mode (-c, -x, -t), only one
of which is allowed per command, and the compression codec (-z, -j,
-J, --zstd).
# Create a gzip-compressed archivetar -czf archive.tar.gz /directory/
# List the contenttar -tvf archive.tar.gz
# Extracttar -xzf archive.tar.gz -C /destination/Option cheat sheet: -c create, -x extract, -t list, -v verbose mode, -f archive file, then the codecs -z gzip, -j bzip2, -J xz and --zstd zstd.
Incremental backups with --listed-incremental
A full backup archives everything. An incremental backup only archives the files modified or created since the last backup. It is the building block of a backup plan, not the plan itself: what tar will never do for you is covered further down, in the section that sets its limits.
tar handles this natively through a snapshot file (.snar) that records the state of the directory at backup time.
Step 1, full backup (level 0)
The first call has two simultaneous effects: it produces the full archive and
it creates the snapshot file. Put this .snar outside the backed-up
directory, otherwise it ends up in its own archive and changes at every run.
The /var/backup/ directory must exist beforehand: tar does not create the
snapshot's directory tree and stops on Cannot open.
tar --listed-incremental=/var/backup/home.snar \ -czf /var/backup/home-full-$(date +%F).tar.gz \ /home/bob/--listed-incremental=/var/backup/home.snar: creates or updates the snapshot file- On the first call, the
.snardoes not exist →tararchives everything
Step 2, incremental backup (level 1+)
tar --listed-incremental=/var/backup/home.snar \ -czf /var/backup/home-incr-$(date +%F-%H%M).tar.gz \ /home/bob/The second time, tar reads the snapshot and only includes the files modified since the last run.
Check
# Count the files in each backuptar -tvf /var/backup/home-full-2026-04-09.tar.gz | wc -ltar -tvf /var/backup/home-incr-2026-04-09-0200.tar.gz | wc -lThe incremental backup must contain far fewer files than the full backup.
Do not be surprised to find every directory of the tree in it, even those
that did not change: tar writes them again at each level so it can replay the
deletions at restore time. Count the lines starting with -rw, not those
starting with drwx.
Restoring an incremental chain
Restoring happens in order: full backup, then each incremental in chronological order.
# 1. Extract the full backuptar --listed-incremental=/dev/null \ -xzf /var/backup/home-full-2026-04-09.tar.gz \ -C /restore/
# 2. Apply the next day's incrementaltar --listed-incremental=/dev/null \ -xzf /var/backup/home-incr-2026-04-10-0200.tar.gz \ -C /restore/--listed-incremental=/dev/null at extraction tells tar to apply the deletions recorded in each incremental archive (files removed between two backups). Measured with GNU tar 1.35: a file deleted between the full and the incremental backup disappears from the restore with the option, and comes back without it.
Exclude files and directories
Backing up /home including browser caches, .git and node_modules directories is a waste. tar offers two exclusion mechanisms.
--exclude, exclude a pattern
A misplaced --exclude is useless: the option must appear before the path to
archive. Placed after, GNU tar 1.35 warns (--exclude '.cache' has no effect),
archives the files to exclude anyway and exits with code 2. Also
remember to quote each pattern: under zsh, an unquoted pattern with no
match stops the command (no matches found), and under bash it would be
expanded if a file of the current directory matched it.
tar -czf /var/backup/home-clean.tar.gz \ --exclude='.cache' \ --exclude='.local/share/Trash' \ --exclude='**/node_modules' \ --exclude='**/.git' \ /home/bob/- A pattern without
/, like.cache, excludes that name at any depth - You can combine several
--exclude **(globbing) works with GNU tar
--exclude-from, exclusion list file
Beyond three or four patterns, the command line becomes unreadable and above all
impossible to version. An exclusion file can be reviewed and shared between
several backup scripts. The format is simple: one pattern per line, without
quotes (tar reads the file, not the shell). Comments are not recognised: a
line starting with # is an ordinary pattern, harmless as long as no file
carries that name (measured). Better document the list elsewhere, in the script
that uses it.
Content of /etc/backup/exclusions.txt:
.cache.local/share/Trash**/node_modules**/.git**/tmp*.log*.pid/proc/sys/dev/runtar -czf /var/backup/system.tar.gz \ --exclude-from=/etc/backup/exclusions.txt \ /Choose between gzip, bzip2, xz and zstd
Choosing a codec is a trade-off between the CPU time spent compressing and the
disk space saved. Two columns of the table really matter: the speed, which
decides whether the backup fits in its nightly window, and the ratio, which
decides how much room it takes on the backup server. Compression is not built
into tar: the option delegates to an external program (gzip, bzip2,
xz, zstd). The codec must therefore be installed on the machine that
reads back the archive, the restore machine, not only on the one that creates it. A .tar.zst
backup that cannot be read on the restore server is a classic incident.
| Codec | tar option | Extension | Ratio | Speed | When to use it |
|---|---|---|---|---|---|
| gzip | -z | .tar.gz | Medium | Very fast | Default, logs, configs, frequent transfers |
| bzip2 | -j | .tar.bz2 | Good | Slow | Long-term archiving, space first |
| xz | -J | .tar.xz | Excellent | Very slow | Distributions, sources, minimal size |
| zstd | --zstd | .tar.zst | Very good | Fast | Modern backups, best speed/ratio balance |
Concrete examples
Notice the extension used in each command: it has no technical effect
(tar detects the format by itself when reading), but it documents the codec for
the administrator who finds the file six months later. Note also that --zstd is
a long option and does not group with the other letters: you write
tar --zstd -cf, never tar -c--zstdf.
# gzip: everyday use (default)tar -czf logs-$(date +%F).tar.gz /var/log/nginx/
# bzip2: long-term archivingtar -cjf archives-2025.tar.bz2 /var/archive/2025/
# xz: source distributiontar -cJf my-project-1.0.tar.xz ./my-project/
# zstd: fast backup with a good ratiotar --zstd -cf backup-$(date +%F).tar.zst /home/Check the actual compression ratio
The ratios in the table are orders of magnitude: the real gain depends entirely on your data. Text logs compress by more than 90%, JPEG images or already compressed archives gain nothing and cost CPU time for nothing. Measure on your own dataset before freezing a codec in a production script, by comparing the size of the archives with that of the source.
ls -lh /var/backup/home-full-2026-04-09.tar.{gz,zst,bz2,xz}du -sh /home/bob/What metadata does tar keep, and what does it silently lose?
By default, GNU tar keeps neither ACLs, nor extended attributes, nor SELinux contexts, and the option that turns them on must be passed on both sides, at creation and at extraction. It is the trap that turns a successful restore into an incident: the files are there, the fine-grained rights are gone, and nobody notices until the service refuses to start.
What are a file's metadata?
A file's metadata is everything the system keeps about it besides its
content. You already know part of it: the owner and group (UID and
GID), the rwx permissions and the dates, which ls -l and stat
show. Three more, quieter ones are those tar forgets by default:
- POSIX ACLs: permissions granted to one specific user or group, on top of
the owner, group, others trio. A
+at the end of the permissions inls -lshows they exist, andgetfacl filedisplays them. - Extended attributes:
name=valuepairs the file system attaches to the file, outside its content, for exampleuser.origin=prod.getfattr -d filedisplays them. - SELinux context: the security label, such as
httpd_sys_content_t, that SELinux compares with the process's own label before letting it read the file, on RHEL, AlmaLinux or Rocky.ls -Zshows it, and prints a plain?on a system without SELinux, such as Ubuntu by default.
You do not need to know how to set them to follow this section: ACLs and SELinux are taught at level 3, in Refine with ACLs and SELinux. Here, only one thing matters: if your files carry them, a backup that ignores them loses them on restore.
Measurements taken on 20 September 2026 with GNU tar 1.35 on AlmaLinux
10.2, dedicated ext4 file system mounted with acl and user_xattr:
| Metadata | By default | Option needed | What the measurement gives |
|---|---|---|---|
| Permissions and dates | kept | none | nothing to do |
| Hard links | kept | none | same inode after restore |
| POSIX ACLs | lost | --acls on both ends | u:nobody:rwx disappears |
| Extended attributes | lost | --xattrs on both ends | user.origine disappears |
| SELinux contexts | lost | --selinux on both ends | httpd_sys_content_t falls back to the directory default |
| Owner (UID/GID) | depends on who extracts | extraction by root | 1500:1600 becomes the extractor's UID |
| Sparse files | not detected | --sparse | 513 MB versus 10 KB |
Why the option is passed twice
The counter-intuitive point fits in one sentence: an archive created with
--acls but extracted without it still loses its ACLs. The archive does
hold the information, tar simply refuses to apply it again unless asked.
The measurement leaves no doubt:
# Archive created WITH the option, extracted WITHOUT: the ACL is losttar --acls -cf backup.tar folder/tar -xf backup.tar -C /restore/getfacl -c /restore/folder/file | grep '^user:nobody' # returns nothing
# Option on both ends: the ACL comes backtar --acls -xf backup.tar -C /restore/getfacl -c /restore/folder/file | grep '^user:nobody' # user:nobody:rwxThe same rule applies to --xattrs and --selinux. So write these
options in your backup scripts as well as in your restore procedures, which
are rarely the same files and drift apart over time.
On RHEL, a lost SELinux context breaks the service
The SELinux case deserves its own paragraph because the failure is
deferred and silent. Measured on the same machine, in enforcing mode,
with a file deliberately labelled httpd_sys_content_t:
| Archive created | Extraction | Context of the restored file |
|---|---|---|
--selinux | without the option | var_t (the directory default) |
--selinux | --selinux | httpd_sys_content_t kept |
| without the option | --selinux | var_t: the information is not in the archive |
The third line is the trap: passing --selinux at restore time catches
up nothing if the archive did not carry it. The files are restored, the
permissions are right, and the web server answers 403 because it is not
allowed to read a file labelled var_t.
The fallback exists and is worth knowing: restorecon -Rv applies again
the contexts expected by the policy, which is enough when the restored tree
is at its usual location. It does not replace --selinux for files whose
context was set by hand.
The owner depends on who extracts, not on the archive
A file owned by 1500:1600 in the archive only gets its owner back if the
extraction is done by root. Extracted by a regular account, it takes the
UID of whoever extracts, silently, because an unprivileged user cannot
give a file away to someone else.
source : uid=1500 gid=1600extracted by an account : uid=1001 gid=1001 <- replacedextracted by root : uid=1500 gid=1600 <- keptRestoring a system tree without being root therefore produces a tree whose files all belong to the operator. It is irreparable without the list of original owners, which only the archive still carries.
Sparse files, the most spectacular gain
A sparse file is a file whose empty areas consume no disk block:
virtual machine images, swap files, preallocated databases. Without
--sparse, tar dutifully writes every zero into the archive.
truncate -s 512M disk.img # 512 MB apparent, 0 byte used
tar -cf without.tar disk.img # 513 MB archivetar --sparse -cf with.tar disk.img # 10 KB archive511 MiB saved on a single empty file. On a server holding a few virtual
machine images, forgetting --sparse makes the difference between a backup that
fits on its volume and one that fills it.
Selective extraction: a single file from a large archive
No need to decompress 50 GB to disk to get back a single configuration file.
List the content to find the exact path
Selective extraction almost always fails for the same reason: the requested path
does not match the one stored in the archive. So start by listing, then
copy-paste the displayed path. On a large archive, this reading still
decompresses the whole stream; count several minutes for several tens of
gigabytes, and filter with grep rather than scrolling the output.
tar -tvf /var/backup/home-full-2026-04-09.tar.gz | grep nginx.conf-rw-r--r-- bob/bob 2847 2026-04-08 22:13 home/bob/.config/nginx/nginx.confExtract a precise file
tar -xzf /var/backup/home-full-2026-04-09.tar.gz \ -C /tmp/restore/ \ home/bob/.config/nginx/nginx.confThe path must match exactly what tar -tvf displays (without a leading / in most cases).
Extract a whole directory
The same principle applies to a directory: pass its path without the leading
/ and tar restores the whole subtree, permissions and dates included.
The destination directory passed to -C must exist: tar does not create it
and stops on Cannot open. The full path tree is recreated under this
destination.
tar -xzf /var/backup/home-full-2026-04-09.tar.gz \ -C /tmp/restore/ \ home/bob/.config/Extract to a different directory
tar -xzf /var/backup/home-full-2026-04-09.tar.gz \ --strip-components=2 \ -C /tmp/restore/ \ home/bob/.config/--strip-components=2 removes the first 2 levels of the path in the archive (home/bob/) to extract directly into /tmp/restore/.
Transfer over SSH, without an intermediate file
One of tar's most elegant moves: create an archive on standard output, pass it straight into SSH, and extract it on the remote server, all in a single pipeline.
Send to a remote server
The - after -czf replaces the file name with standard output: tar
writes nothing to the local disk, the stream goes straight through the pipe
to ssh. Watch where $(date +%F) is evaluated: in double quotes, the
substitution happens on the local machine before sending, which is usually
the intended behaviour to date the archive by the source's clock.
# Archive locally, store on the remote servertar -czf - /home/bob/projects/ | ssh bob@backup-server "cat > /backup/projects-$(date +%F).tar.gz"Copy a tree while preserving permissions
Both tar run as root: the first to read the protected files of /etc,
such as /etc/shadow, the second because only root can give the extracted files
their owner back. The remote root account must therefore accept a
key-based connection.
# Archive and extract immediately on the remote serversudo tar -czf - /etc/ | ssh root@backup-server "tar -xzf - -C /backup/etc/"This pipeline preserves permissions, UIDs, GIDs, symbolic links
and dates, which scp -r does not always do correctly. With a regular
account at the other end, every file would belong to that account, as the section
on owners shows.
Fetch an archive from a remote server
The reverse direction works too: tar runs remotely and writes to its
standard output, which ssh brings back and a local redirection saves.
Useful when the source server lacks disk space to host the intermediate archive.
Always check the received archive with tar -tvf before deleting anything at the
source: a network cut produces a truncated file that looks normal until you
try to read it.
ssh bob@source-server "tar -czf - /var/www/html/" > /backup/html-$(date +%F).tar.gzCheck the integrity of an archive
An unusable backup is worse than no backup. Always check before finding yourself
in an emergency. Keep the limit of the format in mind: a tar archive carries
no global checksum. A full read detects a truncation in the middle of a
file, but an archive cut exactly on a block boundary can list without error.
For a real guarantee, compute a sha256sum of the archive and store it on
another medium.
List the content (read test)
tar -tvf /var/backup/home-full-2026-04-09.tar.gz > /dev/null && echo "Archive OK"If tar returns a non-zero exit code, the archive is corrupted.
Get the right exit code in a script
The trap hides in the pipe: $? returns the code of the last element of
the pipeline, so that of tail, which succeeds even when tar failed before it.
A monitoring script written this way declares all its archives valid. Two
fixes in bash: test tar alone, or read ${PIPESTATUS[0]} right after the
pipeline.
# ❌ Wrong: $? holds tail's code, always 0tar -tvf /var/backup/home-full-2026-04-09.tar.gz | tail -5echo "Exit code: $?"
# Right: we read tar's own codetar -tvf /var/backup/home-full-2026-04-09.tar.gz | tail -5echo "tar exit code: ${PIPESTATUS[0]}"A truncated archive, for example because the disk filled up during the
backup, shows up as tar: Unexpected EOF in archive or as an error passed up by
the decompressor, and tar exits with code 2.
Compare with the source (if it still exists)
tar --compare -f backup.tar.gz -C / 2>&1 | head -20This command compares the content of the existing archive with the current
file system, and prints the differences (size, date, content). The -C /
option says from which root to compare the paths of the archive.
Is tar enough as a backup solution?
No, and the nuance matters: tar can produce a backup archive, it does not on
its own make up a backup strategy. This guide showed you how to build a
faithful archive; what follows says what the tool will never do for you, and
must therefore be placed around it.
What tar does not do | The concrete consequence |
|---|---|
| No deduplication | ten full backups take ten times the space, even if nothing changed |
| No native encryption | a stolen archive reads as is. You must chain gpg or age |
| No retention, no catalogue | you write the purge yourself, and must know which archive holds what |
| No application consistency | a database archived live gives an inconsistent copy, not a backup |
| No handling of open files | a file modified during archiving is captured in an intermediate state |
| No global checksum | an archive truncated on a block boundary lists without error |
The most dangerous point is the fourth, because it produces no error
message. tar -czf base.tar.gz /var/lib/postgresql/ always succeeds, and
the archive produced is unusable: you needed a pg_dump or a service
stop. A backup that succeeds is not a backup that restores.
Common troubleshooting
tar errors are terse and the real cause is rarely the one the message
announces. Before looking further, tell three families apart: an archive
problem (corrupted, truncated), an environment problem (missing codec, full
disk) and a path problem (misplaced snapshot, leading / removed). The
table below links the displayed message to the family concerned and to the fix.
| Symptom | Likely cause | Solution |
|---|---|---|
tar: Error is not recoverable: exiting now | Corrupted or truncated archive | Check the source file, test again with -tvf |
tar: Removing leading `/' from member names at creation | Information message: tar stores relative paths | Nothing to fix. Extract with -C / to restore to the original location |
Incremental backup archives everything despite the .snar | .snar file missing or corrupted | Delete the .snar and make a new full backup |
| A file modified right after the backup is missing from the incremental | Its change time falls in the same clock tick as the snapshot write | Space the levels out. Measured on 20 September 2026 on ext4: modified in the same tick, the file is missed; modified 1 ms later, it is caught. Harmless for a nightly backup, but fatal in a script that chains both levels |
| ACLs or the SELinux context vanished after restore | The --acls, --xattrs or --selinux option was missing at extraction | Replay the extraction with the same options as at creation. restorecon -Rv catches up the standard contexts, not the ones set by hand |
xz: not found then Child returned status 127 | xz not installed (the case of the Debian 13 image) | apt install xz-utils or dnf install xz |
zstd: command not found or zstd: not found, then Child returned status 127 | zstd not installed | apt install zstd or dnf install zstd |
--exclude '...' has no effect, code 2 | --exclude placed after the path to archive | Put every exclusion before the path |
Not found in archive, code 2 | Requested path differs from the stored one | Copy the exact path shown by tar -tvf |
| Extraction cut halfway | Not enough disk space | df -h to check, free some space |
Key points
--listed-incrementalistar's native mechanism for incremental backups: full backup on the first call, incremental afterwards.- At restore time,
--listed-incremental=/dev/nullis what replays the deletions. Without it, a file deleted between two backups comes back. --excludeand--exclude-fromavoid archiving caches,.gitandnode_modules, provided they come before the path.- zstd is the best choice for modern backups: a ratio comparable to
bzip2, a speed close togzip. --aclsand--xattrsare passed on both ends, at creation and at extraction. An archive created with the option and extracted without it still loses its fine-grained rights.--sparsechanges the order of magnitude on disk images: 513 MB versus 10 KB on a 512 MiB sparse file.- The owner is only restored if the extraction is done by root; otherwise everything belongs to the operator.
- Selective extraction (
tar -xzf archive.tar.gz path/in/archive) avoids extracting everything. - The
tar | sshpipeline, run as root on both ends, transfers a full tree without a temporary file and preserves permissions and owners. - Always test an archive with
tar -tvfbefore you need it, and test a restore before depending on it.
Knowledge check
Check that the essentials of this guide stuck. The questions cover only what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Download files with curl and wget
A new full quiz with random questions
Work again only on the questions you missed