Skip to main content
This homelab uses agenix for secrets management, allowing you to encrypt secrets with age and store them safely in Git. The configuration supports per-user secrets, team secrets, and shell integration.

Architecture

The secrets management system consists of:
  • agenix: Encrypts secrets with age public keys
  • sops-nix: Alternative secrets backend (configured via .sops.yaml)
  • secrets.nix: Centralized secrets configuration with user/team mapping
  • agenix-shell: Makes secrets available in development shells

Secrets Configuration Structure

The secrets.nix file defines three key components:

1. User Public Keys

users = {
  soriphoono = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEgxxFcqHVwYhY0TjbsqByOYpmWXqzlVyGzpKjqS8mO7";
};
These SSH public keys are used to encrypt secrets for specific users.

2. Per-User Secrets

userSecrets = {
  github_token = ["soriphoono"];
};
Defines which users can access specific secrets.

3. Team Secrets

teams = {
  default = {
    users = ["soriphoono"];
    secrets = [];
  };
};
Allows shared access across multiple team members.

Adding a New Secret

1

Add the secret to secrets.nix

Edit the userSecrets or team’s secrets list:
userSecrets = {
  github_token = ["soriphoono"];
  api_key = ["soriphoono"]; # New secret
};
2

Create the encrypted secret file

Use agenix to create and edit the secret:
agenix -e secrets/api_key.age
This opens your $EDITOR to input the secret value. The file is automatically encrypted with the appropriate public keys based on your secrets.nix configuration.
3

Commit the encrypted secret

The .age file is safe to commit:
git add secrets/api_key.age
git commit -m "Add API key secret"
4

Deploy to systems

Reference the secret in your NixOS configuration:
age.secrets.api_key = {
  file = ./secrets/api_key.age;
  owner = "myuser";
  group = "users";
};
The decrypted secret will be available at /run/agenix/api_key on the target system.

SOPS Configuration

The .sops.yaml file configures sops-nix with age keys for different users and systems:
keys:
  - &admin_soriphoono age1y4x8gzzcdhxvqaf9matt0eze3z3yxwx607avq4cexfkgwal4psesj39lvn
  - &system_lg_laptop age1yqsdglc9s03k6yhefaqpnpyurj0j9nktlccc8rq2uevpuzn0ea2stx0msr
  - &system_zephyrus age1vz3rjjgwq9t3288mwlt9cdyt83429srm8hwycjrflllhcwkkugvs0jafa0

creation_rules:
  - path_regex: homes/soriphoono(@[^/]+)?/[^/]+\.(yaml|json|env|ini)$
    key_groups:
      - age:
          - *admin_soriphoono

Path-based Encryption Rules

SOPS uses path regex patterns to determine which keys can decrypt files:
  • Home secrets: homes/<user>/ files are encrypted for that user and admins
  • System secrets: systems/<hostname>/ files include the system’s age key
This allows granular control over who can decrypt what.

Shell Integration

The agenix-shell integration automatically loads secrets as environment variables in your development shell:
agenix-shell-secrets = {
  GITHUB_TOKEN = {
    file = ./secrets/github_token.age;
  };
};
Secrets are:
  • Converted to uppercase for environment variable names
  • Only loaded for users with access
  • Available in nix develop shells

Security Best Practices

  • Never commit unencrypted secrets to Git
  • Always verify .age files are encrypted before committing
  • Rotate secrets if a private key is compromised
  • Use system-specific keys for production systems

Converting SSH Keys to Age

To get an age public key from an SSH key:
ssh-to-age < ~/.ssh/id_ed25519.pub
# Output: age1y4x8gzzcdhxvqaf9matt0eze3z3yxwx607avq4cexfkgwal4psesj39lvn
For the private key:
ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/age/key.txt

Editing Existing Secrets

To modify an encrypted secret:
agenix -e secrets/github_token.age
This decrypts the secret, opens your editor, and re-encrypts on save.

Common Issues

”No decryption key found”

Ensure your private key is available:
  • SSH key at ~/.ssh/id_ed25519
  • Age key at ~/.config/age/key.txt

”Public key not found in secrets.nix”

Add your user to the users mapping in secrets.nix with your SSH public key.

Secrets not loading in shell

Check that:
  1. You’re in a nix develop shell with agenix-shell enabled
  2. Your user has access to the secret in secrets.nix
  3. The secret file exists at secrets/<name>.age

Build docs developers (and LLMs) love