Install

# Debian / Ubuntu
sudo apt install age

# macOS
brew install age

# Arch / Fedora similar packages

# Or via mise (see /tutorials/mise-polyglot-runtime-versions.html)
mise use -g age@latest

age --version

Generate a keypair

age-keygen -o ~/.config/age/key.txt

# Output:
# # public key: age1abc...xyz
# AGE-SECRET-KEY-1...

cat ~/.config/age/key.txt
# The full file contains the private key + the public key in a comment
# chmod 600 it

The public key is what others use to encrypt to you. The private key is what you use to decrypt; protect it.

Encrypt to a public key

# File output
age -r age1abc...xyz -o secret.age plaintext.txt

# Or via stdin/stdout
cat plaintext.txt | age -r age1abc...xyz > secret.age

# Decrypt (with your private key)
age -d -i ~/.config/age/key.txt -o plaintext.txt secret.age

Encrypt with a passphrase

# Encrypt
age -p -o secret.age plaintext.txt
# Prompts for passphrase

# Decrypt
age -d -o plaintext.txt secret.age

Passphrase mode uses scrypt; expensive to brute-force but slower than public-key mode. Use a real passphrase — the encryption is only as strong as the secret.

Encrypt to an SSH key

The killer feature for collaboration: encrypt a file for someone using their existing SSH public key:

# Get a colleague's SSH public key from GitHub
curl https://github.com/octocat.keys

# Pick an ed25519 key (RSA also works but is larger)
echo "ssh-ed25519 AAAAC3... octocat" > octocat.pub

# Encrypt for them
age -R octocat.pub -o file-for-octocat.age confidential.pdf

# They decrypt with their SSH private key
age -d -i ~/.ssh/id_ed25519 -o confidential.pdf file-for-octocat.age

This eliminates the "exchange public keys first" step that PGP traditionally required — GitHub already exposes everyone's SSH keys; use them.

Encrypt to multiple recipients

# Anyone whose key is listed can decrypt
age -r age1aaa...000 -r age1bbb...111 -r age1ccc...222 -o team.age secret.txt

# Or with a recipients file
cat > recipients.txt <<'EOF'
age1aaa...000
ssh-ed25519 AAAA... alice
ssh-ed25519 AAAA... bob
EOF

age -R recipients.txt -o team.age secret.txt

Each recipient can decrypt independently with their own key.

Encrypt a directory: pipe through tar

# Encrypt a whole directory
tar -czf - my-secret-folder | age -r age1abc...xyz > folder.tar.gz.age

# Decrypt
age -d -i ~/.config/age/key.txt folder.tar.gz.age | tar -xzf -

age is a streaming encryption tool; it doesn't know about directories. Combine with tar / zip for tree encryption.

age in CI / scripts: secrets encrypted in Git

The "encrypted secrets in your repo" pattern:

# Encrypt a secrets file for the deploy key
age -r age1deploy...key -o secrets/prod.env.age secrets/prod.env

# Commit the .age file; .gitignore the plaintext

# In CI, decrypt using a deploy private key from a secret
echo "$AGE_KEY" > /tmp/key.txt
age -d -i /tmp/key.txt -o /tmp/prod.env secrets/prod.env.age
source /tmp/prod.env
rm /tmp/key.txt /tmp/prod.env

For more complete in-Git secrets management with editor integration, see SOPS (next section) — it can use age as the encryption backend.

SOPS + age: structured-file secrets

SOPS is Mozilla's secrets-management tool: it encrypts only the values of YAML/JSON/INI/.env files, leaving keys + structure visible. Combined with age, it's the cleanest "secrets in Git" workflow:

sudo apt install sops          # or brew install sops

# .sops.yaml
creation_rules:
  - path_regex: secrets/.*\.yaml
    age: age1abc...xyz,age1def...uvw

# Edit a secrets file (sops opens $EDITOR with decrypted contents)
sops secrets/prod.yaml

# View
sops -d secrets/prod.yaml

# CI consumes via
SOPS_AGE_KEY_FILE=/tmp/key.txt sops -d secrets/prod.yaml

The encrypted YAML in Git looks like:

database:
    host: db.prod.example.com
    port: 5432
    password: ENC[AES256_GCM,data:...,iv:...,tag:...,type:str]
    user: ENC[AES256_GCM,...]

Reviewers see structure + non-secret keys; diff tools show which secrets changed without revealing values.

chezmoi + age

For dotfiles with secrets (see chezmoi tutorial), age is the recommended encryption backend:

# chezmoi config
chezmoi edit-config
# Add:
[age]
    identity = "~/.config/age/key.txt"
    recipient = "age1abc...xyz"

# Now files with .age suffix are encrypted in the source dir
chezmoi add --encrypt ~/.aws/credentials

age vs PGP / GPG

  • PGP/GPG — the elder. Long-lived; massive feature surface; many key types; sub-keys; web of trust; key-server protocols. Crypto is mostly fine but the UX has always been hostile.
  • age — modern primitives, one binary, no keyring concept, no sub-keys, no signatures (just encryption). Smaller scope; safer defaults.

For signing artifacts (release tarballs, git commits), use minisign / signify / GPG. For encrypting files to specific recipients, age.

What age doesn't do

  • Signing — age is encrypt-only. Use minisign or GPG for signatures.
  • Web of trust — no key-server protocol, no signed-by chains. Recipients are trusted via out-of-band key exchange.
  • Streaming + random access — age files are encrypted as a single stream; you can't decrypt arbitrary byte ranges.
  • Compression — pipe through gzip / zstd before age.

For "I have a secret and want to give it to a specific person, encrypted, with no setup ceremony," age is the right tool. For everything beyond that, layer on SOPS, minisign, or a full PKI like step-ca (see that tutorial).