Archive, compress and copy: tar, gzip, xz and rsync

Linux tar and rsync are the two tools you’ll use most to protect and move data: tar packs a folder into one archive file (usually compressed with gzip, xz or zstd), and rsync copies folders between servers, sending only what changed. Almost every backup script, migration and “copy the logs to me” request is built on one or both.

In Lesson 36 you finished package management. Here you’ll create a small toll-plaza app folder on rocky01, archive and restore it with tar, compare four compressors on real-looking logs, and pull a copy to the backup folder on ubu01 with rsync over the SSH key from Lesson 33. Then you’ll make the classic mistakes on purpose: the missing trailing slash, a --delete aimed at the wrong folder and an archive with absolute paths.

Where this lesson fits

Lesson 37 of 82 in Linux Administration · Phase 2: Day-to-day administration and the command builder · Before this: Lesson 36, packages on Ubuntu with apt · Next: Lesson 38, bash scripting basics

TimeYou needBy the end you can
About 70 minutesrocky01 and ubu01 from Lesson 2, the ubu01 to rocky01 SSH key and ~/.ssh/config from Lesson 33, about 200 MB of space in your home folderCreate, list, compare and restore tar archives, pick a compressor, and copy or mirror folders between servers with rsync safely, dry run first

What it is, in one line

tar packs many files into one archive and keeps their paths, owners and permissions; rsync makes one folder match another, copying only the differences, locally or over SSH.

In Simple Words

Packing for a move, and the daily courier

When a family moves house, they pack each room into labelled cartons. The cartons keep things together and in order, and a vacuum bag makes the blankets take less space, but you have to open the carton to use anything inside.

Now think of a shop owner who sends his day’s accounts to the head office every evening. He doesn’t send the whole ledger again; the courier carries only the new pages, and the head office copy stays the same as the shop’s. tar is the labelled carton, gzip or xz is the vacuum bag, and rsync is the courier who only carries what changed.

Engineer's View

The tools, the options that matter, and what each keeps

ToolJobOptions you’ll use most
tarPacks files and folders into one archive and unpacks them-c create, -x extract, -t list, -d compare, -f file name, -v verbose, -z gzip, -J xz, --zstd, -C DIR change folder first, --exclude, -p keep exact permissions
gzip, bzip2, xz, zstdCompress one file (or the tar stream)-k keep the original, -d decompress, levels -1 to -9 (zstd up to -19), -T0 all CPU cores for xz and zstd
zip, unzipArchives Windows users can open with a double click-r recurse, -x exclude, -y store symlinks as links, unzip -l list
rsyncMakes a destination match a source, locally or over SSH-a archive (-rlptgoD), -H hard links, -A ACLs, -X extended attributes, --delete, --dry-run, -v, -i, --info=progress2, -e ssh

GNU tar removes the leading / from member names by default, so an archive of /etc always extracts as etc/ under the current folder (or under -C). As a normal user tar applies your umask on extract unless you add -p; as root it keeps permissions and owners. For full fidelity on Rocky (SELinux labels, ACLs, xattrs) add --selinux --acls --xattrs both when you create and when you extract.

rsync’s -a keeps permissions, times, owners, groups, symlinks and device files, but not hard links, ACLs or xattrs, which is why backup jobs use -aHAX. Owners and groups are only applied when the receiving side runs as root; as a normal user every file becomes yours. A non-root receiver also copies only user.* xattrs, so Rocky’s SELinux labels are skipped without an error.

Versions in our lab: tar 1.35 and rsync 3.4.1 on both Rocky 10 and Ubuntu 26.04, which share the same options. zstd is newer than the others and has become the default compressor for many tools because it compresses about as well as gzip at several times the speed.

How it works, step by step

Here is what tar does when you run tar -czf site.tar.gz -C ~/lab37 site. The diagram after these steps follows one archive from folder to restore.

1tar walks the folder

With -C ~/lab37 tar first changes into ~/lab37, then reads site and everything under it. For each file it writes a 512-byte header (name, mode, owner, size, time, link target) followed by the data, so the archive keeps relative paths starting with site/. Hard links are stored once and the second name points at the first.

2The stream goes through a compressor

tar itself doesn’t compress. -z pipes the stream through gzip, -J through xz and --zstd through zstd, which is the same as tar -cf - site | gzip > site.tar.gz. The compressor sees one long stream, so many small similar files compress well together.

3Reading it back

-t lists headers without writing anything, -d compares the archive with the files on disk, and -x writes them out under the current folder or -C DIR. When reading, GNU tar detects the compression by itself, so tar -tf site.tar.zst works without --zstd.

Linux tar and rsync: how tar creates, lists and restores a compressed archive

4rsync compares before it copies

rsync starts a second rsync on the other side (over SSH with -e ssh, the default for host:path). The two build file lists and compare size and modification time for each file. Only files that differ are sent, and for large changed files only the changed blocks travel.

That’s why the second run of the same copy takes seconds.

5It writes safely

Each changed file is written to a hidden temporary name in the destination folder and renamed into place when complete, so a reader never sees half a file. Permissions, times and links are set afterwards. With --delete, files that exist only on the destination are removed, which turns a copy into a mirror.

6The trailing slash decides what gets copied

For the source, site/ means “the contents of site” and site means “the folder site itself”. The destination slash makes no difference. Mixing these up is the most common rsync mistake, and with --delete it can wipe a folder, so the picture below shows the three cases side by side.

Linux tar and rsync: what the trailing slash on the source does

rsync creates only the last folder of the destination path. If /srv/backup/rocky01 doesn’t exist, rsync -a site/ /srv/backup/rocky01/site/ fails with “mkdir failed: No such file or directory”, even though a dry run happily said “created directory”. Create the parent with mkdir -p or add --mkpath.

tar and rsync answer different needs. A dated tar archive is a point-in-time copy you can keep for weeks and move anywhere as one file. An rsync mirror is fast and always current, but it copies mistakes too: a file deleted or encrypted on the source disappears from the mirror on the next run with --delete.

Real backup plans keep both, or use snapshots (Lesson 46) and a backup product (Lessons 62 and 63).

Hands-on lab: archive an app folder and pull it to ubu01 with rsync

Before you run any command, double-check it

Read every command twice before you press Enter. Check the target: the server name, the user, the disk or device name, the path and the sizes. These commands are for learning and were tested in our lab, not on your system. At work, run the read-only commands first, follow your change process, make sure a backup exists, and ask a second person to check anything that changes or deletes data. You run them at your own risk.

Steps 1 to 7 run on rocky01 as student, in ~/lab37. Steps 8 to 10 run on ubu01, which plays the backup server and pulls from rocky01 over SSH, so a hacked app server can’t reach in and delete its own backups. tar, gzip and rsync outputs were produced from these exact files with tar 1.35 and rsync 3.4.1; ordering, times and byte counts on your VMs may differ a little.

1Create a practice app folder on rocky01

Paste this whole block:

mkdir -p ~/lab37/site/{conf,html,logs,releases/v1,releases/v2} && cd ~/lab37/site
printf '<h1>Plaza 07 lane status</h1>\n' > html/index.html
printf 'plaza=07\nlanes=8\ndb_host=10.10.10.21\n' > conf/app.conf
printf 'db_password=Lab@1234\n' > conf/secret.conf && chmod 600 conf/secret.conf
printf 'version 1\n' > releases/v1/VERSION
printf 'version 2\n' > releases/v2/VERSION
ln -s releases/v2 current
ln html/index.html html/home.html
seq 1 200000 | awk '{printf "2026-10-27T%02d:%02d:%02d+05:30 plaza07 lane=%d vehicle=MP09%c%c%04d class=%d amount=%d status=%s\n", int($1/8334)%24, int($1/139)%60, $1%60, $1%8+1, 65+$1%26, 65+int($1/26)%26, $1%9973, $1%4+1, 40+($1%4)*25, ($1%97==0?"FAILED":"OK")}' > logs/transactions.log
cp logs/transactions.log logs/transactions-2026-10-26.log
touch -d '2026-10-26 23:59' logs/transactions-2026-10-26.log
cd ~/lab37 && du -sh site && find site | sort

You should see this:

34M	site
site
site/conf
site/conf/app.conf
site/conf/secret.conf
site/current
site/html
site/html/home.html
site/html/index.html
site/logs
site/logs/transactions-2026-10-26.log
site/logs/transactions.log
site/releases
site/releases/v1
site/releases/v1/VERSION
site/releases/v2
site/releases/v2/VERSION

You now have a config folder with one private file (mode 600), two release folders, a symlink current pointing at v2, a hard link (home.html and index.html are the same file) and two 17 MB transaction logs. These are the things that break careless copies, which is why they’re here. head -n 2 site/logs/transactions.log shows what a log line looks like:

2026-10-27T00:00:01+05:30 plaza07 lane=2 vehicle=MP09BA0001 class=2 amount=65 status=OK
2026-10-27T00:00:02+05:30 plaza07 lane=3 vehicle=MP09CA0002 class=3 amount=90 status=OK

✅ Checkpoint: du -sh site shows about 34M and find lists 16 entries.

2Create a dated, compressed archive

Run this in ~/lab37:

tar -czvf site-$(date +%F).tar.gz site
ls -lh site-*.tar.gz

You should see something like this:

site/
site/html/
site/html/home.html
site/html/index.html
site/releases/
site/releases/v1/
site/releases/v1/VERSION
site/releases/v2/
site/releases/v2/VERSION
site/logs/
site/logs/transactions-2026-10-26.log
site/logs/transactions.log
site/conf/
site/conf/secret.conf
site/conf/app.conf
site/current
-rw-r--r--. 1 student student 2.6M Oct 27 10:37 site-2026-10-27.tar.gz

-c creates, -z compresses with gzip, -v prints each name and -f names the file, so -f must come last before the file name. 34 MB became 2.6 MB because logs repeat a lot. Order follows the directory on disk, so yours may differ. Putting $(date +%F) in the name gives you one file per day that sorts correctly.

✅ Checkpoint: site-2026-10-27.tar.gz (with today’s date) exists and is about 2.6M.

3List an archive, and compare it with the disk

Run this:

tar -tvzf site-$(date +%F).tar.gz

You should see something like this:

drwxr-xr-x student/student   0 2026-10-27 10:37 site/
drwxr-xr-x student/student   0 2026-10-27 10:37 site/html/
-rw-r--r-- student/student  30 2026-10-27 10:37 site/html/home.html
hrw-r--r-- student/student   0 2026-10-27 10:37 site/html/index.html link to site/html/home.html
drwxr-xr-x student/student   0 2026-10-27 10:37 site/releases/
drwxr-xr-x student/student   0 2026-10-27 10:37 site/releases/v1/
-rw-r--r-- student/student  10 2026-10-27 10:37 site/releases/v1/VERSION
drwxr-xr-x student/student   0 2026-10-27 10:37 site/releases/v2/
-rw-r--r-- student/student  10 2026-10-27 10:37 site/releases/v2/VERSION
drwxr-xr-x student/student   0 2026-10-27 10:37 site/logs/
-rw-r--r-- student/student 17658244 2026-10-26 23:59 site/logs/transactions-2026-10-26.log
-rw-r--r-- student/student 17658244 2026-10-27 10:37 site/logs/transactions.log
drwxr-xr-x student/student        0 2026-10-27 10:37 site/conf/
-rw------- student/student       21 2026-10-27 10:37 site/conf/secret.conf
-rw-r--r-- student/student       37 2026-10-27 10:37 site/conf/app.conf
lrwxrwxrwx student/student        0 2026-10-27 10:37 site/current -> releases/v2

The h line shows tar stored the hard link once, and the l line shows the symlink kept as a link. Paths start with site/, not /home/student, which tells you exactly where an extract will put things. Now change a file and ask tar what differs:

echo "lanes=10" >> site/conf/app.conf
tar -dzf site-$(date +%F).tar.gz; echo "exit=$?"
sed -i '$d' site/conf/app.conf

You should see this:

site/conf/app.conf: Mod time differs
site/conf/app.conf: Size differs
exit=1

-d (diff) is a quick way to check a backup still matches, or to see what changed since it was taken. The sed line removes the test line again.

✅ Checkpoint: tar -t shows the hard link and symlink lines, and tar -d reported app.conf.

4Restore somewhere safe, then pick out one file

Run this:

mkdir -p restore
tar -xzf site-$(date +%F).tar.gz -C restore
diff -r site restore/site && echo "no differences"
ls -l restore/site/conf
stat -c '%h %n' restore/site/html/*.html
tar -xzf site-$(date +%F).tar.gz -C /tmp site/conf/app.conf
cat /tmp/site/conf/app.conf

You should see something like this:

no differences
-rw-r--r--. 1 student student 37 Oct 27 10:37 app.conf
-rw-------. 1 student student 21 Oct 27 10:37 secret.conf
2 restore/site/html/home.html
2 restore/site/html/index.html
plaza=07
lanes=8
db_host=10.10.10.21

Always restore into an empty folder with -C first and compare, never straight over live files. The private file kept mode 600 and the hard link count is still 2. To pull out one file, give its path exactly as tar -t prints it.

Clean up with rm -r restore /tmp/site.

✅ Checkpoint: diff prints “no differences” and /tmp/site/conf/app.conf shows lanes=8.

5Leave things out, and back up /etc as root

Run this:

tar -czf site-nologs-$(date +%F).tar.gz --exclude='*.log' site
ls -lh site-*.tar.gz
tar -tzf site-nologs-$(date +%F).tar.gz | grep log

You should see something like this:

-rw-r--r--. 1 student student 2.6M Oct 27 10:37 site-2026-10-27.tar.gz
-rw-r--r--. 1 student student  513 Oct 27 10:37 site-nologs-2026-10-27.tar.gz
site/logs/

The empty logs/ folder stays, its files don’t. Quote the pattern so the shell doesn’t expand it first. Now the job every admin does before a change, an archive of /etc:

sudo mkdir -p /root/backup
sudo tar -czpf /root/backup/etc-$(hostname -s)-$(date +%F).tar.gz -C / etc
sudo ls -lh /root/backup
sudo tar -tzf /root/backup/etc-$(hostname -s)-$(date +%F).tar.gz | head -n 5

You should see something like this:

total 5.6M
-rw-r--r--. 1 root root 5.6M Oct 27 10:52 etc-rocky01-2026-10-27.tar.gz
etc/
etc/mtab
etc/fstab
etc/crypttab
etc/lvm/

-C / etc stores the paths as etc/... with no leading slash, and -p keeps exact modes. On Rocky add --selinux --acls --xattrs when the archive must restore SELinux labels too. The archive holds /etc/shadow and private keys, so it stays readable by root only.

✅ Checkpoint: sudo ls -lh /root/backup shows today’s etc archive.

6Compare gzip, bzip2, xz and zstd

Run this (it takes about half a minute):

mkdir -p ~/lab37/cmp && cp ~/lab37/site/logs/transactions.log ~/lab37/cmp/ && cd ~/lab37/cmp
for c in "gzip -k" "bzip2 -k" "xz -k -T0" "zstd -q"; do /usr/bin/time -f "$c: %e s" $c transactions.log; done
ls -l

You should see something like this (sizes are exact for these files, times depend on your CPU):

gzip -k: 0.14 s
bzip2 -k: 2.20 s
xz -k -T0: 10.21 s
zstd -q: 0.07 s
-rw-r--r--. 1 student student 17658244 Oct 27 10:37 transactions.log
-rw-r--r--. 1 student student   789251 Oct 27 10:37 transactions.log.bz2
-rw-r--r--. 1 student student  1325283 Oct 27 10:37 transactions.log.gz
-rw-r--r--. 1 student student   417844 Oct 27 10:37 transactions.log.xz
-rw-r--r--. 1 student student  1147786 Oct 27 10:37 transactions.log.zst

For this 17.6 MB log, gzip made 1.3 MB in a blink, zstd a little smaller and faster, bzip2 770 KB slowly, and xz 410 KB but took 70 times longer than gzip. Use gzip or zstd for daily backups and logs, xz when the file is written once and copied or stored many times. The same choice works inside tar; when reading, tar detects the format itself:

cd ~/lab37
tar -czf site.tar.gz site
XZ_OPT=-T0 tar -cJf site.tar.xz site
tar --zstd -cf site.tar.zst site
ls -l site.tar.*
tar -tf site.tar.zst | head -n 2
-rw-r--r--. 1 student student 2651273 Oct 27 10:44 site.tar.gz
-rw-r--r--. 1 student student  867816 Oct 27 10:44 site.tar.xz
-rw-r--r--. 1 student student 2292507 Oct 27 10:44 site.tar.zst
site/
site/html/

If /usr/bin/time or zstd is missing on rocky01, install them with sudo dnf install -y time zstd. Remove the test files afterwards with rm -r ~/lab37/cmp ~/lab37/site.tar.*.

✅ Checkpoint: The .xz file is the smallest and the zstd run was the fastest.

7Make a zip for a Windows colleague

Run this:

zip -r site-for-windows.zip site -x '*.log'
unzip -l site-for-windows.zip

You should see something like this (shortened):

  adding: site/ (stored 0%)
  adding: site/html/ (stored 0%)
  adding: site/html/home.html (stored 0%)
  adding: site/html/index.html (stored 0%)
  ...
  adding: site/conf/app.conf (deflated 8%)
  adding: site/current/ (stored 0%)
  adding: site/current/VERSION (stored 0%)
Archive:  site-for-windows.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
...
        0  2026-10-27 10:37   site/current/
       10  2026-10-27 10:37   site/current/VERSION
---------                     -------
      148                     15 files

zip doesn’t keep Linux owners, and by default it follows symlinks, so current became a real folder with a copy of VERSION. Add -y to store symlinks as links. Use zip only for handing files to people on Windows; for Linux backups tar keeps far more.

If zip is missing on rocky01, run sudo dnf install -y zip unzip.

✅ Checkpoint: unzip -l lists 15 entries and no .log files.

8Prepare the backup folder on ubu01 and dry-run the copy

On ubu01, as student (load your key first if ssh asks for the passphrase: eval "$(ssh-agent -s)"; ssh-add):

sudo mkdir -p /srv/backup/rocky01
sudo chown -R student: /srv/backup
rsync -aHAX --dry-run -v rocky01:lab37/site/ /srv/backup/rocky01/site/

You should see something like this:

receiving incremental file list
./
current -> releases/v2
conf/
conf/app.conf
conf/secret.conf
html/
html/index.html
html/home.html => html/index.html
logs/
logs/transactions-2026-10-26.log
logs/transactions.log
releases/
releases/v1/
releases/v1/VERSION
releases/v2/
releases/v2/VERSION

sent 98 bytes  received 564 bytes  1,324.00 bytes/sec
total size is 35,316,637  speedup is 53,348.40 (DRY RUN)

rocky01: uses the Host entry from your ~/.ssh/config (Lesson 33), and a path without a leading slash starts in student’s home on rocky01. “receiving” tells you this is a pull. The => line shows -H recognised the hard link.

“(DRY RUN)” means nothing was copied; read the list every time before you run the real thing.

✅ Checkpoint: The dry run lists 16 entries and ends with (DRY RUN).

Linux tar and rsync lab: rsync with progress2 from rocky01 to ubu01
Example output: rsync pulling the site folder from rocky01 to ubu01, then a second run with nothing to send.

9Copy for real and watch only changes move

Run the copy on ubu01, then run it again:

rsync -aHAX --info=progress2 rocky01:lab37/site/ /srv/backup/rocky01/site/
rsync -aHAX -v rocky01:lab37/site/ /srv/backup/rocky01/site/

You should see something like this:

     35,316,596  99%   22.79MB/s    0:00:01 (xfr#7, to-chk=0/16)
receiving incremental file list

sent 26 bytes  received 492 bytes  1,036.00 bytes/sec
total size is 35,316,637  speedup is 68,178.84

--info=progress2 shows one progress line for the whole job instead of one per file. The second run found nothing to send. Now change one file on rocky01 (sed -i 's/lanes=8/lanes=10/' ~/lab37/site/conf/app.conf) and run the -v copy on ubu01 again:

receiving incremental file list
conf/app.conf

sent 58 bytes  received 542 bytes  1,200.00 bytes/sec
total size is 35,316,638  speedup is 58,861.06

Only app.conf travelled. For a short report of what changed and why, use -i (itemize). Add a line on rocky01 with echo "maint_window=02:00" >> ~/lab37/site/conf/app.conf, then run rsync -aHAX -i rocky01:lab37/site/ /srv/backup/rocky01/site/ on ubu01:

>f.s....... conf/app.conf

Read it as: > received, f file, s size changed; the dots are attributes that were already the same. Check that ubu01 really has the hard link and the private mode with stat -c '%h %a %n' /srv/backup/rocky01/site/html/*.html /srv/backup/rocky01/site/conf/secret.conf.

✅ Checkpoint: The second plain run sent nothing, the third sent only conf/app.conf, and secret.conf on ubu01 is mode 600.

10Mirror with --delete, dry run first

Make an extra file on the ubu01 copy, then preview a mirror:

touch /srv/backup/rocky01/site/html/old.html
rsync -aHAX --delete --dry-run -v rocky01:lab37/site/ /srv/backup/rocky01/site/

You should see something like this:

receiving incremental file list
deleting html/old.html
html/

sent 36 bytes  received 495 bytes  1,062.00 bytes/sec
total size is 35,316,638  speedup is 66,509.68 (DRY RUN)

Only the extra file would go, which is what you want, so run it without --dry-run. If a dry run ever shows more “deleting” lines than you expect, stop and check both paths. The Command Builder version of this task pushes from rocky01 to ubu01 instead, which needs rocky01’s own key on ubu01.

rsync -aHAX --delete -v rocky01:lab37/site/ /srv/backup/rocky01/site/
ls /srv/backup/rocky01/site/html
home.html
index.html

✅ Checkpoint: old.html is gone from the ubu01 copy and nothing else changed.

Break It, Then Fix It

All of these run in your own folders, so no snapshot is needed, but read each command twice anyway. They’re the four mistakes behind most “the backup was there, but…” stories.

1Forget the trailing slash on the source

On ubu01, run:

rsync -av rocky01:lab37/site /srv/backup/rocky01/site/ | head -n 4
ls /srv/backup/rocky01/site

You’ll get:

receiving incremental file list
site/
site/current -> releases/v2
site/conf/
conf
current
html
logs
releases
site

Without the slash, rsync copied the folder site into the destination, so you now have site/site/ with a second full copy, 34 MB of it. On a real server this quietly doubles the backup size and leaves old files in the outer folder forever.

Fix: remove the nested copy and remember the rule: slash on the source means “the contents”.

rm -r /srv/backup/rocky01/site/site
rsync -aHAX --dry-run -v rocky01:lab37/site/ /srv/backup/rocky01/site/ | tail -n 3

✅ Checkpoint: ls /srv/backup/rocky01/site shows conf, current, html, logs and releases only.

2Aim --delete one folder too high

Put a second “backup” next to the site copy, then preview a mirror that targets the parent folder by mistake:

echo test > /srv/backup/rocky01/etc-rocky01-2026-10-27.tar.gz
rsync -av --delete --dry-run rocky01:lab37/site/ /srv/backup/rocky01/ | head -n 20

You’ll get this:

receiving incremental file list
deleting site/releases/v2/VERSION
deleting site/releases/v2/
deleting site/releases/v1/VERSION
deleting site/releases/v1/
deleting site/releases/
deleting site/logs/transactions.log
deleting site/logs/transactions-2026-10-26.log
deleting site/logs/
deleting site/html/index.html
deleting site/html/home.html
deleting site/html/
deleting site/conf/secret.conf
deleting site/conf/app.conf
deleting site/conf/
deleting site/current
deleting site/
deleting etc-rocky01-2026-10-27.tar.gz
./
current -> releases/v2
conf/

The destination should have been /srv/backup/rocky01/site/. Pointed at the parent, --delete makes /srv/backup/rocky01 match the site folder, which means deleting the existing site backup and every other file there, including the etc archive. Without --dry-run that would have happened in a second, with no recycle bin.

Fix: correct the destination, and add brakes to any scripted --delete.

rsync -aHAX --delete --max-delete=20 --backup --backup-dir=/srv/backup/rocky01/deleted-$(date +%F) --dry-run -v rocky01:lab37/site/ /srv/backup/rocky01/site/
rm /srv/backup/rocky01/etc-rocky01-2026-10-27.tar.gz

--max-delete=20 makes rsync stop deleting after 20 files and report an error, and --backup-dir moves deleted or replaced files into a dated folder instead of removing them.

✅ Checkpoint: The corrected dry run shows no “deleting” lines for the etc archive or the site folder.

3An archive with absolute paths overwrites live files

On rocky01, archive the config with -P (keep the leading /), make an approved change, then “just look at” the archive in /tmp:

cd ~/lab37
tar -czPf conf-P.tar.gz ~/lab37/site/conf
tar -tzPf conf-P.tar.gz
sed -i 's/lanes=10/lanes=12/' site/conf/app.conf
cd /tmp && tar -xzPf ~/lab37/conf-P.tar.gz; echo "exit=$?"
grep lanes ~/lab37/site/conf/app.conf

You’ll get:

/home/student/lab37/site/conf/
/home/student/lab37/site/conf/secret.conf
/home/student/lab37/site/conf/app.conf
exit=0
lanes=10

No warning, no error, and your new value 12 is gone: -P kept absolute paths, so the extract ignored the current folder and wrote straight over the live file. Do the same with an archive of /etc taken by root and you put last month’s fstab and sshd_config back on a running server. Without -P, tar prints “Removing leading `/’ from member names” and stores relative paths, which is the safe default.

Fix: never use -P for backups; create with -C and relative paths, and always list (-t) before you extract.

rm ~/lab37/conf-P.tar.gz
tar -czf ~/lab37/conf.tar.gz -C ~/lab37/site conf
tar -tzf ~/lab37/conf.tar.gz
conf/
conf/secret.conf
conf/app.conf

✅ Checkpoint: The new archive lists conf/ paths with no leading slash.

4A backup that is quietly incomplete

As student (not root), try to archive two files from /etc:

tar -czf ~/lab37/etc-test.tar.gz /etc/shadow /etc/hostname; echo "exit=$?"
tar -tzf ~/lab37/etc-test.tar.gz

You’ll get this:

tar: Removing leading `/' from member names
tar: /etc/shadow: Cannot open: Permission denied
tar: Removing leading `/' from hard link targets
tar: Exiting with failure status due to previous errors
exit=2
etc/hostname

tar still wrote an archive, with only the files it could read. In a cron job with output thrown away, this looks like a successful backup every night. Exit status 2 means fatal errors; 1 means some files changed while being read, which is common for live logs.

Fix: run backups of system files as root, and make the script check the exit code.

sudo tar -czf /root/backup/etc-test.tar.gz /etc/shadow /etc/hostname 2>/dev/null; echo "exit=$?"
sudo rm /root/backup/etc-test.tar.gz; rm ~/lab37/etc-test.tar.gz
exit=0

Lesson 39 turns this into a script with set -euo pipefail and a check on tar’s exit code, so a broken backup fails loudly.

✅ Checkpoint: The root run prints exit=0, and you know what exit codes 1 and 2 mean for tar.

This happened at work

The night rsync mirrored an empty folder

The on-call engineer at a highway toll operator in Indore told me this one. Each toll plaza had a small Linux server that collected lane transactions into /data/txn, and every night at 2 a.m. a cron job ran rsync -a --delete to mirror that folder to the central backup server. It had worked for three years.

One morning the finance team couldn’t build the monthly reconciliation for one plaza, because the backup folder for it was empty. The first suspicion was the backup server: a failing disk or even ransomware. The disk was healthy and every other plaza’s folder was fine.

The plaza server itself had rebooted after a power cut the evening before.

That was the cause. After the reboot, the data disk didn’t mount because its fstab line used a device name that had changed, so /data/txn was an empty folder on the root disk. At 2 a.m. rsync did exactly what it was told: it made the mirror match the empty source and deleted 40 days of transactions.

They rebuilt most of it from the weekly tar archives kept on separate storage and pulled the last two days again from the lane controllers.

The script now checks mountpoint -q /data/txn and a marker file before it starts, uses --max-delete=200 and a dated --backup-dir, and the fstab entries use UUIDs with nofail (Lesson 43). Weekly dated tar archives were kept as a separate copy, because a mirror on its own copies mistakes as faithfully as data.

Common mistakes and troubleshooting

MistakeWhat happensWhat to do instead
Leaving out the trailing slash on the rsync sourceA nested copy (site/site) appears in the destinationUse src/ dest/ to copy contents; check with --dry-run -v
Running --delete without a dry run, or against the wrong folderWhole backups or other files vanish in secondsDry run first, then add --max-delete and --backup-dir in scripts
Using tar -P or extracting a backup in placeLive files are overwritten by old copiesCreate with -C DIR relative, list with -t, extract into an empty folder with -C
Using plain rsync -a for a full system copyHard links become separate files; ACLs and SELinux labels are lostUse -aHAX and run the receiver as root when owners must be kept
Not checking tar’s exit code in a backup jobPermission errors leave a partial archive that looks fineRun as root and treat exit code 2 as a failure
Compressing everything with xz -9 in the nightly jobThe backup runs for hours and overlaps the business dayUse gzip or zstd for daily jobs, xz for archives you keep long
Calling an rsync mirror a backupDeleted or encrypted files disappear from the mirror tooKeep dated archives or snapshots as well, on separate storage

Interview questions

Click a question to open it. Each one is written the way an interviewer would ask it. Under it you will see why they ask it and one way you could answer, in your own words.

Fresher level

1. How would you create a compressed archive of /etc and restore just one file from it?

Why they ask this: A basic hands-on question; they listen for -C, the leading slash and listing before extracting.

How you might answer: I’d run sudo tar -czpf /root/backup/etc-$(hostname -s)-$(date +%F).tar.gz -C / etc, which stores paths as etc/ without a leading slash. To get one file I list it first with tar -tzf FILE | grep fstab.

Then I extract only that member into a temporary folder, tar -xzf FILE -C /tmp/restore etc/fstab, compare it with the live file using diff, and copy it into place only if that’s really what I want.

2. What is the difference between rsync -a source and rsync -a source/ to the same destination?

Why they ask this: The trailing slash question is a favourite because it causes real damage.

How you might answer: With source/ rsync copies the contents of the folder into the destination. Without the slash it copies the folder itself, so you end up with destination/source/.

The destination’s slash doesn’t matter. I always do a --dry-run -v first, especially when --delete is involved, because pointing a mirror at the wrong level deletes everything else there.

2–5 years experience

3. You need to move 2 TB of application data to a new server with as little downtime as possible. How do you use rsync?

Why they ask this: They want the two-pass method and awareness of what -a doesn’t keep.

How you might answer: I’d run rsync -aHAX --info=progress2 while the app is still running, which copies the bulk, then repeat it a few times so each pass gets shorter. In the downtime window I stop the app, run one final pass with --delete, and the cut-over only takes as long as that last pass.

I run it as root on both ends so owners, ACLs and SELinux labels come across, and check afterwards with a dry run that reports nothing to do, plus a file count and a few checksums.

4. When would you choose gzip, xz or zstd for backups?

Why they ask this: They’re checking you think about time and CPU as well as size.

How you might answer: For daily jobs I want speed, so gzip or zstd: in my lab zstd compressed a 17 MB log to 1.1 MB in 0.07 seconds, while xz got 410 KB but took ten seconds. On big data that difference decides whether the job finishes in the night.

I use xz for things written once and kept or shipped many times, like release tarballs or year-end archives, and I add -T0 so it uses every core. If the data is already compressed, such as images or database dumps with compression, I skip compression altogether.

Senior level

5. Design a simple but safe file-level backup for 20 Linux app servers using only tar, rsync and SSH.

Why they ask this: Seniors should cover pull versus push, retention, safety brakes and monitoring.

How you might answer: The backup server pulls with a dedicated key that is restricted on each app server, so a compromised app server can’t delete its backups. Nightly rsync with -aHAX into a current copy, using --link-dest to the previous day so each day is a full tree but unchanged files are hard links.

Scripts check that the source is mounted, use --max-delete, fail on non-zero exit codes and report to monitoring. Weekly tar archives go to separate storage with 30-day retention, and we test a restore every month, because an untested backup isn’t a backup.

6. An rsync job that ran for years suddenly emptied the backup copy. How do you investigate and what do you change?

Why they ask this: They want root-cause thinking about the source side, not only the command.

How you might answer: I’d check the source first: was it mounted, did the path change, did someone move data. A mirror with --delete deletes whatever the source no longer has, so an unmounted disk or a renamed folder looks like “delete everything”. The cron log and rsync’s own output with the deleting lines usually show it.

Changes: a mountpoint and marker check before the run, --max-delete and a dated --backup-dir, alerts when the deleted count is unusual, and dated archives or snapshots kept separately so one bad night can never wipe history.

Quick quiz

Q1. What does tar -czf backup.tar.gz -C /srv app store?

  1. Paths starting with /srv/app
  2. Paths starting with app/
  3. Only the files, without folders
  4. Nothing, -C is invalid with -c
Show answer

B — -C changes into /srv first, so members are stored relative to it.

Q2. Which rsync command copies the contents of site into /backup/site?

  1. rsync -a site /backup/site/
  2. rsync -a site/ /backup/site/
  3. rsync -a /backup/site/ site
  4. rsync site /backup
Show answer

B — A trailing slash on the source means “the contents of”.

Q3. Why do backup jobs use rsync -aHAX instead of -a?

  1. It is faster
  2. -a does not keep hard links, ACLs or extended attributes
  3. -a deletes files
  4. -aHAX compresses the data
Show answer

B — -H keeps hard links, -A ACLs and -X extended attributes such as SELinux labels.

Q4. tar ends with “Exiting with failure status due to previous errors” and exit code 2. What is true?

  1. No archive was written
  2. The archive was written but some files are missing from it
  3. The archive is encrypted
  4. It only means files changed while reading
Show answer

B — tar writes what it can; code 2 means fatal errors such as permission denied.

Q5. On a 17 MB log, which compressor gave the smallest file in the lab?

  1. gzip
  2. bzip2
  3. xz
  4. zstd
Show answer

C — xz made 410 KB, but it was by far the slowest.

Quick recap

  • tar packs folders: -c create, -t list, -d compare, -x extract, -C to work from or into a folder.
  • Store relative paths, never use -P for backups, and restore into an empty folder before touching live files.
  • gzip and zstd are fast, xz is smallest and slow; tar detects the compression when reading.
  • rsync sends only differences; -aHAX keeps hard links, ACLs and xattrs, and owners need root on the receiver.
  • A slash on the source means “contents”, and rsync only creates the last folder of the destination unless you add --mkpath.
  • Dry-run every --delete, add --max-delete and --backup-dir in scripts, and keep dated archives as well as mirrors.

Next lesson

Lesson 38: Bash scripting basics → You’ll write your first scripts with a shebang, variables, if tests, loops, arguments and exit codes, and turn today’s tar and rsync commands into a small backup script.

Further reading: Linux tar and rsync

Want to go deeper into Linux tar and rsync? Read the GNU tar manual, the rsync(1) manual, the man7.org pages for gzip and xz, and the Zstandard project page. You can also see the full plan for this course on the Linux Administration course page.

Scroll to Top

This website uses cookies

We use a few cookies to keep this site working. If you allow it, we also use Google Analytics to count visits so we can improve our lessons. You can accept all, deny the optional ones, or choose what to allow.