Encrypt Files with age and rage
Learn to encrypt and decrypt files with age and rage: generate keys, encrypt for multiple recipients, use SSH keys, and integrate with the passage password manager.
Before you start
- ▸A terminal with sudo access
- ▸Cargo installed if building rage from source (rustup.rs)
- ▸Basic familiarity with the command line and file paths
age is a modern, simple file encryption tool designed as a replacement for GPG's most common use case: encrypt a file for yourself or a recipient. It has a tiny attack surface, a clean key format, and predictable behaviour. rage is a Rust implementation of the same spec — compatible, fast, and available in most package managers. Both tools share identical command syntax, so everything here works for either.
Install age and rage
Debian / Ubuntu
sudo apt update && sudo apt install age
rage is not yet in the Ubuntu/Debian main repos on older LTS releases. Install it from a pre-built binary or via Cargo:
cargo install rage
Fedora / RHEL 9+ / Rocky 9+
sudo dnf install age
For rage on Fedora:
sudo dnf install rage-encryption
Arch Linux
sudo pacman -S age rage
Verify installation
age --version
rage --version
You should see a version string such as age v1.2.0. Either binary will do for everything below; substitute rage for age if that is what you installed.
Generate a Key Pair
age uses X25519 key pairs. The private key file holds both the private and public key. Keep the private key file safe — it is not passphrase-protected by default, so treat it like an SSH private key.
mkdir -p ~/.config/age
age-keygen -o ~/.config/age/identity.txt
Output looks like:
# Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
The file identity.txt contains a comment line with your public key and the secret key itself. Copy the public key string — it starts with age1 — for sharing with senders.
Protect the private key file
chmod 600 ~/.config/age/identity.txt
Optional: passphrase-protected identity
If you want the private key encrypted at rest, encrypt the identity file itself with a passphrase using age's -p flag and store the result instead:
age -p -o ~/.config/age/identity.txt.age ~/.config/age/identity.txt
rm ~/.config/age/identity.txt
You will be prompted for the passphrase on every decrypt. This is the right trade-off for keys stored on a shared or portable machine.
Encrypt a File
Encrypt for yourself (using your public key)
age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \
-o secret.txt.age secret.txt
Replace the age1… string with your actual public key from the previous step.
Encrypt with a passphrase instead of a key
Useful for sharing over email or with someone who does not have an age key yet:
age -p -o secret.txt.age secret.txt
Encrypt for multiple recipients
Pass -r once per recipient. The file can be decrypted by any of them independently:
age \
-r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \
-r age1lggyhqr0e7j2a5uu8ltztm8g9e2pxq5m6pj2x6vq3z9u2krhqrqd8vpx2 \
-o report.pdf.age report.pdf
Encrypt a directory (via tar)
age encrypts a single stream. Pipe tar into age to handle multiple files:
tar -czf - ~/Documents/project | \
age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \
-o project.tar.gz.age
Decrypt a File
With your identity file
age -d -i ~/.config/age/identity.txt -o secret.txt secret.txt.age
With a passphrase-encrypted identity
age -d -i ~/.config/age/identity.txt.age -o secret.txt secret.txt.age
age will prompt for the identity file passphrase first, then decrypt the payload.
Decrypt a tar archive
age -d -i ~/.config/age/identity.txt project.tar.gz.age | tar -xzf -
Working with Recipients Files
Typing public keys on the command line every time is error-prone. Use a recipients file — one public key per line, comments allowed with #:
cat ~/.config/age/recipients.txt
# Alice
age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
# Bob
age1lggyhqr0e7j2a5uu8ltztm8g9e2pxq5m6pj2x6vq3z9u2krhqrqd8vpx2
age -R ~/.config/age/recipients.txt -o report.pdf.age report.pdf
The -R flag (capital R) reads recipients from a file. This is equivalent to passing each key with its own -r flag.
Use an SSH Key as an age Identity
age supports Ed25519 and RSA SSH public keys as recipients. This is handy when the person you are encrypting for already has an SSH key on GitHub:
curl https://github.com/username.keys | \
age -R - -o secret.txt.age secret.txt
Decryption uses the corresponding SSH private key:
age -d -i ~/.ssh/id_ed25519 -o secret.txt secret.txt.age
Note: RSA SSH keys work but Ed25519 is preferred. Do not rely on this feature for long-term archival without understanding the SSH key lifecycle.
Integrate age with pass
pass uses GPG by default, but the passage fork or the pass-age extension wraps age instead. The most practical approach without switching to a fork is passage:
Install passage
# Arch
sudo pacman -S passage
# Others: install from source
git clone https://github.com/FiloSottile/passage ~/.local/src/passage
cd ~/.local/src/passage
sudo make install
Initialise a passage store
passage init age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
This creates ~/.passage/store and writes a .age-recipients file containing your public key. Your identity file at ~/.passage/identities must exist — symlink or copy it:
ln -s ~/.config/age/identity.txt ~/.passage/identities
Use passage like pass
# Insert a password
passage insert email/personal
# Retrieve it
passage email/personal
# Copy to clipboard (requires xclip or wl-clipboard)
passage -c email/personal
On Wayland, install wl-clipboard instead of xclip; passage will use wl-copy automatically when WAYLAND_DISPLAY is set.
Verify Encryption and Spot-Check Decryption
# Confirm the file is opaque binary (not plaintext)
file secret.txt.age
# Expected: secret.txt.age: data
# Confirm decryption round-trips correctly
age -d -i ~/.config/age/identity.txt secret.txt.age | diff - secret.txt && echo "OK"
A clean OK confirms the ciphertext decrypts to exactly the original file.
Troubleshooting
"no identity matched any of the file's recipients"
The identity file you passed with -i does not match the key used during encryption. Double-check you are using the correct identity file. If you have multiple keys, try each. The error is intentionally vague for security reasons.
Permission denied on identity.txt
age refuses to use a private key file readable by others. Fix it:
chmod 600 ~/.config/age/identity.txt
rage: command not found after cargo install
Cargo installs binaries to ~/.cargo/bin. Add it to your PATH:
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
passage init fails with "no identities file"
passage looks for ~/.passage/identities. Create or symlink the file as shown above, then re-run passage init.
Frequently asked questions
- What is the difference between age and rage?
- rage is a Rust implementation of the same age encryption spec. Both produce and consume identical .age files; the choice is mainly about installation preference or performance on large files.
- Can someone without an age key decrypt a file I encrypted with -p?
- Yes. A passphrase-encrypted file only requires the correct passphrase, not an age key pair. Share the passphrase through a separate secure channel.
- Is it safe to store the private key in ~/.config/age/identity.txt unencrypted?
- Only if the machine has full-disk encryption and you are the sole user. For shared or portable machines, encrypt the identity file with a passphrase as described in the guide.
- How does age compare to GPG for file encryption?
- age is simpler: no key server, no web of trust, no complex configuration, and a much smaller codebase. It does not support signatures; use Minisign or SSH signing for that.
- Can I use age to encrypt files for a team where members change over time?
- Yes. Keep a recipients file and add or remove public keys as the team changes. Re-encrypting existing files for the new recipient set requires decrypting and re-encrypting each file.
Related guides
Manage Secrets with Ansible Vault
Encrypt Ansible secrets with AES-256 using ansible-vault: encrypt files and inline vars, automate with password files, and isolate group-level secrets with vault IDs.
AppArmor Explained
Learn how AppArmor profiles work, how to switch between enforce and complain mode, create new profiles, and diagnose access denials on Ubuntu, Debian, and Arch.
Apply CIS Benchmarks with OpenSCAP
Use OpenSCAP and scap-security-guide to evaluate, report on, and remediate Linux systems against CIS Benchmarks — covering install, eval, and automation.
How to Audit a Linux System with auditd
Set up auditd on Linux to track file access, syscalls, and privilege use. Covers persistent rules, file watches, ausearch, and aureport across major distros.