How to Create and Extract tar and gz Files in Linux
Archiving and compression are two separate jobs on Linux:
tarbundles files and directories into one archive and keeps paths, permissions, ownership and timestamps. On its own it does not compress anything.gzip,xz,zstdandbzip2compress a single file or stream.
A .tar.gz (or .tgz) file is a tar archive that went through gzip. A plain .gz file is one compressed file, not an archive.
Create a tar archive
tar -cf archive.tar my-dir/ # no compression
tar -czf archive.tar.gz my-dir/ # gzip
tar -cJf archive.tar.xz my-dir/ # xz: smaller, slower
tar --zstd -cf archive.tar.zst my-dir/ # zstd: fast and small
tar -caf archive.tar.zst my-dir/ # -a picks compression from the extension
tar -czf backup.tar.gz notes.txt config/ logs/ # several sources
The letters: c create, f archive file name, z gzip, J xz, j bzip2, v list files as they are processed, a choose compression by extension. f takes the next word as the archive name, so keep it last in the group: -czf name.tar.gz, not -cfz.
--zstd needs GNU tar 1.31 or newer. On older systems such as RHEL 8, use tar -I zstd -cf archive.tar.zst my-dir/.
Use relative paths
sudo tar -czf /backup/my-site-$(date +%F).tar.gz -C /var/www my-site
-C /var/www changes into that directory first, so the archive contains my-site/... instead of the full path. If you pass absolute paths, tar strips the leading / and prints Removing leading '/' from member names, so extraction always lands under the current directory (or the one given with -C).
Exclude files
tar -czf my-app.tar.gz --exclude='node_modules' --exclude='.git' --exclude='*.log' my-app/
Put --exclude options before the paths they should apply to. Newer GNU tar treats them as positional.
Permissions and ownership
tar always stores owner, group and mode. When root extracts an archive, ownership and permissions are restored. When a regular user extracts it, files belong to that user and the umask applies unless you add -p. For system backups add --acls --xattrs (and --selinux on the RHEL family) when those attributes matter, and --numeric-owner when restoring on a machine where user names map to different IDs.
List and extract
tar -tf archive.tar.gz # list contents
tar -tvf archive.tar.gz # with permissions, owners, sizes
tar -xf archive.tar.gz # extract into the current directory
tar -xf archive.tar.gz -C /opt/my-app # extract into a directory (must exist)
tar -xf archive.tar.gz my-dir/config.yml # extract a single file
tar -xf my-app-1.4.2.tar.gz --strip-components=1 -C /opt/my-app
GNU tar detects the compression when reading a file, so tar -xf works for .tar.gz, .tar.xz, .tar.bz2 and .tar.zst alike. It still needs the matching program installed (xz, zstd, bzip2). -z on extraction is harmless but unnecessary.
List an archive before extracting one you did not create. Some archives have no top-level directory and spill hundreds of files into the current directory. --strip-components=1 drops the first path component, which is useful for release tarballs that wrap everything in my-app-1.4.2/.
Compress a single file with gzip
gzip access.log # creates access.log.gz, deletes the original
gzip -k access.log # keep the original
gzip -9 dump.sql # best compression (1 fastest, 6 default)
gzip -c dump.sql > /backup/dump.sql.gz # write to stdout, keep the source
pg_dump my_db | gzip > my_db.sql.gz # compress a stream
gzip does not handle directories. gzip -r dir/ compresses every file inside separately, which is rarely what you want; use tar -czf for a directory.
Open a .gz file
gunzip access.log.gz # restores access.log, deletes the .gz
gzip -dk access.log.gz # decompress, keep the .gz
zcat access.log.gz | less # read without writing to disk
zless access.log.gz
zgrep 'ERROR' access.log.gz
gzip -l access.log.gz # sizes and ratio
gzip -t access.log.gz # test integrity
With gzip older than 1.12 (RHEL 8, Ubuntu 22.04), gzip -l shows a wrong uncompressed size for files of 4 GiB or more, because the header stores it modulo 4 GiB. gzip 1.12 and later get the real size by decompressing the file, so -l is slow on big archives.
Other formats
| Extension | Decompress | Notes |
|---|---|---|
.gz | gunzip | available everywhere, fast |
.bz2 | bunzip2 | older, slow, still common |
.xz | unxz | high ratio, slow to compress |
.zst | unzstd | fast with a good ratio |
.zip | unzip | best for sharing with Windows users |
.tar.* | tar -xf | compression detected automatically |
gzip uses one CPU core. For big archives use a multi-threaded compressor:
tar -I 'zstd -T0' -cf backup.tar.zst /srv/data # zstd on all cores
tar -I pigz -cf backup.tar.gz /srv/data # parallel gzip, if pigz is installed
xz -T0 big.img # xz on all cores
For zip files (install zip and unzip if missing):
zip -r archive.zip my-dir/
unzip -l archive.zip # list
unzip archive.zip -d target/ # extract into target/
Verify, split and encrypt
sha256sum backup.tar.gz > backup.tar.gz.sha256
sha256sum -c backup.tar.gz.sha256
tar -tzf backup.tar.gz > /dev/null && echo OK # reads the whole archive
split -b 1G backup.tar.gz backup.tar.gz.part- # 1 GiB pieces
cat backup.tar.gz.part-* > backup.tar.gz # join them again
gpg --symmetric --cipher-algo AES256 backup.tar.gz # creates backup.tar.gz.gpg
gpg -o backup.tar.gz -d backup.tar.gz.gpg
Listing the archive with tar -t reads it end to end, so a truncated upload or a broken disk shows up as an error before you need the backup.
Quick reference
tar -czf out.tar.gz dir/ # create gzip archive
tar -cJf out.tar.xz dir/ # create xz archive
tar --zstd -cf out.tar.zst dir/ # create zstd archive
tar -czf out.tar.gz -C /path dir # store relative paths
tar -tf archive.tar.gz # list
tar -xf archive.tar.gz # extract here
tar -xf archive.tar.gz -C /target # extract elsewhere
gzip -k file # compress, keep original
gunzip file.gz # decompress
zcat file.gz | less # read compressed text
unzip archive.zip -d target/ # zip files
