Archive, compress and copy: tar, gzip, xz and rsync
-
Phase 0: Before you start
-
Phase 1: The origins and OS architecture
-
Phase 2: Day-to-day administration and the command builder
-
Phase 3: Enterprise infrastructure and storage
-
Phase 4: Troubleshooting step by step
-
Phase 5: Final project and career
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
| Time | You need | By the end you can |
|---|---|---|
| About 70 minutes | rocky01 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 folder | Create, 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
| Tool | Job | Options you’ll use most |
|---|---|---|
| tar | Packs 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, zstd | Compress 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, unzip | Archives Windows users can open with a double click | -r recurse, -x exclude, -y store symlinks as links, unzip -l list |
| rsync | Makes 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.
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.
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 | sortYou 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/VERSIONYou 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.gzYou 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.gzYou 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/v2The 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.confYou 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.confYou 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.21Always 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 logYou 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 5You 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.
Try it yourself: build a dated tar backup command in the Command Builder.
✅ 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 -lYou 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.zstFor 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.zipYou 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 fileszip 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).

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.06Only 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.confRead 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/htmlhome.html
index.htmlTry it yourself: build an rsync copy or mirror, dry run first, in the Command Builder.
✅ 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/siteYou’ll get:
receiving incremental file list
site/
site/current -> releases/v2
site/conf/
conf
current
html
logs
releases
siteWithout 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 20You’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.confYou’ll get:
/home/student/lab37/site/conf/
/home/student/lab37/site/conf/secret.conf
/home/student/lab37/site/conf/app.conf
exit=0
lanes=10No 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.gzconf/
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.gzYou’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/hostnametar 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.gzexit=0Lesson 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
| Mistake | What happens | What to do instead |
|---|---|---|
| Leaving out the trailing slash on the rsync source | A nested copy (site/site) appears in the destination | Use src/ dest/ to copy contents; check with --dry-run -v |
Running --delete without a dry run, or against the wrong folder | Whole backups or other files vanish in seconds | Dry run first, then add --max-delete and --backup-dir in scripts |
Using tar -P or extracting a backup in place | Live files are overwritten by old copies | Create with -C DIR relative, list with -t, extract into an empty folder with -C |
Using plain rsync -a for a full system copy | Hard links become separate files; ACLs and SELinux labels are lost | Use -aHAX and run the receiver as root when owners must be kept |
| Not checking tar’s exit code in a backup job | Permission errors leave a partial archive that looks fine | Run as root and treat exit code 2 as a failure |
| Compressing everything with xz -9 in the nightly job | The backup runs for hours and overlaps the business day | Use gzip or zstd for daily jobs, xz for archives you keep long |
| Calling an rsync mirror a backup | Deleted or encrypted files disappear from the mirror too | Keep 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?
- Paths starting with /srv/app
- Paths starting with app/
- Only the files, without folders
- 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?
rsync -a site /backup/site/rsync -a site/ /backup/site/rsync -a /backup/site/ sitersync 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?
- It is faster
- -a does not keep hard links, ACLs or extended attributes
- -a deletes files
- -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?
- No archive was written
- The archive was written but some files are missing from it
- The archive is encrypted
- 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?
- gzip
- bzip2
- xz
- zstd
Show answer
C — xz made 410 KB, but it was by far the slowest.
Quick recap
- tar packs folders:
-ccreate,-tlist,-dcompare,-xextract,-Cto work from or into a folder. - Store relative paths, never use
-Pfor 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;
-aHAXkeeps 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-deleteand--backup-dirin 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.
