Agenix
age-encrypted secrets for NixOS and Home manager
Install / Use
/learn @ryantm/AgenixREADME
agenix - age-encrypted secrets for NixOS
agenix is a small and convenient Nix library for securely managing and deploying secrets using common public-private SSH key pairs:
You can encrypt a secret (password, access-token, etc.) on a source machine using a number of public SSH keys,
and deploy that encrypted secret to any another target machine that has the corresponding private SSH key of one of those public keys.
This project contains two parts:
- An
agenixcommandline app (CLI) to encrypt secrets into secured.agefiles that can be copied into the Nix store. - An
agenixNixOS module to conveniently- add those encrypted secrets (
.agefiles) into the Nix store so that they can be deployed like any other Nix package usingnixos-rebuildor similar tools. - automatically decrypt on a target machine using the private SSH keys on that machine
- automatically mount these decrypted secrets on a well known path like
/run/agenix/...to be consumed.
- add those encrypted secrets (
Contents
- Problem and solution
- Features
- Installation
- Tutorial
- Reference
- Community and Support
- Threat model/Warnings
- Contributing
- Acknowledgements
Problem and solution
All files in the Nix store are readable by any system user, so it is not a suitable place for including cleartext secrets. Many existing tools (like NixOps deployment.keys) deploy secrets separately from nixos-rebuild, making deployment, caching, and auditing more difficult. Out-of-band secret management is also less reproducible.
agenix solves these issues by using your pre-existing SSH key infrastructure and age to encrypt secrets into the Nix store. Secrets are decrypted using an SSH host private key during NixOS system activation.
Features
- Secrets are encrypted with SSH keys
- system public keys via
ssh-keyscan - can use public keys available on GitHub for users (for example, https://github.com/ryantm.keys)
- system public keys via
- No GPG
- Very little code, so it should be easy for you to audit
- Encrypted secrets are stored in the Nix store, so a separate distribution mechanism is not necessary
Notices
- Password-protected ssh keys: since age does not support ssh-agent, password-protected ssh keys do not work well. For example, if you need to rekey 20 secrets you will have to enter your password 20 times.
Installation
<details> <summary>Install via niv
</summary>First add it to niv:
$ niv add ryantm/agenix
Install module via niv
Then add the following to your configuration.nix in the imports list:
{
imports = [ "${(import ./nix/sources.nix).agenix}/modules/age.nix" ];
}
Install home-manager module via niv
Add the following to your home configuration:
{
imports = [ "${(import ./nix/sources.nix).agenix}/modules/age-home.nix" ];
}
Install CLI via niv
To install the agenix binary:
{
environment.systemPackages = [ (pkgs.callPackage "${(import ./nix/sources.nix).agenix}/pkgs/agenix.nix" {}) ];
}
</details>
<details>
<summary>
Install via nix-channel
</summary>As root run:
$ sudo nix-channel --add https://github.com/ryantm/agenix/archive/main.tar.gz agenix
$ sudo nix-channel --update
Install module via nix-channel
Then add the following to your configuration.nix in the imports list:
{
imports = [ <agenix/modules/age.nix> ];
}
Install home-manager module via nix-channel
Add the following to your home configuration:
{
imports = [ <agenix/modules/age-home.nix> ];
}
Install CLI via nix-channel
To install the agenix binary:
{
environment.systemPackages = [ (pkgs.callPackage <agenix/pkgs/agenix.nix> {}) ];
}
</details>
<details>
<summary>
Install via fetchTarball
</summary>Install module via fetchTarball
Add the following to your configuration.nix:
{
imports = [ "${builtins.fetchTarball "https://github.com/ryantm/agenix/archive/main.tar.gz"}/modules/age.nix" ];
}
or with pinning:
{
imports = let
# replace this with an actual commit id or tag
commit = "298b235f664f925b433614dc33380f0662adfc3f";
in [
"${builtins.fetchTarball {
url = "https://github.com/ryantm/agenix/archive/${commit}.tar.gz";
# update hash from nix build output
sha256 = "";
}}/modules/age.nix"
];
}
Install home-manager module via fetchTarball
Add the following to your home configuration:
{
imports = [ "${builtins.fetchTarball "https://github.com/ryantm/agenix/archive/main.tar.gz"}/modules/age-home.nix" ];
}
Or with pinning:
{
imports = let
# replace this with an actual commit id or tag
commit = "298b235f664f925b433614dc33380f0662adfc3f";
in [
"${builtins.fetchTarball {
url = "https://github.com/ryantm/agenix/archive/${commit}.tar.gz";
# update hash from nix build output
sha256 = "";
}}/modules/age-home.nix"
];
}
Install CLI via fetchTarball
To install the agenix binary:
{
environment.systemPackages = [ (pkgs.callPackage "${builtins.fetchTarball "https://github.com/ryantm/agenix/archive/main.tar.gz"}/pkgs/agenix.nix" {}) ];
}
</details>
<details>
<summary>
Install via Flakes
</summary>Install module via Flakes
{
inputs.agenix.url = "github:ryantm/agenix";
# optional, not necessary for the module
#inputs.agenix.inputs.nixpkgs.follows = "nixpkgs";
# optionally choose not to download darwin deps (saves some resources on Linux)
#inputs.agenix.inputs.darwin.follows = "";
outputs = { self, nixpkgs, agenix }: {
# change `yourhostname` to your actual hostname
nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
# change to your system:
system = "x86_64-linux";
modules = [
./configuration.nix
agenix.nixosModules.default
];
};
};
}
Install home-manager module via Flakes
{
inputs.agenix.url = "github:ryantm/agenix";
outputs = { self, nixpkgs, agenix, home-manager }: {
homeConfigurations."username" = home-manager.lib.homeManagerConfiguration {
# ...
modules = [
agenix.homeManagerModules.default
# ...
];
};
};
}
Install CLI via Flakes
You can run the CLI tool ad-hoc without installing it:
nix run github:ryantm/agenix -- --help
But you can also add it permanently into a NixOS module (replace system "x86_64-linux" with your system):
{
environment.systemPackages = [ agenix.packages.x86_64-linux.default ];
}
e.g. inside your flake.nix file:
{
inputs.agenix.url = "github:ryantm/agenix";
# ...
outputs = { self, nixpkgs, agenix }: {
# change `yourhostname` to your actual hostname
nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
# ...
{
environment.systemPackages = [ agenix.packages.${system}.default ];
}
];
};
};
}
</details>
Tutorial
-
The system you want to deploy secrets to should already exist and have
sshdrunning on it so that it has generated SSH host keys in/etc/ssh/. -
Make a directory to store secrets and
secrets.nixfile for listing secrets and their public keys:$ mkdir secrets $ cd secrets $ touch secrets.nixThis
secrets.nixfile is not imported into your NixOS configuration. It's only used for theagenixCLI tool (example below) to know which public keys to use for encryption. -
Add public keys to your
secrets.nixfile:let user1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0idNvgGiucWgup/mP78zyC23uFjYq0evcWdjGQUaBH"; user2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILI6jSq53F/3hEmSs+oq9L4TwOo1PrDMAgcA1uo1CCV/"; users = [ user1 user2 ]; system1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPJDyIr/FSz1cJdcoW69R+NrWzwGK/+3gJpqD1t8L2zE"; system2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKzxQgondgEYcLpcPdJLrTdNgZ2gznOHCAxMdaceTUT1"; systems = [ system1 system2 ]; in { "secret1.age".publicKeys = [ user1 system1 ]; "secret2.age".publicKeys = users ++ systems; "armored-secret.age" = { publicKeys = [ user1 ]; armor = true; }; }These are the users and systems that will be able to decrypt the
.agefiles later with their corresponding private keys. The armor option may also be supplied here to ensure files are output in Base64 PEM text which is useful for more readable diffs. You can obtain the public keys from- your local computer usually in
~/.ssh, e.g.~/.ssh/id_ed25519.pub. - from a running target machine with
ssh-keyscan:$ ssh-keyscan <ip-address> ... ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKzxQgondgEYcLpcPdJLrTdNgZ2gznOHCAxMdaceTUT1 ... - from GitHub like https://github.com/ryantm.keys.
- your local computer usually in
-
Create a secret file:
$ agenix -e secret1.ageIt will open a temporary file in the app configured in your $EDITOR environment variable. When you save that file its content will be encrypted with all the public keys mentioned in the
secrets.nixfile. -
Add secret to a NixOS module config:
{ age.secrets.secret1.file = ../secrets/sec
Related Skills
node-connect
348.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.1kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
348.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
348.5kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
