Archiving and Compression (tar, gzip)

Archiving bundles many files and directories into a single file while preserving their structure, permissions, and timestamps. Compression shrinks that file by encoding repeated patterns more efficiently. On Linux these are traditionally two separate jobs handled by two separate tools: tar builds the archive, and gzip compresses it, though tar can drive gzip internally with a single flag. You will use this combination constantly — backing up a project, shipping a directory of logs to a teammate, or unpacking software distributed as a .tar.gz file.

Overview / How it works

tar stands for tape archive — it was originally built to write backups to magnetic tape drives, where you could only write and read one continuous stream of bytes, not jump around a filesystem’s individual files. That constraint shaped the tool that survives today: tar reads a set of files and directories and serializes them into one continuous stream. For every file it writes a small header block (name, owner, permissions, size, modification time) followed by the file’s raw contents, then moves to the next file. The result is a .tar file that is roughly the same total size as the inputs plus a little header overhead — tar by itself does not compress anything, it only concatenates.

gzip is a separate, older program that does one job well: it compresses a single file using the DEFLATE algorithm, a combination of LZ77 dictionary-based redundancy elimination and Huffman coding (assigning shorter bit-codes to more frequent byte patterns). Given report.txt, gzip replaces it with report.txt.gz and deletes the original by default. gzip has no concept of directories or multiple files — it compresses one stream of bytes into another.

Put the two together and you get the workflow this lesson is about: tar solves the "many files into one file" problem, and gzip solves the "make it smaller" problem. Historically you would chain them with a pipe, since a pipe connects one process’s standard output directly to another process’s standard input at the kernel level: tar cf - mydir | gzip > mydir.tar.gz (the - tells tar to write the archive to standard output instead of a file). Modern GNU tar links directly against the compression library (zlib for gzip, libbzip2, liblzma for xz) and can do this in one step when you pass the -z flag, avoiding the extra process entirely while producing an identical result. Either way the naming convention is the same: archive.tar.gz, often shortened to archive.tgz.

gzip is not the only compressor tar can drive. bzip2 (-j, extension .tar.bz2) compresses noticeably better than gzip but is slower. xz (-J, extension .tar.xz) compresses better still and is common for distributing source packages, at the cost of being the slowest of the three to compress (decompression is fast for all of them). Choose based on what you’re optimizing for:

Tool / extension Speed Typical ratio Best for
gzip / .gz Fast Moderate Default choice, logs, quick backups
bzip2 / .bz2 Slower Better When size matters more than time
xz / .xz Slowest Best Software releases, archival storage

Syntax

The general shape of a tar command is:

tar <mode> [options] -f <archive-file> [file-or-directory ...]
  • -c — create a new archive
  • -x — extract an existing archive
  • -t — list (table of contents) without extracting
  • -r — append files to the end of an existing, uncompressed archive
  • -v — verbose: print each file name as it’s processed
  • -z — filter the archive through gzip (produces/reads .tar.gz)
  • -j — filter through bzip2 (produces/reads .tar.bz2)
  • -J — filter through xz (produces/reads .tar.xz)
  • -f <file> — the archive filename; required for almost every real invocation
  • -C <dir> — change to <dir> before adding/extracting files, so paths inside the archive stay relative
  • -p — preserve exact permissions and ownership on extraction (important for system backups run with sudo)
  • --exclude=<pattern> — skip files matching a glob pattern

And for gzip directly:

gzip [options] <file>
Flag Meaning
-d Decompress (equivalent to running gunzip)
-k Keep the original file instead of deleting it
-v Show the compression ratio achieved
-c Write compressed output to standard output instead of a file
-r Recurse into directories, compressing each file individually
-1 .. -9 Speed vs. ratio trade-off; -1 fastest/worst, -9 slowest/best (default is -6)
-l List size and ratio info for a .gz file without decompressing it

Examples

1. Create a compressed archive of a directory

tar -czvf project-backup.tar.gz ~/projects/website
~/projects/website/
~/projects/website/index.html
~/projects/website/css/
~/projects/website/css/style.css
~/projects/website/js/
~/projects/website/js/app.js

Reading the flags right to left: -f project-backup.tar.gz names the output archive, -z pipes it through gzip, -v prints each file as it’s added, and -c means create. The verbose output confirms exactly which files and directories ended up inside the archive.

2. List an archive’s contents without extracting

tar -tzvf project-backup.tar.gz
drwxr-xr-x user/user 0 2026-08-01 10:02 projects/website/
-rw-r--r-- user/user 512 2026-08-01 09:58 projects/website/index.html
drwxr-xr-x user/user 0 2026-08-01 10:00 projects/website/css/
-rw-r--r-- user/user 231 2026-08-01 09:59 projects/website/css/style.css

Swapping -c for -t lists what’s inside — permissions, owner, size, and modification time for every entry — without writing anything to disk. Always worth doing before extracting an archive you didn’t create yourself.

3. Extract an archive into a specific directory

mkdir -p /tmp/restore
tar -xzvf project-backup.tar.gz -C /tmp/restore
projects/website/
projects/website/index.html
projects/website/css/
projects/website/css/style.css
projects/website/js/
projects/website/js/app.js

-x extracts, and -C /tmp/restore tells tar to change into that directory first, so the archive’s internal paths land there instead of scattering into your current working directory.

4. Compress and decompress a single file directly

gzip -v /var/log/app.log
/var/log/app.log:	 78.3% -- replaced with /var/log/app.log.gz
gunzip -v /var/log/app.log.gz
/var/log/app.log.gz:	 78.3% -- replaced with /var/log/app.log

Notice app.log is gone after the first command and app.log.gz is gone after the second — by default gzip and gunzip replace the file rather than keeping both copies. Use -k on either command if you want to keep the original alongside the compressed version.

How it works step by step

When you run tar -czvf project-backup.tar.gz ~/projects/website:

  1. tar parses the flags and determines the mode is create, the compression filter is gzip, and the output path is project-backup.tar.gz.
  2. It walks the given directory tree, and for each file/directory it encounters, writes a 512-byte header (name, mode bits, owner/group IDs, size, mtime, checksum) into the archive stream.
  3. Immediately after each header, it writes that file’s raw byte content, padded to a multiple of 512 bytes.
  4. Because -z was given, every byte of that stream is fed through zlib’s DEFLATE compressor before hitting the disk, rather than being written uncompressed first and compressed after.
  5. Two 512-byte blocks of zeros mark the end of the archive, and the compressed result is closed and flushed to project-backup.tar.gz.

Extraction reverses this: gunzip-equivalent decompression reconstructs the original byte stream, tar reads each header to learn a file’s name and permissions, creates the file (and any parent directories) at that path relative to the current directory (or -C target), writes its content, and finally applies the recorded permission bits and timestamp with chmod/utime-equivalent syscalls.

Common Mistakes

Extracting into a messy current directory

Running extraction without -C dumps every file relative to wherever you happen to be standing, which is fine for a single self-contained folder but risky for archives whose top level is a pile of loose files:

tar -xzvf project-backup.tar.gz

If you’re not sure what’s inside, list first, then extract into a dedicated directory:

mkdir extracted
tar -xzvf project-backup.tar.gz -C extracted

Compressing files that are already compressed

JPEGs, MP4s, and ZIP files are already compressed internally, so running gzip on them wastes CPU time for little or no gain, and can occasionally make the file slightly larger:

gzip -v vacation-photos.zip
vacation-photos.zip:	 0.0% -- replaced with vacation-photos.zip.gz

Only compress text-like or already-uncompressed data: source code, logs, HTML, CSVs, tar archives of mixed files, and so on.

Unquoted variables in a tar command inside a script

A path or filename containing a space breaks an unquoted variable, because Bash performs word-splitting on unquoted expansions:

tar czvf $backup_name $source_dir
# if source_dir="/home/user/My Documents", tar sees two separate
# arguments: /home/user/My and Documents -- and fails on the second

Always quote variable expansions so the whole value is passed as one argument:

tar czvf "$backup_name" "$source_dir"

Forgetting that gzip deletes the original file

Since gzip file removes file and leaves only file.gz, running it on your only copy of something important without a backup elsewhere is unforgiving if you later need the exact original bytes for a checksum or diff. Use gzip -k when you want to keep both.

Best Practices

  • Use the standard extension for the compressor you chose (.tar.gz/.tgz, .tar.bz2, .tar.xz) so anyone reading the filename knows how to open it.
  • List an unfamiliar archive with -t before extracting it, so you know what paths and how many files you’re about to create.
  • Extract untrusted archives into a fresh, empty directory rather than your current one, in case the archive contains absolute paths or files you didn’t expect.
  • Use --exclude= to keep noise like node_modules, .git, or *.log out of source backups.
  • Pass -p when backing up system files with sudo so ownership and permissions survive the round trip.
  • Reach for xz (-J) instead of gzip when archive size matters more than the time it takes to create it, such as long-term backups.
  • Periodically test that you can actually restore from a backup archive — an archive nobody has successfully extracted is not a backup you can rely on.

Practice Exercises

  1. Create a compressed archive of your home directory’s Documents folder that excludes any *.tmp files, then use -t to confirm none made it in.
  2. Write a small Bash script, backup-etc.sh, that archives /etc into /var/backups/etc-<date>.tar.gz using $(date +%F) for the date, quotes every variable expansion, and is executable with chmod +x before you run it with sudo.
  3. Take an existing .tar.gz file, list its contents, extract it into a new empty directory with -C, and compare the extracted file permissions against the original with ls -l to confirm -p preserved them.

Example backup script

#!/usr/bin/env bash
set -euo pipefail

source_dir="/etc"
backup_dir="/var/backups"
date_stamp="$(date +%F)"
archive_path="${backup_dir}/etc-${date_stamp}.tar.gz"

mkdir -p "$backup_dir"
tar -czpf "$archive_path" -C / "${source_dir#/}"

echo "Backup written to $archive_path"
Backup written to /var/backups/etc-2026-08-04.tar.gz

The script quotes every variable, uses set -euo pipefail so it stops on the first failure instead of silently continuing, and uses -C / with a relative path so the archive stores etc/... instead of an absolute /etc/... path, which makes it safer to extract on another machine without overwriting an unrelated /etc.

Summary

  • tar combines many files and directories into one archive, preserving names, permissions, and timestamps; it does not compress on its own.
  • gzip compresses a single stream of bytes using DEFLATE, and by default replaces the original file with a .gz version.
  • Combine them with tar -czvf archive.tar.gz path to create, tar -tzvf archive.tar.gz to list, and tar -xzvf archive.tar.gz -C dir to extract.
  • bzip2 (-j) and xz (-J) are slower but better-compressing alternatives to gzip’s -z.
  • Always list an archive before extracting it, extract into a dedicated directory, and quote every variable in scripts that build archive names or paths.
  • Use -k with gzip/gunzip when you need to keep the original file alongside the compressed one.