▶Prefer to watch? This short video runs this page's main commands on a real Ubuntu 24.04 machine, with on-screen captions and no voiceover. Click to play, or watch it on YouTube.
tar [options] [ARCHIVE] [FILE...]
tar -czf backup.tar.gz ./project
tar bundles many files into one archive (a "tarball"). It only bundles;
compression (gzip, bzip2, xz, zstd) is a flag on top.
The three modes (pick exactly one)
-c Create a new archive
-x Extract files from an archive
-t List the contents of an archive
-r Append files to an existing (uncompressed) archive
-u Append only files newer than the archive copy
-d Compare the archive with the filesystem (also --diff)
Common flags
-f FILE Archive file name (must come right after the flags, e.g. -czf name)
-v Verbose: list files as they are processed
-z gzip compression (.tar.gz, .tgz)
-j bzip2 compression (.tar.bz2, .tbz2)
-J xz compression (.tar.xz, .txz)
--zstd zstd compression (.tar.zst)
-a Pick compression from the file extension (create)
-C DIR Change to DIR before doing anything
-p Preserve permissions (default when extracting as root)
-P Keep absolute paths (leading /) - use with care
-h Follow symlinks and archive the files they point to
Memory aid
c = create x = extract t = list z = gzip f = file v = verbose
tar -czvf "compress, zip, verbose, file"
tar -xzvf "extract, zip, verbose, file"
Create archives
tar -cf files.tar file1 file2 dir/ # plain tar, no compression
tar -czf backup.tar.gz ./project # gzip
tar -cjf backup.tar.bz2 ./project # bzip2
tar -cJf backup.tar.xz ./project # xz (smaller, slower)
tar -caf backup.tar.zst ./project # -a picks compression from the name
tar -czvf backup.tar.gz ./project # verbose: show each file added
tar -czf "backup-$(date +%F).tar.gz" ./project # date-stamped name
tar -czf site.tar.gz -C /var/www html # store as html/..., not /var/www/html/...
Extract archives
tar -xf archive.tar # modern tar detects compression
tar -xzf archive.tar.gz # gzip
tar -xjf archive.tar.bz2 # bzip2
tar -xJf archive.tar.xz # xz
tar -xzvf archive.tar.gz # verbose
tar -xzf archive.tar.gz -C /opt/app # extract into a directory (must exist)
tar -xzf archive.tar.gz path/in/archive.txt # extract one file
tar -xzf archive.tar.gz --wildcards '*.conf' # extract by pattern
tar -xzf archive.tar.gz --strip-components=1 # drop the top-level folder
tar -xzf archive.tar.gz --one-top-level # extract into a folder named for the archive
List and inspect
tar -tf archive.tar.gz # list files
tar -tvf archive.tar.gz # long listing: perms, owner, size, date
tar -tf archive.tar.gz | head # peek at the first few entries
tar -tf archive.tar.gz | grep -c . # count entries
tar -xOf archive.tar.gz path/file.txt # print one file to the screen (-O = stdout)
Exclude and include
tar -czf out.tar.gz --exclude='*.log' ./project
tar -czf out.tar.gz --exclude='node_modules' --exclude='.git' ./project
tar -czf out.tar.gz --exclude-vcs ./project # skip .git, .svn, etc.
tar -czf out.tar.gz -T filelist.txt # read names to archive from a file
tar -czf out.tar.gz --exclude-from=skip.txt ./project
Add and update
tar -rf files.tar newfile.txt # append (uncompressed archives only)
tar -uf files.tar file1 # update only if newer
tar --delete -f files.tar oldfile.txt # remove a member (uncompressed only)
Pipelines and remote copies
tar -czf - ./project | ssh user@host 'cat > backup.tar.gz'
tar -czf - ./project | ssh user@host 'tar -xzf - -C /srv' # copy a tree over SSH
ssh user@host 'tar -czf - /etc' > etc-backup.tar.gz # pull a remote backup
tar -cf - ./src | (cd /dest && tar -xf -) # copy a tree, keeping perms
curl -sL https://example.com/app.tar.gz | tar -xz # download and unpack
tar -czf - ./project | split -b 100M - part_ # split into 100MB chunks
cat part_* | tar -xzf - # rejoin and extract
tar -cf - ./data | pv | gzip > data.tar.gz # progress bar (needs pv)
Incremental backups (GNU tar)
tar -czf full.tar.gz --listed-incremental=snap.file ./data # level 0 (full)
tar -czf inc1.tar.gz --listed-incremental=snap.file ./data # only changes since
tar -xzf full.tar.gz --listed-incremental=/dev/null # restore in order
Practical one-liners
# Back up a home directory, skipping caches
tar -czf "home-$(date +%F).tar.gz" --exclude='.cache' --exclude='Downloads' ~
# See what is inside without extracting
tar -tzf archive.tar.gz | less
# Extract a tarball whose top folder you do not want
tar -xzf project-1.2.3.tar.gz --strip-components=1 -C ~/src/project
# Archive files modified in the last day
find . -mtime -1 -type f -print0 | tar -czf recent.tar.gz --null -T -
# Preserve owner, permissions and extended attributes (run as root)
tar --xattrs --acls -czpf system.tar.gz /etc
# Verify an archive is readable
tar -tzf archive.tar.gz > /dev/null && echo OK
# Compare an archive with what is on disk
tar -dzf archive.tar.gz -C /
# Handle a filename that starts with a dash
tar -czf out.tar.gz -- -weirdname.txt
Compression comparison
gzip (-z) Fast, good compression, universal .tar.gz .tgz
bzip2 (-j) Slower, smaller than gzip .tar.bz2
xz (-J) Slowest, smallest, great for releases .tar.xz
zstd Very fast, good ratio, modern .tar.zst
Exit codes
0 - success
1 - some files changed while being archived (warning, archive is usually fine)
2 - fatal error (missing file, bad archive, permission)
Tips
tar strips the leading / from paths when creating, so extracting will not overwrite system files by surprise. -P turns that off.
Old tar needs the compression flag when extracting (-z, -j, -J). Modern GNU tar auto-detects, so plain -xf works.
Put -f last in a bundle of flags (-czf name, not -cfz name) because it takes the next word as the file name.
Pointing -f at - means stdin or stdout, which is how tar fits into pipelines.
Always list with -t before extracting an unknown archive. Look for absolute paths or ../ entries.
Archive with a top-level folder (tar -czf x.tar.gz project/) so extracting does not spill files into the current directory.
Wildcards in --exclude are matched by tar, so quote them. Wildcards in file lists are matched by the shell.
bsdtar (macOS) differs slightly from GNU tar: --wildcards, --one-top-level and --listed-incremental are GNU-only.