CoursesLinux essentialsArchiving, compression & transfer

Archiving, compression & transfer

tar, gzip, zip, scp, rsync, curl, wget.

Beginner14 min · lesson 8 of 25

Backups, deployments, log collection, shipping a build to a server: nearly all of it comes down to three moves. Bundle a pile of files into one. Squeeze that one file smaller. Move it to another machine, then check that it arrived intact. Linux gives you a small set of tools for each move, and once you know which one does what, they snap together like Lego bricks.

Packing Files With tar

tar (short for 'tape archive', from the days it wrote backups to magnetic tape) is your packing box. It takes a directory full of files and folders and puts them into one file, called a tarball, keeping the folder structure, the timestamps, and the permissions exactly as they were. It does not make anything smaller on its own. It boxes things up so you carry one item instead of a thousand.

The flags read like initials. c is create, x is extract, t is list (think 'table of contents'), v is verbose (print each file as it goes by), f means 'the next word is the filename', and z adds gzip compression on top of the box. So tar czf backup.tar.gz mydir/ reads as create, zipped, to this file. Swap the c for an x and you extract it. The phrase to burn into memory: czf to pack, xzf to unpack.

~/secopslog — bash
$ tar czf logs-backup.tar.gz /var/log/app/
tar: Removing leading `/' from member names

That warning is tar protecting you. It strips the leading / off /var/log/app/ so that when someone extracts the archive, the files land in a var/log/app/ folder wherever they happen to be standing, instead of silently overwriting the real /var/log/app on their system. Before you trust any archive, look inside it. tar tzf lists the contents without unpacking a single byte.

~/secopslog — bash
$ ls -lh logs-backup.tar.gz tar tzf logs-backup.tar.gz | head -3
-rw-r--r-- 1 deploy deploy 2.4M Jul 17 10:30 logs-backup.tar.gz var/log/app/app.log var/log/app/error.log var/log/app/access.log
$ mkdir -p /tmp/restore tar xzf logs-backup.tar.gz -C /tmp/restore # -C extracts into a chosen directory ls /tmp/restore/var/log/app/
access.log app.log error.log

On a modern system you can drop the z when extracting: tar xf backup.tar.gz works whether the archive is gzip, bzip2, or zstd, because GNU tar sniffs the format for you. The security angle matters more than it sounds. An archive is really a set of instructions for where files should go, and a hostile one can carry paths like ../../etc/cron.d/evil that try to climb out of the folder you extract into. GNU tar strips those ../ climbs out by default, so the file lands inside your directory instead of on top of the real /etc, but the habit that keeps you safe is the same one every time: list first, extract into a fresh empty directory with -C, and never unpack an archive from a stranger while you are root.

Squeezing Files Smaller: gzip And zip

tar boxed your files; gzip shrink-wraps them. gzip (GNU zip) takes one file and compresses it in place: big.log becomes big.log.gz, and the original disappears. Text and logs squeeze down beautifully, often to a tenth of their size, because logs repeat themselves over and over. gunzip (or gzip -d) puts it back.

~/secopslog — bash
$ ls -lh big.log gzip big.log ls -lh big.log.gz
-rw-r--r-- 1 deploy deploy 180M Jul 17 09:12 big.log -rw-r--r-- 1 deploy deploy 14M Jul 17 09:12 big.log.gz
gzip eats the original
Plain gzip big.log deletes big.log and leaves only big.log.gz. If you still need the uncompressed copy, use gzip -k big.log (k for keep). People have compressed the only copy of a file and been surprised the original was gone.

zip is the box and the shrink-wrap in one step, and its real job is talking to the rest of the world. A .zip is the archive format Windows and macOS open with a double-click, so it is what you reach for when you send files to someone who is not on Linux. -r tells it to recurse into subfolders.

~/secopslog — bash
$ zip -r site.zip public/
adding: public/ (stored 0%) adding: public/index.html (deflated 62%) adding: public/style.css (deflated 71%) adding: public/logo.png (deflated 3%)

One trap worth naming: a zip file's built-in password is not real protection. The old ZipCrypto scheme is broken, and even the newer AES (Advanced Encryption Standard) option is only as strong as the password you pick and the tool on the other end. If you are guarding anything that actually matters, encrypt it properly with gpg (GNU Privacy Guard) or age, or send it over an encrypted channel, and treat the zip password as a speed bump, not a lock.

Moving Files: scp And rsync

The file exists; now you need it on another machine. scp (secure copy) is the plain option. It copies a file over SSH (Secure Shell, the encrypted tunnel you use to log into remote servers), so the bytes are protected the whole way across the network. The shape is scp source destination, where a remote place is written user@host:/path.

~/secopslog — bash
$ scp config.yml deploy@web-01:/opt/app/ # push a file up to the server scp deploy@web-01:/var/log/app/error.log ./ # pull one down (arguments reversed)
config.yml 100% 512 1.2MB/s 00:00 error.log 100% 43KB 4.1MB/s 00:00

Reverse the two arguments and you pull instead of push. The direction is only a matter of which side carries the user@host: prefix. If you type that same address twenty times a day, write it down once in ~/.ssh/config and give it a short name:

~/.ssh/config
Host web-01
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/deploy_ed25519
Port 22

Now scp file web-01:/opt/app/ and rsync ... web-01:... both already know the address, the user, and which key to use. rsync (remote sync) does the same job as scp but with a brain. Instead of copying everything every time, it compares the two sides and sends only the pieces that changed. Re-sync a 5 GB directory after editing one config file and it moves a few kilobytes, not 5 GB. The everyday form is rsync -avz: a for archive (preserve permissions, timestamps, symlinks, and recurse into folders), v for verbose, z to compress the data while it is in flight.

~/secopslog — bash
$ rsync -avz ./site/ deploy@web-01:/var/www/
sending incremental file list index.html css/style.css sent 3,124 bytes received 133 bytes 6,514.00 bytes/sec total size is 84,209 speedup is 25.86

Two things about rsync will bite you if nobody warns you. First, the trailing slash on the source changes the meaning. rsync -avz ./site/ host:/var/www/ copies the contents of site into /var/www. Drop that slash, rsync -avz ./site host:/var/www/, and rsync copies the folder itself, creating /var/www/site. Same-looking command, very different result. Second, --delete tells rsync to make the destination an exact mirror, which means it removes files on the far end that are not in your source. Aim that at the wrong directory and you can wipe a server's worth of files in one line.

Dry-run before you --delete
Add n for a dry run: rsync -avzn --delete ./site/ host:/var/www/ prints exactly what would be sent and deleted without touching anything. Run that first, every single time, before any sync that deletes. Also mind the SSH fingerprint prompt on a first connection: typing 'yes' without checking it against a fingerprint you trust hands your session to whoever is answering. And know that rsync can also run over its own unencrypted protocol (rsync://); the SSH form shown here is the one that keeps the contents private.

Downloading: curl And wget

Downloading is fetching mail from an address. wget (web get) is the no-fuss delivery driver: hand it a URL (a web address) and it saves the file, shows a progress bar, and will retry and resume a broken download on its own. curl (client URL) runs the same errand plus a full toolkit for talking to web services. On its own it prints the response to your screen; you add flags to shape the request.

~/secopslog — bash
$ wget https://example.com/app-1.4.2.tar.gz
--2026-07-17 10:41:03-- https://example.com/app-1.4.2.tar.gz Resolving example.com... 93.184.216.34 Connecting to example.com|93.184.216.34|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 5242880 (5.0M) [application/gzip] Saving to: 'app-1.4.2.tar.gz' app-1.4.2.tar.gz 100%[===================>] 5.00M 8.20MB/s in 0.6s 2026-07-17 10:41:04 (8.20 MB/s) - 'app-1.4.2.tar.gz' saved [5242880/5242880]
$ curl -s https://api.internal/health # -s hides the progress meter curl -I https://example.com # -I asks for headers only curl -sS -X POST -H 'Content-Type: application/json' -d '{"n":1}' https://api.internal/items
{"status":"ok","version":"1.4.2"} HTTP/2 200 content-type: text/html; charset=UTF-8 content-length: 1256 date: Fri, 17 Jul 2026 10:42:11 GMT {"id":42,"n":1,"created":true}

The flags earn their keep. -s silences the progress meter (paired with -S it will still show real errors), -O saves the download under the file's own remote name, -o name saves it under one you pick, -L follows redirects, -I asks only for the headers, and -X POST -d ... -H ... sends a method, a body, and headers. That last form is why curl is the standard way to poke an API (Application Programming Interface, the address a program exposes for other programs to call) from the terminal.

Check It Before You Run It

Here is where a download turns dangerous, and it is worth slowing down for. wget and curl fetch whatever the URL serves, and the risky move is running it without looking. The infamous one-liner curl https://get.example.com | sudo bash pipes a script straight off the internet into a root shell, sight unseen. If that server has been compromised, or someone is tampering with your connection, you have run their code as the most powerful user on the box. Download to a file. Read it. Check it against a published fingerprint before you trust it.

~/secopslog — bash
$ sha256sum app-1.4.2.tar.gz curl -sO https://example.com/app-1.4.2.tar.gz.sha256 sha256sum -c app-1.4.2.tar.gz.sha256
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 app-1.4.2.tar.gz app-1.4.2.tar.gz: OK

A checksum is a fingerprint of a file. sha256sum (which runs SHA-256, a Secure Hash Algorithm) reads every byte and produces a 64-character string that changes completely if even one byte differs. The project publishes the fingerprint of the genuine file; you compute the fingerprint of what you downloaded; if they match, you got the real thing, unaltered. sha256sum -c does the comparison for you and prints OK or FAILED. Two more habits round this out. Never reach for curl -k or --insecure to make a certificate error go away: that flag turns off the check that you are actually talking to who you think you are, which is the entire point of HTTPS (the encrypted, identity-verified version of web traffic). And prefer a signature (with gpg) over a plain checksum when the project offers one, because a checksum sitting on the same server as the file only proves the file was not corrupted in transit, not that an attacker did not swap both at once.

A Safe Backup-And-Ship, Step By Step
1Bundle
tar czf backup.tar.gz dir/
2Fingerprint
sha256sum backup.tar.gz > backup.sha256
3Transfer
rsync -avz over SSH
4Verify
sha256sum -c on arrival
5Unpack
tar xzf into a fresh dir
Quick check
01You want the contents of your local site folder to land directly inside /var/www on the server (an index.html at the top of site should become /var/www/index.html). Which command does that?
Incorrect — No trailing slash on the source, so rsync copies the folder itself and you end up with /var/www/site/index.html, one level too deep.
Correct — The trailing slash on the source means 'the contents of site', so the files land straight in /var/www.
Incorrect — Still no source slash, so it nests under /var/www/site anyway, and --delete adds destructive mirroring you did not ask for.
Incorrect — The arguments are reversed, so this pulls from the server down into your local site folder, the opposite direction.
02A colleague sends you logs.tar.gz. You want to see what is inside before you unpack anything onto your disk. Which command does that?
Incorrect — x extracts the archive, exactly what you wanted to hold off on until you have looked.
Incorrect — c creates an archive; pointed at an existing file it would overwrite it, not list it.
Correct — t prints the table of contents without unpacking a single byte.
Incorrect — that shows compression ratios, not the list of files inside the archive.
03A project's install page tells you to run curl https://get.example.com/install.sh | sudo bash. Why is this risky, and what is the safer path?
Incorrect — HTTPS protects the bytes in transit but says nothing about whether the code is safe or the server is compromised.
Incorrect — the real hazard is executing unknown code as root, not download speed.
Incorrect — bash reads happily from a pipe; the command actually runs, which is the whole problem.
Correct — if that server is compromised or the connection is tampered with, you run their code as root, so inspect and verify first.

Try this

Work through “Check It Before You Run It” yourself on a sandbox you can throw away, following the commands above in order. Then break one step deliberately and re-run, so you have seen the failure before it finds you.

Takeaway

The trap worth remembering here: gzip eats the original. Check that on your own systems before you need to, because it is cheaper to find on a quiet afternoon than during an incident.

Related