Last updated: March 15, 2026

Install age with brew install age (macOS) or go install filippo.io/age@latest, generate a key pair with age-keygen, then encrypt any file with age -r <public-key> -o output.age input.txt. Age is a modern, minimal alternative to PGP that handles file encryption with far less complexity – no key servers, no web of trust, no configuration files. This tutorial covers command-line usage, passphrase-based encryption, Go library integration, SSH key interoperability, and CI/CD pipeline automation.

Quick Start Steps

  1. Install age: brew install age (macOS) or apt install age (Debian/Ubuntu)
  2. Generate a keypair: age-keygen -o key.txt (saves private key, prints public key)
  3. Encrypt a file: age -r age1xxxxxxxxx -o secret.age plaintext.txt
  4. Decrypt a file: age -d -i key.txt -o plaintext.txt secret.age
  5. Encrypt with passphrase: age -p -o secret.age plaintext.txt (prompts for password)
  6. Encrypt for multiple recipients: age -r age1xxx -r age1yyy -o shared.age file.txt
  7. Use SSH keys instead: age -R ~/.ssh/id_ed25519.pub -o secret.age file.txt
  8. Pipe from stdin: echo "secret data" | age -r age1xxx > secret.age

Prerequisites

Before you begin, make sure you have the following ready:

Step 1 - Install Age

Age supports multiple platforms including macOS, Linux, and Windows. Install it using Homebrew on macOS:

brew install age

On Linux, download the appropriate binary from the GitHub releases page:

wget https://github.com/FiloSottile/age/releases/download/v1.2.1/age-v1.2.1-linux-amd64.tar.gz
tar -xzf age-v1.2.1-linux-amd64.tar.gz
sudo mv age/age* /usr/local/bin/

For Go developers, install directly via go install:

go install filippo.io/age@latest

Verify the installation:

age --version
age v1.2.1

Step 2 - Understand Age’s Key Pairs

Age uses two types of keys - identity keys (private keys) and recipient keys (public keys). Generate a new identity key pair:

Generate identity (private key)
age-keygen

Output example:
created: 2026-03-15T12:00:00Z
public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrjwfkg5vwveyq6mk9l
AGE-SECRET-KEY-1XK7Q6Z9J8V4Y2W5N3M1L6P0R8T3U9V7W4X1Y2Z5A3B6C9D0E2F4G7H8J

Save this output securely. The public key (age1ql3z...) allows others to encrypt files for you without accessing your private key.

Step 3 - Encrypt Files

Encrypt a file for yourself using your public key:

age -p -o encrypted.tar.gz.age secret.tar.gz

The -p flag prompts for a passphrase to add an additional layer of encryption (symmetric encryption layered on top of recipient encryption). The -o flag specifies the output file.

Alternatively, encrypt directly with a recipient’s public key:

age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrjwfkg5vwveyq6mk9l \
    -o document.pdf.age document.pdf

For multiple recipients, specify each public key with a separate -r flag:

age -r age1recipient1... -r age1recipient2... -o shared.tar.gz.age secret.tar.gz

Step 4 - Decrypt Files

Decrypt a file using your identity key:

age -d -o decrypted.tar.gz encrypted.tar.gz.age

If the file was encrypted with a passphrase only (using -p without -r):

age -d encrypted.tar.gz.age

This prompts for the passphrase. For automated workflows, pipe the passphrase:

echo "your-passphrase" | age -d -i ~/age-key.txt encrypted.tar.gz.age -o decrypted.tar.gz

Step 5 - Use Passphrases Only

For quick symmetric encryption without key pairs, use passphrase-based encryption:

age -p secret.txt > secret.txt.age

This creates a file encrypted with a passphrase. Decrypt using the same approach:

age -d secret.txt.age > restored_secret.txt

Step 6 - Implement Programmatic Integration

Integrate age into your Go applications using the age package:

package main

import (
    "bytes"
    "fmt"
    "filippo.io/age"
    "filippo.io/age/armor"
    "io/ioutil"
)

func main() {
    // Generate a new identity
    identity, err := age.GenerateX25519Identity()
    if err != nil {
        panic(err)
    }

    fmt.Println("Public Key:", identity.PublicKey().String())

    // Encrypt data
    plaintext := []byte("Sensitive data to encrypt")

    // Create recipient from public key
    recipient, err := age.ParseX25519Recipient(identity.PublicKey().String())
    if err != nil {
        panic(err)
    }

    var buf bytes.Buffer
    w, err := age.Encrypt(&buf, recipient)
    if err != nil {
        panic(err)
    }

    if _, err := w.Write(plaintext); err != nil {
        panic(err)
    }
    if err := w.Close(); err != nil {
        panic(err)
    }

    // Decrypt data
    r, err := age.Decrypt(bytes.NewReader(buf.Bytes()), identity)
    if err != nil {
        panic(err)
    }

    decrypted, err := ioutil.ReadAll(r)
    if err != nil {
        panic(err)
    }

    fmt.Println("Decrypted:", string(decrypted))
}

Install the package:

go get filippo.io/age

Step 7 - Integration with SSH Keys

Age can derive recipient keys from existing SSH keys, making migration easier:

Generate age key from SSH key
ssh-to-age < ~/.ssh/id_rsa.pub

Use the resulting age public key for encryption. This approach uses your existing SSH infrastructure.

Step 8 - Create Shell Script Automation

Create a reusable encryption script:

#!/bin/bash
encrypt.sh

RECIPIENT="$1"
INPUT_FILE="$2"
OUTPUT_FILE="${INPUT_FILE}.age"

if [ -z "$RECIPIENT" ] || [ -z "$INPUT_FILE" ]; then
    echo "Usage: $0 <recipient-public-key> <file-to-encrypt>"
    exit 1
fi

age -r "$RECIPIENT" -o "$OUTPUT_FILE" "$INPUT_FILE"
echo "Encrypted: $OUTPUT_FILE"

Create a corresponding decryption script:

#!/bin/bash
decrypt.sh

INPUT_FILE="$1"
OUTPUT_FILE="${INPUT_FILE%.age}"

if [ -z "$INPUT_FILE" ]; then
    echo "Usage: $0 <file-to-decrypt>"
    exit 1
}

age -d -o "$OUTPUT_FILE" "$INPUT_FILE"
echo "Decrypted: $OUTPUT_FILE"

Make them executable and use them:

chmod +x encrypt.sh decrypt.sh
./encrypt.sh age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrjwfkg5vwveyq6mk9l backup.tar.gz
./decrypt.sh backup.tar.gz.age

Step 9 - Configure CI/CD Pipeline Integration

Use age in automated deployment pipelines. Generate keys specifically for CI environments:

In your CI pipeline
age-keygen > /tmp/ci-age-key.txt

Encrypt secrets for the pipeline
age -r "$(grep 'public key:' /tmp/ci-age-key.txt | awk '{print $3}')" \
    -o secrets.age .env

Decrypt during build
age -d -i /tmp/ci-age-key.txt -o .env secrets.age

Store the CI identity key as a secret in your CI platform’s secrets management.

Security Considerations

When using age in production environments, follow these practices:

Store identity keys separately from encrypted data. Use hardware security modules or secure key management services for production secrets.

Periodically rotate keys and re-encrypt sensitive data. Age makes this straightforward, decrypt with the old key and re-encrypt with a new one.

When using -p, choose passphrases that meet modern complexity requirements. Combine with public-key encryption for defense in depth.

Log encryption and decryption operations in sensitive environments. Consider adding metadata to encrypted files for organizational purposes.

Troubleshooting

Common issues and solutions:

If you get a permission denied error, ensure your identity key file has restrictive permissions:

chmod 600 ~/.age/identity

If you see an invalid key format error, verify the public key format, age public keys start with age1. Check for copy errors when sharing keys.

A passphrase mismatch occurs when decrypting files encrypted with -p using the wrong passphrase. There is no recovery mechanism for lost passphrases.

Age encrypts files in streaming fashion, handling large files efficiently. For very large files, consider splitting them first with split before encryption.

Frequently Asked Questions

How long does it take to developers?

For a straightforward setup, expect 30 minutes to 2 hours depending on your familiarity with the tools involved. Complex configurations with custom requirements may take longer. Having your credentials and environment ready before starting saves significant time.

What are the most common mistakes to avoid?

The most frequent issues are skipping prerequisite steps, using outdated package versions, and not reading error messages carefully. Follow the steps in order, verify each one works before moving on, and check the official documentation if something behaves unexpectedly.

Do I need prior experience to follow this guide?

Basic familiarity with the relevant tools and command line is helpful but not strictly required. Each step is explained with context. If you get stuck, the official documentation for each tool covers fundamentals that may fill in knowledge gaps.

Is this approach secure enough for production?

The patterns shown here follow standard practices, but production deployments need additional hardening. Add rate limiting, input validation, proper secret management, and monitoring before going live. Consider a security review if your application handles sensitive user data.

Where can I get help if I run into issues?

Start with the official documentation for each tool mentioned. Stack Overflow and GitHub Issues are good next steps for specific error messages. Community forums and Discord servers for the relevant tools often have active members who can help with setup problems.

Related Articles

Built by theluckystrike. More at zovo.one

Related Reading

Built by theluckystrike. More at zovo.one