SkillAgentSearch skills...

Agenix

age-encrypted secrets for NixOS and Home manager

Install / Use

/learn @ryantm/Agenix
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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:

  1. An agenix commandline app (CLI) to encrypt secrets into secured .age files that can be copied into the Nix store.
  2. An agenix NixOS module to conveniently
    • add those encrypted secrets (.age files) into the Nix store so that they can be deployed like any other Nix package using nixos-rebuild or 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.

Contents

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)
  • 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

  1. The system you want to deploy secrets to should already exist and have sshd running on it so that it has generated SSH host keys in /etc/ssh/.

  2. Make a directory to store secrets and secrets.nix file for listing secrets and their public keys:

    $ mkdir secrets
    $ cd secrets
    $ touch secrets.nix
    

    This secrets.nix file is not imported into your NixOS configuration. It's only used for the agenix CLI tool (example below) to know which public keys to use for encryption.

  3. Add public keys to your secrets.nix file:

    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 .age files 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.
  4. Create a secret file:

    $ agenix -e secret1.age
    

    It 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.nix file.

  5. Add secret to a NixOS module config:

    {
      age.secrets.secret1.file = ../secrets/sec
    

Related Skills

View on GitHub
GitHub Stars2.3k
CategoryDevelopment
Updated1d ago
Forks149

Languages

Nix

Security Score

100/100

Audited on Apr 3, 2026

No findings