sysadmin · June 12, 2026 · 14 min read

A backup pipeline.
tar, rsync, scp, and the trailing-slash trap.

logadmin1’s backup script broke when the last admin left. Rebuild it: a daily gzip archive of /etc, a weekly bzip2 snapshot of a home directory, and a continuous rsync mirror of /var/log.

// what we’re getting into
  1. The ticket
  2. Concept review. tar flags and compression.
  3. Set up the backup directory
  4. Daily archive of /etc
  5. Weekly bzip2 snapshot
  6. The rsync mirror and the trailing slash
  7. Copy an archive with scp
  8. Extracting, including a single file
  9. Exam questions
  10. Final checklist: confirm everything works

The ticket: logadmin1 lost their backup script when the previous admin left. Three things to rebuild. A daily compressed tarball of /etc, a weekly bzip2 snapshot of /home/dev1 for better compression on source code, and a continuous rsync mirror of /var/log that excludes already-rotated .gz files and removes anything from the mirror that is gone from the source. Also test scp by copying one archive locally.

The real problem: two traps live here. The trailing slash in rsync changes what gets copied, and the —delete flag is unforgiving. Both deserve a dry run.

What we are doing: tar in every compression flavor, rsync with delete and exclude, and scp.

The ticket

bash
id logadmin1
ls /srv/backups 2>/dev/null || sudo mkdir -p /srv/backups
which tar rsync scp

Concept review. tar flags and compression.

bash
man tar rsync scp
tar flags
c  create      x  extract     t  list
v  verbose     f  filename (always right before the archive name)
z  gzip (.tar.gz)   j  bzip2 (.tar.bz2)   J  xz (.tar.xz)
-C change to a directory before extracting
--exclude  skip a pattern

The three compressors trade speed for ratio. gzip is fast and moderate, good for daily backups. bzip2 is slower but compresses text and source better. xz is slowest and smallest, for long-term storage.

Set up the backup directory

bash
sudo mkdir -p /srv/backups/logs
sudo chown admin:logadmins /srv/backups
sudo chmod 770 /srv/backups
ls -ld /srv/backups

Daily archive of /etc

Put the date in the filename so daily runs do not overwrite each other. c z v f means create, gzip, verbose, filename. Always verify the archive after.

bash
DATE=$(date +%Y%m%d)
sudo tar -czvf /srv/backups/etc-${DATE}.tar.gz /etc/ 2>/dev/null
ls -lh /srv/backups/etc-*.tar.gz
tar -tzvf /srv/backups/etc-${DATE}.tar.gz | head -10    # list without extracting

Weekly bzip2 snapshot

Same idea, bzip2 this time with j for better compression on source code.

bash
DATE=$(date +%Y%m%d)
sudo tar -cjvf /srv/backups/dev1-${DATE}.tar.bz2 /home/dev1/ 2>/dev/null
ls -lh /srv/backups/dev1-*.tar.bz2

The rsync mirror and the trailing slash

The trailing slash on the source is the whole game. A trailing slash copies the contents of the directory. No trailing slash copies the directory itself into the destination. Because —delete removes anything in the destination that is not in the source, always dry run first.

bash
# the difference, in words:
#   rsync -av /src/  /dst/   copies the CONTENTS of src into dst
#   rsync -av /src   /dst/   creates /dst/src/

# dry run first, always, with --delete
sudo rsync -av --dry-run --delete --exclude='*.gz' /var/log/ /srv/backups/logs/

# looks safe? run it for real
sudo rsync -av --delete --exclude='*.gz' /var/log/ /srv/backups/logs/
du -sh /srv/backups/logs/

Copy an archive with scp

bash
mkdir -p /tmp/scp_landing
DATE=$(date +%Y%m%d)
scp /srv/backups/etc-${DATE}.tar.gz localhost:/tmp/scp_landing/
ls -lh /tmp/scp_landing/

# recursive directory copy
scp -r /srv/backups/logs localhost:/tmp/scp_landing/

Extracting, including a single file

Restores matter as much as backups. You can extract everything, or pull one file out by naming its path inside the archive.

bash
DATE=$(date +%Y%m%d)

# extract everything to a new directory
mkdir -p /tmp/restore_test
sudo tar -xzvf /srv/backups/etc-${DATE}.tar.gz -C /tmp/restore_test/

# extract only one file
mkdir -p /tmp/restore_one
sudo tar -xzvf /srv/backups/etc-${DATE}.tar.gz -C /tmp/restore_one/ etc/hostname
ls -la /tmp/restore_one/etc/

sudo rm -rf /tmp/restore_test /tmp/restore_one

Exam questions

Write the command first.

Q1. Create a gzip tar archive of /etc at /tmp/etc_backup.tar.gz, then list its contents without extracting.

Q2. Extract only /etc/hostname from that archive into /tmp/restored/.

Q3. rsync /var/log to /backup/logs, excluding all .gz files, deleting anything in the destination no longer in the source. Show the dry run first.

Answers.

bash
# A1
sudo tar -czvf /tmp/etc_backup.tar.gz /etc/ 2>/dev/null
tar -tzvf /tmp/etc_backup.tar.gz | head -20
# A2
mkdir -p /tmp/restored
sudo tar -xzvf /tmp/etc_backup.tar.gz -C /tmp/restored/ etc/hostname
# A3
sudo mkdir -p /backup/logs
sudo rsync -av --dry-run --delete --exclude='*.gz' /var/log/ /backup/logs/
sudo rsync -av --delete --exclude='*.gz' /var/log/ /backup/logs/

Final checklist: confirm everything works

If every check passes, the ticket is done.

bash
# 1. daily /etc gzip archive exists
ls /srv/backups/etc-*.tar.gz

# 2. weekly bzip2 home snapshot exists
ls /srv/backups/dev1-*.tar.bz2

# 3. /var/log mirror excludes .gz and honors --delete
ls /srv/backups/logs/ | head

# 4. --delete tested with --dry-run first
# 5. all three exam commands written from scratch
# 6. tracker entry checked off

Reply to logadmin1: daily gzip archives of /etc and weekly bzip2 snapshots of /home/dev1 in /srv/backups/. Continuous /var/log rsync mirror at /srv/backups/logs/ with —delete and —exclude=*.gz. Ready to wire into cron.

next post
XFS quotas before the database team fills the disk