How to Use Age: The Beginner's Guide (First Keypair, the Recipient Lesson, Passphrase Mode, and the SOPS Hand-Off)
CodexFuse Team · 2026-11-01T23:19:58.626Z · 15 min read min read
Learn age, the modern file-encryption CLI: install it, make a keypair, encrypt to a recipient — plus passphrase mode, SSH keys, and the SOPS hand-off.
How to Use Age: The Beginner's Guide (First Keypair, the Recipient Lesson, Passphrase Mode, and the SOPS Hand-Off)
If you search "how to use age" you mostly get English-learner lessons about saying how old you are. This guide is about the other age: age (always lowercase, pronounced with a hard g, like GIF), the modern command-line tool for encrypting files, created by Filippo Valsorda. Learning how to use age file encryption is a single sitting of work: you generate a keypair, encrypt to a public recipient, and decrypt with your private identity. If you came here looking for an age encryption tutorial that starts from zero and ends with encrypted backups, you are in the right place. No keyrings, no web of trust, no certificate wizard.
The mental model is the whole tool. You encrypt to a public RECIPIENT; you decrypt with your private IDENTITY. The recipient can be shared freely. The identity file must be guarded like a password. If you only remember one sentence from this guide, remember that one.
A lane to draw before we start: age is a command-line tool for encrypting files. If you need to encrypt a short piece of text right in the browser instead, the AES encryption tool handles that lane — no install, paste-and-go. This guide is for developers who want file encryption from the terminal, the kind that scripts, backups, and servers can use.
What Is Age, Exactly?
Age is a small, modern file-encryption program with a deliberately tiny feature set. It encrypts files for one or more recipients or for a passphrase, and it decrypts them with the matching identity or passphrase. It was designed as the anti-GPG: where GPG accumulated decades of options, formats, and sharp edges, age does one thing — files in, encrypted files out — with modern cryptography underneath and a command line you can learn in minutes.
The file format is an open spec published at age-encryption.org, and the reference implementation is written in Go with implementations in Rust (rage) and TypeScript (Typage) following the same spec. The default recipient type is X25519, and since version 1.3.0 age also supports post-quantum recipients built on hybrid ML-KEM-768. For a beginner, the version that matters is simply the latest one in your package manager.
How Do I Install Age?
Install age from your platform's official package source. The project's README documents the full table; the common routes are:
| Platform | Command |
|---|---|
| macOS (Homebrew) | brew install age |
| macOS (MacPorts) | port install age |
| Debian 12+ / Ubuntu 22.04+ | apt install age |
| Arch Linux | pacman -S age |
| Fedora 33+ | dnf install age |
| Alpine | apk add age |
| Windows (winget) | winget install --id FiloSottile.age |
| Windows (Chocolatey) | choco install age.portable |
| Windows (Scoop) | scoop bucket add extras && scoop install age |
| FreeBSD | pkg install age |
| Anywhere (prebuilt binary) | https://dl.filippo.io/age/latest?for=linux/amd64 |
| Anywhere (Go toolchain) | go install filippo.io/age/cmd/...@latest |
The install gives you two binaries you will actually use: age (encrypt and decrypt) and age-keygen (generate keys). Version 1.3.0 also added age-inspect, a small utility that shows metadata about an encrypted file without decrypting it. At the time of writing, the latest release is v1.3.2, a hardening release that rejects oversized headers and malformed SSH keys in recipient files.
How Do I Generate My First Age Keypair?
Run age-keygen and save the output to a file. That is the whole ceremony:
$ age-keygen -o key.txt
Public key: age1ql3z7hj432v2jl2z8alunwwun8hm4s4h6lxu2uqq7e9yqr8e8a2s0a3kq0
The terminal prints your public key — the recipient, starting with age1... — and the file key.txt now holds your secret key, the identity, on a line starting with AGE-SECRET-KEY-1..., preceded by comment lines recording when it was created and which public key it belongs to. Anyone can encrypt to the recipient. Only the holder of the identity file can decrypt.
The identity file contains your secret key, so treat it like a password: set restrictive permissions (chmod 600 key.txt), back it up somewhere safe, and never commit it to a repository. If you are new to Git workflows, the Git beginner guide covers the ignore-file habits that keep secrets out of commits. If age-keygen notices you are writing a secret key to a world-readable file, it warns you with "age-keygen: warning: writing secret key to a world-readable file" — do not ignore that warning.
Two conveniences worth knowing now: age-keygen -y key.txt reprints the public key for an identity file (handy when you lose track of which recipient goes with which file), and age-keygen -pq generates a post-quantum keypair instead, whose recipient starts with age1pq1... and whose identity starts with AGE-SECRET-KEY-PQ-1....
How Do I Encrypt a File to an Age Recipient?
Encrypt with -r naming the recipient, and decrypt with -d -i naming your identity file. The two commands are mirror images:
$ age -r age1ql3z7hj432v2jl2z8alunwwun8hm4s4h6lxu2uqq7e9yqr8e8a2s0a3kq0 \
-o secrets.txt.age secrets.txt
$ age -d -i key.txt -o secrets.txt secrets.txt.age
Encrypting and decrypting are the spine of age, so here is the recipient lesson in full. The -r flag takes a public recipient — the age1... string you share. The -d flag switches to decrypt mode, and -i points at your identity file, which holds the secret key. Input defaults to stdin and output defaults to stdout when you do not give file arguments, so age composes with pipes; when you do name an output file that already exists, age overwrites it.
Three variations beginners reach for quickly. Repeat -r to encrypt for multiple recipients — useful when two teammates both need access. Pass -R with a file of recipients (one per line) instead of listing them on the command line. And run age-inspect secrets.txt.age to see metadata — recipient types, post-quantum status, armor status, payload size — without decrypting, with --json if a script needs to parse it. Encryption overhead is small: roughly 200 bytes per recipient plus 16 bytes per 64KiB of plaintext.
How Does Age's Passphrase Mode Work?
Passphrase mode is age's second mode, and it exists for the case where there is no recipient key — encrypting a backup for yourself, or handing someone a file with a shared secret. Use -p:
$ age -p secrets.txt > secrets.txt.age
Enter passphrase (leave empty to autogenerate a secure one):
Age prompts for a passphrase, and if you leave it empty it autogenerates a strong one from a word list — something like release-response-step-brand-wrap-ankle-pair-unusual-sword-train. This is age passphrase encryption in its simplest form: one flag, one prompt, one encrypted file. Decryption is automatic: age -d secrets.txt.age detects the passphrase encryption and prompts "Enter passphrase:". One gotcha to memorize: since version 1.1.0, passing -i with a passphrase-encrypted file is rejected outright. Recipients and passphrases are two different modes; the tool will not let you mix them up.
If you need passphrase encryption inside scripts where no one can type at a prompt, the ecosystem has an answer: the batchpass plugin takes the passphrase from an environment variable for non-interactive use. Keep that plugin out of interactive workflows — a passphrase in the environment is visible to other processes — but it is the correct tool for automated jobs.
Can I Use My SSH Keys With Age?
Yes — the age ssh key workflow is one of the tool's best features: age speaks SSH keys, and this is the feature that makes it click for developers. You already have SSH keys; age lets you encrypt files to them without managing a second key type. Encrypt to an SSH public key with -R or a raw key string, and decrypt with the private key via -i:
$ age -R ~/.ssh/id_ed25519.pub -o backup.tar.gz.age backup.tar.gz
$ age -d -i ~/.ssh/id_ed25519 backup.tar.gz.age > backup.tar.gz
The recipient form accepts ssh-ed25519 AAAA... and ssh-rsa AAAA... key strings directly. Since version 1.2.0, age also accepts PKCS#8-encoded Ed25519 private keys as identities, which covers keys exported from password managers. A party trick from the README shows the shape of it: curl https://github.com/benjojo.keys | age -R - example.jpg > example.jpg.age encrypts a file to someone's published GitHub keys. If you have not set that flow up yet, the GitHub beginner guide walks through keys and remotes from zero.
Three caveats, each worth one sentence. Age does not talk to ssh-agent — point -i at the key file itself. SSH keys held on YubiKeys cannot decrypt age files, because the YubiKey will not perform the operation age needs. And the README is honest that SSH support uses more complex cryptography and embeds a public key tag in the encrypted file, which is a small trackability tradeoff compared to native age keys.
How Do I Encrypt a Whole Directory With Age?
Age encrypts files, not directories — and that is a deliberate design choice, not a missing feature. Searches for "age encrypt directory" all land on the same answer: to encrypt a folder, pipe it through tar first, exactly as the official examples show:
$ tar cvz ~/data | age -r age1ql3z7hj432v2jl2z8alunwwun8hm4s4h6lxu2uqq7e9yqr8e8a2s0a3kq0 \
> data.tar.gz.age
This is the UNIX-philosophy shape of the tool: tar handles archiving, age handles encryption, the pipe connects them. Decrypt the other way — age -d -i key.txt data.tar.gz.age | tar xvz — and the directory comes back out. If you ever find yourself wishing age took a directory argument, remember the pipe: it is the intended answer, not a workaround.
One armor-mode note belongs here. Add -a (or --armor) when you need the encrypted output to be ASCII text — for pasting into a ticket or an email. Armor produces a strict PEM block typed AGE ENCRYPTED FILE with canonical Base64 and no headers, and decryption detects it transparently. For files on disk, skip armor; binary output is smaller.
What Are Age Plugins and Should I Use a Hardware Key?
Plugins are how age reaches hardware security keys without changing the core tool. When you pass a recipient like -r age1yubikey1..., the age CLI finds age-plugin-yubikey on your PATH and lets it handle the key operation. Plugins can be mixed freely with native recipients on the same file, and they manage their own key generation, so your secret never touches the age binary.
The stable plugin is age-plugin-yubikey (by str4d, written in Rust, using the YubiKey's PIV slots; installable with brew install age-plugin-yubikey, with identities that start AGE-PLUGIN-YUBIKEY-1... and an interactive setup run via age-plugin-yubikey itself). There is also a plugin for Apple's Secure Enclave. On the experimental side sit a TPM 2.0 plugin, symmetric FIDO2, and Shamir's Secret Sharing — promising, but not beginner territory yet.
Two freshness notes that every pre-2024 guide misses. First, the current passkey story is age-plugin-fido2prf, installed with go install filippo.io/typage/fido2prf/cmd/age-plugin-fido2prf@latest; its identities look like AGE-PLUGIN-FIDO2PRF-1..., and because it is symmetric you encrypt with the same identity (age -e -i) and decrypt with a security-key PIN prompt. Second, version 1.3.0 added age1tag1... and age1tagpq1... recipients, which let you encrypt to someone's hardware-plugin key without installing the plugin yourself. If a tutorial you find still says plugins require everyone to install the same software, it predates this change.
How Does Age Compare to GPG?
The age vs gpg question comes up constantly, and the honest answer is that age is what you reach for when GPG's complexity is the problem. This table draws the line once:
| age | GPG | |
|---|---|---|
| Key generation | One command, one file | Interactive wizard, keyring |
| Encrypt a file | age -r <recipient> | Key selection, trust model, cipher options |
| Key distribution | Paste the age1... string | Keyservers or manual key exchange |
| Feature surface | Files in, encrypted files out | Email, signing, keyservers, smartcards, more |
| Scripting | Trivial — small flags, pipeable | Possible but fiddly (--batch, --yes, trust) |
| Signing | Not built in — use minisign or signify | Built in |
GPG is the right tool when you need signatures, email integration, or compatibility with an existing PGP workflow. Age is the right tool when you need to encrypt files — backups, secrets, credentials — with the least machinery that still does the job properly. For the structured-secrets case, neither tool is the final answer; that is where SOPS takes over.
When Should I Hand Off to SOPS?
Age encrypts whole files. The moment your secrets live inside structured config — a YAML file with ten settings where two are passwords, a JSON deploy manifest, an env file — encrypting the entire file makes every edit an all-or-nothing affair. SOPS (Secrets OPerationS) encrypts individual values inside YAML, JSON, ENV, and INI files while leaving the structure readable, and it uses age as one of its key backends. Age is the encryption primitive; SOPS is the secrets-file manager. Draw that line once and the two never compete.
The age sops hand-off is small. Generate an age keypair as above, then point SOPS at the recipient in .sops.yaml:
creation_rules:
- path_regex: secrets.yaml$
age: age1ql3z7hj432v2jl2z8alunwwun8hm4s4h6lxu2uqq7e9yqr8e8a2s0a3kq0
SOPS finds your identity in $XDG_CONFIG_HOME/sops/age/keys.txt (falling back to ~/.config/sops/age/keys.txt) on Linux, ~/Library/Application Support/sops/age/keys.txt on macOS, or wherever SOPS_AGE_KEY_FILE points, and it accepts extra recipients through SOPS_AGE_RECIPIENTS. The tutorials that document this setup recommend age over PGP as the SOPS backend — the same simplicity argument, applied one layer up.
If the machine running all of this is a server you manage, the VPS hosting guide covers the hosting half of the picture — where the box lives, how you reach it, and why encrypted backups matter there too.
What Mistakes Do Age Beginners Make?
Every one of these comes from real beginner sessions. The table is the article's cheat sheet — read it before your first real encryption:
| Mistake | What happens | Do this instead |
|---|---|---|
Committing key.txt to git | Your secret key is in version history forever | chmod 600 key.txt, add it to .gitignore before the first commit |
| Losing the identity file with no backup | Nobody — not even age's author — can decrypt your files | Back up the identity file to a second location now |
| Trying to decrypt with the recipient | "no identity matched any of the recipients" | Recipients encrypt; identities decrypt — -i takes the identity file |
Using -i on a passphrase file | Rejected outright since v1.1.0 | Just age -d file.age and type the passphrase |
| Encrypting a directory directly | age refuses — it only takes files or stdin | tar the directory and pipe it in |
| Expecting ssh-agent support | age cannot ask your agent for keys | Point -i at the private key file |
| Forgetting the plaintext original | age encrypts a copy — secrets.txt still sits on disk | Securely delete the original after verifying the encrypted copy |
What Does a Complete First Session Look Like?
Here is the whole beginner arc in seven steps. Run them in order and you will have encrypted, decrypted, and understood every core concept:
- Install age from your package manager (
brew install age,apt install age, or the prebuilt binary). - Generate a keypair:
age-keygen -o key.txt, thenchmod 600 key.txtand back the file up. - Copy your recipient (the
age1...line) and confirm it withage-keygen -y key.txt. - Encrypt something real:
age -r <your-recipient> -o notes.txt.age notes.txt. - Inspect it:
age-inspect notes.txt.age— see the recipient type without decrypting. - Decrypt it back:
age -d -i key.txt notes.txt.ageand diff against the original. - Try passphrase mode:
age -p notes.txt > notes.txt.age, leave the passphrase empty, and write down the generated words.
Sources
- FiloSottile/age GitHub releases — v1.3.2 ("minor release with a wide range of fixes and hardening improvements"), v1.3.0 (post-quantum recipients, age-inspect, tag recipients, GHSA-32gq-x56h-299c plugin-name fix), v1.3.1 (version-injection restore): https://github.com/FiloSottile/age/releases
- FiloSottile/age README (official install table, encrypt/decrypt/passphrase usage lines, SSH key support and caveats, tar-pipe directory example, overhead figures): https://raw.githubusercontent.com/FiloSottile/age/main/README.md
- age(1) man page (armor mode,
-Rrecipients file, output-overwrite behavior, overhead ~200 bytes/recipient + 16 bytes/64KiB): https://www.mankier.com/1/age - FreshPorts security/age — 1.3.1→1.3.2 packaging dated 2026-08-30: https://www.freshports.org/security/age/
- words.filippo.io/age-plugins — plugin architecture, age-plugin-yubikey (stable), Secure Enclave, experimental TPM 2.0 / symmetric FIDO2 / Shamir's Secret Sharing, v1.3.0
age1tag1...recipients: https://words.filippo.io/age-plugins/ - words.filippo.io/passkey-encryption — age-plugin-fido2prf install,
AGE-PLUGIN-FIDO2PRF-1...identities, symmetric encrypt with-e -i, PIN prompt on decrypt: https://words.filippo.io/passkey-encryption/ - Amir Eslampanah — "age: modern file encryption" tutorial (install, age-keygen, -r encrypt, -d decrypt, passphrase mode, multi-recipient, tar-pipe, SOPS hand-off): https://www.amireslampanah.com/tutorials/age-modern-file-encryption.html
- aran.dev — "Age: a practical guide to modern file encryption" (age-keygen world-readable warning transcript): https://aran.dev/posts/age-encryption-guide/
- Luca Berton — "GitOps secrets on Linux with SOPS + age" (age-keygen -y, .sops.yaml age backend, identity file locations): https://lucaberton.com/blog/ansible-sops-community-sops-encrypted-vars/
- Lyra Alishaikh (dev.to) — "GitOps Secrets on Linux with SOPS + Age" (SOPS_AGE_RECIPIENTS, SOPS_AGE_KEY_FILE): https://dev.to/lyraalishaikh/gitops-secrets-on-linux-with-sops-age-encrypted-configs-clean-deploys-1nek
- HostMyCode — "Linux VPS Secrets Management: SOPS + Age (2026)" (age-as-SOPS-backend recommendation over PGP): https://www.hostmycode.com/blog/linux-vps-secrets-management-sops-age-2026
- opensourceprojects.dev — "Simplify file encryption with small keys and UNIX-style composability" (age beginner walkthrough): https://opensourceprojects.dev/
- sts10.github.io — "Exploring age 1.0" (passphrase mode, per-project keys advice, armor): https://sts10.github.io/2021/09/06/exploring-age-1-point-0.html
- xuankentay.com — "Age: Simple, Modern, and Secure File Encryption Tool" (GPG-replacement framing, ssh-key recipients): https://xuankentay.com/blog/age/
Frequently Asked Questions
Is age free to use?
Yes. Age is free and open source, and the file format is an open spec at age-encryption.org, so implementations in Go, Rust (rage), and TypeScript (Typage) all read the same files. There is no paid tier, no account, and no telemetry.
Should I use age or GPG to encrypt files?
Use age when the job is encrypting files — it is simpler, easier to script, and uses modern cryptography with no keyring to maintain. Use GPG when you need signatures, email integration, or compatibility with an existing PGP workflow. For secrets inside structured config files, hand off to SOPS with age as the backend.
Can age encrypt a folder?
Not directly — age only encrypts files or stdin. Pipe the folder through tar first: tar cvz ~/data | age -r <recipient> > data.tar.gz.age. The pipe is the intended design, not a workaround.
What happens if I lose my age identity file?
Your encrypted files become permanently unreadable. There is no recovery mechanism and no support channel that can help — that is the price of real encryption. Back up the identity file to a second location the day you create it.
Can age replace OpenSSL for file encryption?
For most beginner use cases, yes, and more safely: age's passphrase mode autogenerates strong passphrases and its defaults are modern, while OpenSSL's enc command has historically sharp edges around key derivation and cipher choice. Age is the tool designed for exactly this job.