Agenix
Agenix[1] is a command-line tool for managing secrets in Nix configurations. It relies on existing SSH key pairs and integrates with the age encryption tool to protect secrets while they reside in the Nix store and during system activation.[2][3]
Features
- SSH-based encryption workflow: The `agenix` CLI encrypts raw secret files with user and host SSH public keys, allowing decryption wherever the matching private keys are present.[3]
- Reproducible storage model: Encrypted `.age` files can live in version control, enter the Nix store, and are decrypted during system activation to paths such as
/run/agenix
.[3] - Declarative module support: NixOS and Home Manager modules ship with the project, so secrets can be imported, decrypted, and exposed declaratively alongside other configuration.[3]
- Minimal dependency footprint: Agenix depends on the `age` tool rather than GnuPG and intentionally omits templating features, keeping the code base small and auditable.[3][4]
Installation
The following example describes installing Agenix with Flakes.[1] For additional installation methods, consult the README.[3]
{
inputs.agenix.url = "github:ryantm/agenix";
# optional, not necessary for the module
#inputs.agenix.inputs.nixpkgs.follows = "nixpkgs";
outputs = { self, nixpkgs, agenix }: {
nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
agenix.nixosModules.default
];
};
};
}
Replace yourhostname
with the actual host name and x86_64-linux
with the relevant system architecture.
After adding the input, install the agenix
client application with:
{ config, pkgs, lib, inputs, ... }:{
environment.systemPackages = [
inputs.agenix.packages."${system}".default
];
}
Configuration
Choose a public/private key
Decide which SSH public key should encrypt the secrets. The corresponding private key decrypts the secrets when rebuilding the system.
If you deploy the configuration as the root user, the host key pairs generated by SSH are already available at:
/etc/ssh/ssh_host_rsa_key
//etc/ssh/ssh_host_rsa_key.pub
/etc/ssh/ssh_host_ed25519_key
//etc/ssh/ssh_host_ed25519_key.pub
For deployments triggered by another user, create a dedicated key pair with ssh-keygen
. Such keys typically reside at:
~/.ssh/id_rsa
/~/.ssh/id_rsa.pub
~/.ssh/id_ed25519
/~/.ssh/id_ed25519.pub
Refer to SSH public key authentication for more information.
Create the secrets
Create a directory that stores encrypted secrets. For example:
# mkdir /etc/nixos/secrets
Inside the secrets directory, create a secrets.nix
file that defines the encryption rules. The following example gives user1
and system1
access to secret1.age
:
let
user1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0idNvgGiucWgup/mP78zyC23uFjYq0evcWdjGQUaBH";
users = [ user1 ];
system1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPJDyIr/FSz1cJdcoW69R+NrWzwGK/+3gJpqD1t8L2zE";
systems = [ system1 ];
in
{
"secret1.age".publicKeys = [ user1 system1 ];
}
Usage
Create a secret file and encrypt it by running:
# cd /etc/nixos/secrets
# agenix -e secret1.age
The agenix
command opens the default terminal editor. Enter the secret (for example, password123
) and save the file. The secret1.age
filename is defined in secrets.nix
, allowing agenix
to select the correct SSH keys.[3]
To reference the secret inside a NixOS module, use a configuration similar to the following example:
age.secrets.nextcloud = {
file = ./secrets/secret1.age;
owner = "nextcloud";
group = "nextcloud";
};
services.nextcloud = {
enable = true;
package = pkgs.nextcloud28;
hostName = "localhost";
config.adminpassFile = config.age.secrets.nextcloud.path;
};
In this case, the Nextcloud service reads its administrator password from the age-encrypted file, preventing the secret from appearing in the world-readable Nix store.[3]
Secrets can also be deployed as files with specific permissions:[3]
age.secrets = {
netrc = {
file = ./secrets/netrc.age;
path = "/home/myuser/.netrc";
owner = "myuser";
group = "users";
mode = "600";
};
};
Tips and tricks
Replace in-place strings with secrets
Some modules do not yet support reading secrets from a file. Provide a placeholder string and replace it during activation.
system.activationScripts."dex-user-secret" = ''
secret=$(cat "${config.age.secrets.dex-user-password.path}")
configFile=/run/dex/config.yaml
${pkgs.gnused}/bin/sed -i "s#@dex-user-password@#$secret#" "$configFile"
'';
Access secrets inside a container
Expose secrets to a container by binding the decrypted file into the container filesystem:
containers.mycontainer.bindMounts."${config.agenix.secrets.mysecret.path}".isReadOnly = true;
Another option is to import agenix.nixosModules.default
directly inside the container and supply the required private key:
{ agenix, ... }: {
containers."mycontainer" = {
# pass the private key to the container for agenix to decrypt the secret
bindMounts."/etc/ssh/ssh_host_ed25519_key".isReadOnly = true;
config = { config, lib, pkgs, ... }: {
imports = [ agenix.nixosModules.default ];
age.identityPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
age.secrets."mysecret" = {
file = ../secrets/mysecret.age;
owner = "myuser";
};
};
};
}
Use secrets in the initrd
Agenix provisions secrets during system activation, so they are unavailable while building the initrd. As a workaround, create the secret as a static file outside /run/agenix
and reference it from /etc/initrd-hostkey
.
age.secrets.hostkey-initrd = {
file = "${paths.agenix}/hostkey-initrd.age";
path = "/etc/initrd-hostkey";
symlink = false;
};
boot.initrd.network.ssh.hostKeys = [ "/etc/initrd-hostkey" ];
Rebuild the system twice and reference /etc/initrd-hostkey
only after the file exists.
Agenix with Impermanence
Systems that use Impermanence may regenerate host keys on each boot. Point age.identityPaths
to the persistent locations of those keys:
age.identityPaths = [
"/persist/etc/ssh/ssh_host_ed25519_key"
"/persist/etc/ssh/ssh_host_rsa_key"
];
See also
- Comparison of secret managing schemes – Overview of alternative secret management approaches
- age – Encryption tool that underpins Agenix secrets[2]
- Home Manager – Declarative per-user configuration that can consume Agenix-managed secrets
References
- ↑ 1.0 1.1 Ryan Mulligan and contributors, "agenix", GitHub Repository, Accessed October 2025. https://github.com/ryantm/agenix
- ↑ 2.0 2.1 Filippo Valsorda and collaborators, "age", Official Website, Accessed October 2025. https://age-encryption.org/
- ↑ 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 Ryan Mulligan and contributors, "agenix README", GitHub Repository, Accessed October 2025. https://github.com/ryantm/agenix/blob/main/README.md
- ↑ NixOS Wiki Community, "Comparison of secret managing schemes", NixOS Wiki, Accessed October 2025. https://wiki.nixos.org/wiki/Comparison_of_secret_managing_schemes