Why age Is a Better Fit Than GPG for Encrypting a Single File
Most people who run gpg -c secrets.tar only want one thing: turn this file into something unreadable, and let me get it back later. GPG will do that, and it will also drag in a keyring, a trust database, a config file and a pile of algorithm choices you never asked for. age does the one thing and stops.
age (written by Filippo Valsorda, with Ben Cox, and pronounced with a hard g) is a small file-encryption tool with a deliberately tiny surface. Here is what that looks like in practice, and where it falls short.
The whole workflow is four commands
Generate a key pair, encrypt to the public half, decrypt with the private half. That is it.
$ age-keygen -o key.txt
Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
$ age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \
-o secrets.tar.age secrets.tar
$ age -d -i key.txt -o secrets.tar secrets.tar.age
The public key is one short string you can paste into a chat or a README. The private key file holds a line starting AGE-SECRET-KEY-1. There is no keyring, no --import, and no "do you trust this key ultimately?" prompt, because there is no trust model to prompt about.
Why that smaller surface matters
GPG's flexibility is where many real-world mistakes come from. The key choices in age are fixed: there is nothing to negotiate, so nothing to misconfigure or downgrade.
- Recipient keys use X25519 for key agreement.
- File contents use ChaCha20-Poly1305, an authenticated cipher.
- Passphrase mode uses scrypt to stretch the passphrase.
- The header carries a MAC, so tampering with the recipient list is detected.
The payload is split into 64 KiB chunks, each authenticated, with the final chunk marked as final. A truncated file therefore fails to decrypt rather than quietly giving you a shorter plaintext. That is the sort of property you want and rarely think to check.
Quick detour: why chunks?
Hang on, why not authenticate the whole file at once? Because then you could not safely output any plaintext until you had read to the very end, which rules out streaming a large file through a pipe. Per-chunk authentication lets age -d stream while still refusing to hand you unauthenticated data. The "this is the last chunk" flag is what stops an attacker chopping the tail off cleanly.
Encrypting to people and to SSH keys
You can pass -r several times, or read recipients from a file with -R. Any of the recipients can decrypt.
$ age -R recipients.txt -o backup.tar.age backup.tar
The neat trick is that age accepts SSH public keys as recipients (ssh-ed25519 and ssh-rsa). Many people already have those distributed, for example on a code host, so you can encrypt to a colleague without any new key exchange.
$ age -R ~/.ssh/id_ed25519.pub -o notes.age notes.txt
$ age -d -i ~/.ssh/id_ed25519 notes.age
Treat that as convenient, not magic. You are relying on the key you fetched really being theirs, and age cannot tell you that. Check the fingerprint through another channel if it matters.
Passphrase mode, with a catch
For the "just lock this file" case, use -p. It prompts for a passphrase, or generates a strong one for you if you leave it blank.
$ age -p -o secrets.tar.age secrets.tar
The catch: age deliberately reads the passphrase from the terminal, not from a flag or an environment variable. That is a sensible refusal, since arguments end up in shell history and process listings. It does mean passphrase mode is awkward in unattended scripts. For automation, use a key pair and keep the identity file with tight permissions. A weak passphrase is still weak, whatever scrypt does for you.
What age does not do
This is the honest bit, because the comparison is not entirely one-sided.
- No signing. age encryption does not tell the recipient who made the file. If you need that, sign separately (minisign, ssh-keygen -Y, or GPG).
- No web of trust, no key servers, no revocation. You distribute public keys yourself.
- No email integration. It is a file tool, not a mail tool.
- No built-in compression, so compress first if you want it.
If you need signed email or long-lived identities with certification chains, GPG is still the tool that exists for that. age is not trying to replace it there.
Hardware keys and other implementations
age has a plugin mechanism, so keys can live on hardware, for example via age-plugin-yubikey. There is also a Rust implementation, rage, which is compatible with the same format, and the reference implementation is a Go library you can import directly.
For the job most of us actually have, encrypting a backup or a secrets file before it leaves the machine, a format you can describe in a page beats one you have to configure carefully. Pick age for that, and keep GPG for the jobs that genuinely need its extras.