Skip to content
shellhacks.dev
Command Line

How to Use tar to Create and Extract Archives Step by Step

Create, list and extract tar archives with the right flags, choose gzip, xz or zstd compression, exclude files, and extract safely with a target directory.

6 min read
Summarize in AI
Illustration for "How to Use tar to Create and Extract Archives Step by Step": a terminal session, a disk usage gauge, a directory tree.

tar was designed to write files onto tape, and it still bundles a directory tree into one file with ownership, modes, links and timestamps intact. Compression is a separate step that tar calls out to. The examples use GNU tar 1.35; BSD tar, used on macOS, accepts the same common flags. Choosing a compressor is covered in comparing gzip, bzip2, xz and zstd.

Create an archive

Shell
tar -czf site-backup.tar.gz -C /var/www site
tar -cJf site-backup.tar.xz -C /var/www site
tar --zstd -cf site-backup.tar.zst -C /var/www site

-c creates, -f names the file, and -z, -J and --zstd select gzip, xz and zstd. -C /var/www changes directory before archiving, so the stored paths start at site/ instead of var/www/site/. That is the single most useful habit: a clean top-level directory makes the archive pleasant to extract.

List before extracting

Shell
tar -tzf site-backup.tar.gz | head
tar -tvf site-backup.tar.gz | head -3
Text
site/
site/index.php
site/assets/app.css
drwxr-xr-x www-data/www-data 0 2026-11-03 09:10 site/

-t lists names and -v adds modes and owners. Listing first tells you whether the archive has a top-level directory (extracting one without it scatters files into the current directory, the "tar bomb") and whether any path is absolute or contains ... GNU tar strips leading slashes by default and refuses members that escape the target, but it is still wise to look.

Extract into a chosen directory

Shell
mkdir -p /srv/restore
tar -xf site-backup.tar.gz -C /srv/restore
tar -xf site-backup.tar.gz -C /srv/restore site/index.php

GNU tar detects the compression, so -xf is enough for gzip, xz, bzip2 and zstd. Naming members after the archive extracts only those. Without -p, ordinary users get their umask applied to modes, while root restores modes exactly; add --same-owner for non-root tests of ownership behaviour. A failed extraction commonly points to a missing compressor, shown as "tar: Child returned status 127", or to a damaged archive, described in tar not in gzip format.

Exclude, stream and verify

Shell
tar -czf code.tar.gz --exclude='node_modules' --exclude='*.log' -C ~/src project
tar -cf - -C ~/src project | ssh host 'tar -xf - -C /srv/src'
tar -tzf code.tar.gz >/dev/null && echo "archive readable"

Patterns for --exclude are matched against member names and should be quoted so the shell leaves them alone. Writing to - sends the archive to stdout, which makes a copy over SSH preserve ownership and special files; compare it with rsync when updates are repeated. Listing to /dev/null reads the whole archive and fails on corruption, a cheap integrity check.

What tar stores and what it leaves out

Each member in an archive has a header with the path, mode, owner and group ids and names, size, modification time, type and, for links, the target. File contents follow in blocks of 512 bytes. Hard links are stored once, with later names recorded as links to the first, and symbolic links are stored as links and not followed unless -h is given. Sparse files can be stored efficiently with -S. Extended attributes, ACLs and SELinux contexts are kept only when asked for with --xattrs, --acls and --selinux, so a backup meant to restore a system faithfully must pass them, and the restore must pass them too. Without those flags, a restored web server can fail on SELinux denials that the original did not have.

Absolute paths are the other point. GNU tar strips the leading slash when creating, with a warning, and refuses to extract members with .. components or absolute names by default. That protects against an archive that would overwrite system files. The protection is a reason to extract untrusted archives into an empty directory as an unprivileged user, and to list them first.

Incremental and multi-volume backups

GNU tar supports incremental archives through a snapshot file. The first run with --listed-incremental=state.snar makes a full archive and writes the state. Later runs with the same file store only what changed since, so a weekly full and daily incrementals fit in a small amount of storage. Restoring requires extracting the full archive and each incremental in order, with --listed-incremental=/dev/null, and the chain is only as reliable as its weakest member, so verify each one. For most uses today, tools with deduplication and encryption, such as restic and borg, are easier to operate, and tar remains the right format for portable one-off archives and for packaging.

Reproducible archives

Two archives of the same content differ by default because of timestamps, file order and owners. For builds that need identical output, set sorted names, a fixed time, and numeric owners: tar --sort=name --mtime='2026-01-01 00:00Z' --owner=0 --group=0 --numeric-owner -cf out.tar dir. Pipe through gzip -n so the compressor does not store a timestamp. The result hashes the same on any machine with the same input, which lets a checksum prove that a rebuild matches a published artifact.

Differences between tar implementations

GNU tar and bsdtar, the version on macOS and the BSDs, read each other's archives, but options differ. GNU options such as --exclude-vcs, --transform and --listed-incremental do not exist in bsdtar, which has -s for substitution. macOS tar also stores extended attributes as extra ._ files unless told otherwise, with COPYFILE_DISABLE=1, which then shows up as strange files on Linux. A script that must run on both should keep to the options in the POSIX specification, or check the version at the start and fail with a clear message.

Extracting safely and recovering from damage

Treat an archive from outside as input to be checked. List it first and look for absolute paths, parent references and unexpected top-level names. Extract into a new empty directory, not a populated one, and as a normal user, so that setuid files in the archive are not created with owner privileges; GNU tar drops the setuid bits for non-root users, but root restores them with -p. Add --no-same-owner when extracting as root, to avoid creating files owned by ids from another system.

A corrupt compressed archive is not always lost. Because tar is a sequence of blocks, gzip -dc broken.tar.gz | tar -xvf - extracts everything before the damage, and tools such as zstd and xz can sometimes be told to continue past errors. Keep a checksum next to every archive so that damage is detected at once and not on the day of a restore, and test a restore regularly, as a backup that has never been restored is an untested assumption.

Where this does not apply

tar has no built-in encryption and no random-access index, so pulling one file out of a large compressed archive reads everything before it. Archives made on one system and extracted on another may need --xattrs and --acls to carry extended attributes, and sparse files need --sparse. For a Windows recipient, a zip is friendlier, covered in zip and unzip. Options are in the GNU tar manual and the POSIX pax specification, which defines the archive formats.

Taggedtar
Share

Keep reading

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.