You download a project dump, type tar -xzf in the folder you already work in, and src/readme.txt is suddenly last week’s copy. Or the extract “succeeds” from a truncated file and you spend an hour debugging a tree that was never whole. tar packs a directory into one file; gzip shrinks that pack. The safe habit is the same either way: inspect the archive, then extract into a directory you chose.

You do not need every GNU tar switch on day one. Start by packing a small tree, list what is inside, restore it somewhere empty, then add compression tests and excludes as the job gets real. The sections below walk through the commands you will reach for most often — with enough context that each one feels intentional, not magical.

tar is a point-in-time snapshot. If you need an incremental live mirror (including --delete), that is rsync, not another tar flag.

Warm-up: build a small playground

Give yourself a source tree you can pack, list, and restore without touching real projects. The layout mirrors a tiny app: nested folders, a noisy node_modules you will skip later, and a repetitive log so compression has something to chew on.

Create it once, then reuse it for every local example in this article:

rm -rf tar-demo
mkdir -p tar-demo/src/{app,docs,node_modules/pkg,logs}
echo 'hello' > tar-demo/src/readme.txt
echo 'notes' > tar-demo/src/docs/guide.txt
echo 'main' > tar-demo/src/app/index.js
echo 'dep' > tar-demo/src/node_modules/pkg/index.js
printf 'error timeout retry\n%.0s' {1..400} > tar-demo/src/logs/app.log

Confirm the files exist before you archive them:

find tar-demo/src -type f | sort
tar-demo/src/app/index.js
tar-demo/src/docs/guide.txt
tar-demo/src/logs/app.log
tar-demo/src/node_modules/pkg/index.js
tar-demo/src/readme.txt

How big is the tree on disk? A single du summary is enough for this playground:

du -sh tar-demo/src

Create a tar, then a tar.gz

-c creates, -f names the archive. Pack the src directory as it sits under tar-demo, without dragging tar-demo/ itself into the file list. -C changes directory first, then the path you name is relative to that:

tar -cf tar-demo/project.tar -C tar-demo src

Members inside the archive now start with src/…. That wrapping folder is useful later: extract will not dump readme.txt straight into whatever directory you happen to be in.

Same tree, compressed in one step. -z runs gzip while creating. The usual Linux filename is .tar.gz (you will also see .tgz):

tar -czf tar-demo/project.tar.gz -C tar-demo src

Compare the two files. The uncompressed archive is roughly “the files plus tar headers.” gzip then shrinks the repetitive log. Exact sizes vary by filesystem block size; the .tar.gz should be the smaller of the two:

ls -lh tar-demo/project.tar tar-demo/project.tar.gz
-rw-r--r-- 1 you you  20K Sep 11 10:00 tar-demo/project.tar
-rw-r--r-- 1 you you  400 Sep 11 10:00 tar-demo/project.tar.gz

gzip here is the everyday compressor. The bytes it writes are DEFLATE — the pairing of a sliding window with Huffman codes is covered in LZ77 and DEFLATE. This post stays on the commands. You may also meet .tar.xz on distro downloads; the inspect-then-extract habit is the same.

Note: Clustered flags still obey -f: the next argument is the archive name. tar -cfz project.tar.gz src treats z as the output filename. Prefer -czf archive.tar.gz (or put -f last: tar -cz -f archive.tar.gz).

To skip one directory while creating, pass --exclude before the path list. This drops node_modules anywhere in the tree:

tar -czf tar-demo/lean.tar.gz --exclude='node_modules' -C tar-demo src

List before you extract

-t lists members. It does not write files. Make this the first command you run on any archive you did not just create yourself — a download, a teammate’s dump, last night’s snapshot.

Uncompressed:

tar -tf tar-demo/project.tar

gzip-compressed (the z matches how you created it):

tar -tzf tar-demo/project.tar.gz

You should see the wrapping src/ prefix on every path. GNU tar lists members in archive order (how it walked the tree), not alphabetically:

src/
src/logs/
src/logs/app.log
src/docs/
src/docs/guide.txt
src/node_modules/
src/node_modules/pkg/
src/node_modules/pkg/index.js
src/readme.txt
src/app/
src/app/index.js

Verbose listing adds sizes and timestamps (-tvf / -tzvf). Confirm two things: there is a top-level folder, and you are not about to restore into a live tree that already uses those names.

Check the lean archive the same way — node_modules should be gone:

tar -tzf tar-demo/lean.tar.gz

Note: Habit: tar -tzf file.tar.gz (or -tf) before any -x. Listing is how you catch a “tar bomb” (files at the archive root with no wrapping directory) and how you notice you are standing in a directory that already has a src/.

Extract into a directory you chose

-x extracts. Combined with -C, GNU tar writes into a target directory instead of the current working directory. Create an empty restore folder first so you can see exactly what landed:

mkdir -p tar-demo/restore
tar -xzf tar-demo/project.tar.gz -C tar-demo/restore

Confirm the tree came back under restore/src/:

find tar-demo/restore -type f | sort
tar-demo/restore/src/app/index.js
tar-demo/restore/src/docs/guide.txt
tar-demo/restore/src/logs/app.log
tar-demo/restore/src/node_modules/pkg/index.js
tar-demo/restore/src/readme.txt

Uncompressed extract is the same shape without z: tar -xf tar-demo/project.tar -C tar-demo/restore.

If you want the contents of src/ directly in the target (no extra src/ wrapper), strip one path component:

mkdir -p tar-demo/flat
tar -xzf tar-demo/project.tar.gz -C tar-demo/flat --strip-components=1
find tar-demo/flat -type f | sort
tar-demo/flat/app/index.js
tar-demo/flat/docs/guide.txt
tar-demo/flat/logs/app.log
tar-demo/flat/node_modules/pkg/index.js
tar-demo/flat/readme.txt

GNU tar overwrites existing files of the same name by default. Extracting project.tar.gz in a project that already has src/ replaces those files. --keep-old-files (-k) refuses to replace them; that is a seatbelt, not a substitute for -C into an empty folder.

Note: Extract into the current directory can overwrite a live tree. Inspect with -t first, then restore with -C into a directory you created for that job. Do not run -x “to see what is inside.”

A nightly snapshot is still just tar -czf plus a destination path. Scheduling that job belongs with cron and systemd timers, not with extra tar flags.

Standalone gzip and integrity checks

tar -z is gzip wrapped around a tar stream. You can also compress a single file with gzip itself. Without -k, gzip replaces the original with a .gz sibling — use a copy so you do not collide with project.tar.gz from tar -czf:

cp tar-demo/project.tar tar-demo/standalone.tar
gzip tar-demo/standalone.tar

standalone.tar is gone; standalone.tar.gz remains. Keep the original with -k:

cp tar-demo/project.tar tar-demo/kept.tar
gzip -k tar-demo/kept.tar

That leaves both kept.tar and kept.tar.gz. Decompress with gunzip (or gzip -d). -k again keeps the .gz:

gunzip -k tar-demo/standalone.tar.gz

Before you extract a download, ask gzip whether the compressed stream is intact. -t tests and prints nothing on success (exit code 0):

gzip -t tar-demo/project.tar.gz && echo ok
ok

A truncated or corrupt .gz fails the test. Listing through tar is a second check that the tar stream after decompression is readable:

tar -tzf tar-demo/project.tar.gz >/dev/null && echo tar-ok

Note: gzip -t validates the compressor wrapper. tar -tzf walks archive members. Run both on a file you did not create — a half-downloaded .tar.gz can look plausible in ls and still be junk.

Quick reference card

Keep this nearby until the flags become muscle memory:

GoalCommand
Create tartar -cf archive.tar -C parent dir
Create tar.gztar -czf archive.tar.gz -C parent dir
List tartar -tf archive.tar
List tar.gztar -tzf archive.tar.gz
Extract to a directorytar -xzf archive.tar.gz -C dest/
Strip leading pathtar -xzf archive.tar.gz -C dest/ --strip-components=1
Exclude a directorytar -czf a.tar.gz --exclude='node_modules' -C parent dir
Do not overwritetar -xkzf archive.tar.gz -C dest/
gzip one file (replace)gzip file
gzip, keep originalgzip -k file
Test gzip integritygzip -t file.gz
Decompress, keep .gzgunzip -k file.gz

Practice drills

Use the tar-demo trees (recreate them from the warm-up if needed) and try these without peeking. The point is to choose the flags with intent, not to memorize every switch under pressure.

  1. Create an uncompressed archive of src at tar-demo/drill.tar without putting tar-demo/ inside the archive.
  2. List that archive and confirm every member starts with src/.
  3. Create tar-demo/drill.tar.gz of the same tree, excluding node_modules.
  4. Extract the gzip archive into a new folder tar-demo/out/ (not into the current directory).
  5. Copy drill.tar.gz to tar-demo/check.tar.gz and prove it is intact with gzip -t.

When you are ready to compare, here are solid answers — not the only ones, but clear and portable:

tar -cf tar-demo/drill.tar -C tar-demo src
tar -tf tar-demo/drill.tar
tar -czf tar-demo/drill.tar.gz --exclude='node_modules' -C tar-demo src
mkdir -p tar-demo/out
tar -xzf tar-demo/drill.tar.gz -C tar-demo/out
cp tar-demo/drill.tar.gz tar-demo/check.tar.gz
gzip -t tar-demo/check.tar.gz && echo ok

If you can work through those five comfortably, you already cover most real tar and gzip work: pack a tree, shrink it, look inside before you write, restore into a directory you chose, skip noise, and reject a corrupt download. Start with -czf / -tzf / -xzf -C dest, add --exclude and --strip-components when the listing says you need them, and treat extract-in-place as the last option, not the first.