Archiving and Compression: tar, gzip, zip
Taking a backup, keeping logs for the long term, or shipping files to another server all involve the same two needs: gathering multiple files into one object, and shrinking its size. On Linux these are two distinct jobs — archiving (packing several files into one file) and compression (reducing size) — usually handled by two different tools that are almost always used together.
This article follows Finding Files and Searching Text and Rewriting Text: sed and awk: once files have been located and cleaned up, they often need to be stored or transferred. It covers creating, inspecting, and extracting an archive with tar; compressing with gzip; when zip is a better fit than tar.gz; and the habit of verifying an archive before you trust it.
Lab Setup
mkdir -p ~/lab/archive/project/{src,logs}
cd ~/lab/archive
printf 'def main():\n pass\n' > project/src/app.py
printf 'level=INFO message=service started\n' > project/logs/app.log
printf 'level=ERROR message=connection timeout\n' >> project/logs/app.log
find project -type f -printf '%p %s bytes\n'
tar: Packing Files into One Archive
The name tar ("tape archive") is a holdover from an era of writing to magnetic tape, but it remains the most widely used archiving tool today. On its own, tar does not compress anything — it only bundles files and directories into one file while preserving the directory structure, permissions, and ownership.
Creating an Archive
-c: create a new archive.-v: verbose, showing which files are being added.-f project.tar: the archive's filename.-fmust be the last flag in a bundled group, immediately followed by the filename.
Viewing Contents Without Extracting
drwxr-xr-x user/user 0 2026-07-29 10:00 project/
drwxr-xr-x user/user 0 2026-07-29 10:00 project/src/
-rw-r--r-- user/user 21 2026-07-29 10:00 project/src/app.py
drwxr-xr-x user/user 0 2026-07-29 10:00 project/logs/
-rw-r--r-- user/user 49 2026-07-29 10:00 project/logs/app.log
-t lists the archive's contents without writing anything to disk. Running this before extracting an archive from an untrusted source is good practice.
Warning
Extracting an archive without inspecting it first can be risky: a path inside the archive that begins with ../ can attempt to write outside the current directory (path traversal). Modern GNU tar rejects absolute paths and paths starting with .. by default and warns about them, but checking an untrusted archive with -tvf first is still worth the habit.
Warning
Extracting as root is riskier than as a normal user. GNU tar restores ownership, timestamps, and — with -p, which is implied when running as root — the original permission bits, including setuid/setgid. A hostile archive can therefore drop a root-owned setuid binary straight onto the system. Extract untrusted archives as an unprivileged user, into an empty directory, only after inspecting them with -tvf.
Extracting an Archive
-x: extract.-C extracted: extract into the given directory instead of the current one. Without-C,tarextracts paths relative to the current directory.
Extracting a Single File
This extracts only the given path rather than the whole archive. The path must match exactly what tar -tvf reported.
Compression: gzip Alongside tar
tar alone does not compress. To shrink the resulting .tar file, pass it through a separate compression tool — usually gzip — either as a second step, or through a flag tar integrates directly, in a single command.
Archiving and Compressing in One Command
The -z flag tells tar to pipe its output through gzip. The extension is conventionally written .tar.gz or shortened to .tgz — this is purely a naming convention; tar does not infer behavior from the extension, only from the -z flag itself.
Extracting a Compressed Archive
-x (extract) and -z (gzip) are used together — tar first runs the equivalent of gunzip, then extracts the archive.
Compressing a Single File: gzip/gunzip
gzip compresses one file and, by default, replaces the original with a .gz-suffixed compressed copy:
app.log.copy no longer exists — gzip compressed it and left app.log.copy.gz in its place. To keep the original file:
-k (keep) leaves the original file in place.
Restoring a compressed file:
Viewing a compressed file's contents without decompressing it to disk:
zcat is equivalent to gunzip -c; it writes the result to standard output instead of a file.
Compression Level: Size versus Speed
gzip -k -9 project/logs/app.log
mv project/logs/app.log.gz project/logs/app.log.max.gz
gzip -k -1 project/logs/app.log
mv project/logs/app.log.gz project/logs/app.log.fast.gz
ls -l project/logs/app.log.max.gz project/logs/app.log.fast.gz
-1 through -9 (default -6) set the compression level: a lower value is faster but compresses less, a higher value is slower but compresses more. The difference is often negligible for small text files, but becomes clear on large log files.
Choosing a Compressor: gzip, bzip2, xz, zstd
gzip is the default, but tar integrates several compressors, each a different point on the speed-versus-ratio curve:
| Flag | Tool | Typical use |
|---|---|---|
-z |
gzip |
Fast, universal, the safe default when compatibility matters |
-j |
bzip2 |
Smaller than gzip but noticeably slower — now largely superseded |
-J |
xz |
Best ratio of the classic set, slowest and most memory-hungry — good for archives written once and downloaded many times (source tarballs, release artifacts) |
--zstd |
zstd |
Near-gzip speed with far better ratios and very fast decompression — the modern default where it is installed |
Extraction mirrors creation (tar -xJf, tar --zstd -xf), but modern GNU tar also auto-detects the compressor on extraction, so a plain tar -xf project.tar.zst usually just works. The rule of thumb: zstd for routine backups where it is available, xz when squeezing the last few percent off a file that will be distributed widely, and gzip when you cannot guarantee the other tools exist on the receiving end.
zip/unzip: A Cross-Platform Alternative
.tar.gz is the standard in the Linux and Unix world, but when an archive needs to go to a Windows user, or when a single file inside an archive needs updating without recompressing everything, the zip format is more convenient.
-r: add a directory recursively.unzip -l: liketar -tvf, lists contents without extracting.
tar.gz versus zip
| Property | tar.gz |
zip |
|---|---|---|
| Preserves Unix permissions and ownership | Yes, fully | Partially, platform-dependent |
| Native support on Windows | No (requires extra software) | Yes |
| Replace one file inside without recompressing everything | No — the whole archive must be rebuilt | Yes, with zip -u |
| Common default in the Linux ecosystem | Yes (backups, packages, source distribution) | Less common, mostly cross-platform cases |
For backups and deployments between Linux servers, tar.gz is the usual choice; for sending a file to an external user, especially on Windows, zip is often preferred.
rsync: Incremental Synchronization
tar and zip both produce a single, complete snapshot every time they run — re-archiving a 50 GB directory to update one changed file still reads and repacks the whole thing. rsync solves a different problem: keeping two directory trees (locally, or across the network) in sync by transferring only the differences.
sudo apt install rsync
mkdir -p ~/lab/archive/backup-target
rsync -avz project/ ~/lab/archive/backup-target/
-a(archive): a shorthand for "recurse into directories and preserve almost everything that matters for a faithful copy" — permissions, ownership, timestamps, symbolic links, and device files. This is what makesrsyncsuitable as a backup tool rather than a plain file copy.-v: verbose, listing which files are transferred.-z: compress data during the transfer itself (useful over a network link, less relevant on local disk).
Running the same command a second time, after touching only one file, shows the difference from tar:
echo 'level=WARN message=disk usage high' >> project/logs/app.log
rsync -avz project/ ~/lab/archive/backup-target/
Only app.log is retransferred — rsync compares file size and modification time (and, if needed, file content via a checksum algorithm) on each side, and sends just the changed data. This is the core reason rsync is the standard tool for incremental backups: a nightly backup of a large filesystem takes seconds after the first run, instead of repeating a full copy every time.
Danger
rsync's --delete flag removes files from the destination that no longer exist in the source, which makes a mistyped source or destination path capable of silently deleting backup data. Always check what a sync would do before running it for real:
-n (also --dry-run) prints exactly what would be transferred and deleted without touching anything. Only drop -n once the dry-run output looks correct, and keep a separate, verified archive (from tar, or a previous rsync run to a different directory) as a rollback path.
The Trailing Slash Matters
The first command copies the project directory itself into the destination, producing backup-target/project/.... The second, with a trailing slash after project, copies the contents of project directly into the destination, producing backup-target/... with no extra project/ level. This distinction is one of the most common sources of rsync mistakes — always check the resulting path with ls after the first run against a new destination.
rsync versus tar for Backups
| Property | tar/tar.gz |
rsync |
|---|---|---|
| Transfers only changed data on repeat runs | No — rebuilds the whole archive | Yes — this is its main purpose |
| Produces a single portable archive file | Yes | No — it mirrors a directory tree |
| Good fit for "point-in-time snapshot to move or store" | Yes | Less direct (though --link-dest can approximate it) |
| Good fit for "keep this backup directory current" | No — repeats full work each time | Yes |
Works over the network directly (user@host:/path) |
Only combined with ssh manually |
Yes, built in |
In practice, many production backup strategies use both: rsync to mirror data efficiently to a backup host or volume, and tar/tar.gz when a single, self-contained archive file needs to be shipped elsewhere or retained as a dated snapshot.
Practical Scenario: Archive, Verify, Retire a Log Directory
Problem: compress project/logs as a backup, confirm the archive is not corrupted, and only then clean up the original logs.
backup_dir=$HOME/lab/archive/backups
mkdir -p "$backup_dir"
stamp=$(date -u +%Y%m%dT%H%M%SZ)
archive="$backup_dir/logs-$stamp.tar.gz"
tar -czf "$archive" -C "$HOME/lab/archive/project" logs
Once the archive exists, verify its integrity without extracting it:
gzip -t "$archive" && echo "gzip integrity OK"
tar -tzf "$archive" > /dev/null && echo "tar structure OK"
gzip -tchecks compression-level integrity (whether the file is corrupted).tar -tzf ... > /dev/nullreads through the archive's internal structure and returns a non-zero status on error.
Only remove the original files once both checks succeed:
if gzip -t "$archive" && tar -tzf "$archive" > /dev/null; then
echo "archive verified: $archive"
else
echo "archive is corrupted, original files kept" >&2
exit 1
fi
Danger
In any script that automatically deletes original files after archiving, verifying the archive's integrity is a required step, not an optional one. A full disk or an interrupted process can leave a half-written archive that gets treated as "successfully created" while the original data has already been removed.
Common Mistakes
- Assuming
tarcompresses on its own — a separate flag such as-z(or a standalone compression tool) is always needed. - Mixing another flag in after
-f(for example,tar -fcv) —-fmust be immediately followed by the filename. - Extracting an untrusted archive directly instead of inspecting it first with
-tvforunzip -l. - Assuming
gzip file.logkeeps the original file — without-k, the original is replaced by the compressed copy. - Deleting original files in a backup script without verifying the archive's integrity first.
- Sending a
.tar.gzto a Windows user with no explanation — it may be harder to open without extra software, which is wherezipfits better.
Exercises
- Compress the
projectdirectory withtar -czvfand compare its size against an uncompressedtar -cvfarchive. - Verify the resulting
project.tar.gzwith-tzfwithout extracting it, then pull onlyproject/logs/app.logout of it. - Compare the file sizes produced by
gzip -k -1andgzip -k -9, and explain the difference. - Archive
projectwithzip -r, inspect it withunzip -l, then compare its size against thetar.gzversion. - Deliberately corrupt a copy of the archive from the scenario above (for example, by truncating its final bytes) and confirm that the verification script correctly detects the problem.
Verification criterion: for each archive, you should be able to explain how you checked its contents before extracting it, and why you chose that particular format (tar.gz or zip).
References
- GNU tar manual: https://www.gnu.org/software/tar/manual/tar.html
- GNU tar: integrated compression: https://www.gnu.org/software/tar/manual/html_node/gzip.html
- GNU Gzip manual: https://www.gnu.org/software/gzip/manual/gzip.html
- Info-ZIP documentation for
zipandunzip: https://infozip.sourceforge.net/ - POSIX: tar: https://pubs.opengroup.org/onlinepubs/9799919799/utilities/tar.html
- rsync manual: https://download.samba.org/pub/rsync/rsync.1