This repository uses sops-nix to manage secrets.
- Encrypted files (e.g.,
secrets/secrets.yaml) SHOULD be committed to Git. - Private keys (e.g.,
~/.config/sops/age/keys.txt) MUST NEVER be committed.
The following tools are installed via modules/home/ops/sops.nix:
sops: The CLI tool for editing secrets.age: The encryption backend.ssh-to-age: Utility to convert SSH keys to age keys.
We use age keys for encryption:
- User Key: Located at
~/.config/sops/age/keys.txt. Used for manual editing and Home Manager secrets. - Host Key: Derived from
/etc/ssh/ssh_host_ed25519_key. Used by NixOS for system-level secrets.
The public keys for both are configured in the root .sops.yaml.
To edit secrets, sops needs to know where your private age key is. You can export this in your shell:
export SOPS_AGE_KEY_FILE=$HOME/.config/sops/age/keys.txt
sops secrets/secrets.yaml- Run
sops secrets/secrets.yamland add a new key-value pair. - Reference the secret in your Nix configuration:
sops.secrets.my_secret = {
owner = "cwilliams";
};Access via: config.sops.secrets.my_secret.path (resolves to /run/secrets/my_secret).
sops.secrets.my_home_secret = {};Access via: config.sops.secrets.my_home_secret.path (resolves to /run/user/1000/secrets/my_home_secret).
- Get the Host Age Key:
sudo ssh-to-age -i /etc/ssh/ssh_host_ed25519_key.pub
- Update
.sops.yaml: Add the new host key to thekeysandcreation_rulessections. - Re-encrypt:
sops updatekeys secrets/secrets.yaml
If you have your SSH key but not the age keys.txt file:
mkdir -p ~/.config/sops/age
# If your SSH key is passphrase protected, you must use the private key and enter the pass:
ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt- Verify your local private key matches the public key in
.sops.yaml:age-keygen -y ~/.config/sops/age/keys.txt - Ensure the
SOPS_AGE_KEY_FILEenvironment variable is set if using the CLI manually.
- System secrets are owned by
root:rootwith0400permissions by default. - Use
owner = "cwilliams";in the secret definition to allow your user to read it.
This is the most secure way to use secrets in services.
sops.secrets.api_key = {};
systemd.services.my-service = {
serviceConfig = {
# sops-nix creates a file in /run/secrets/api_key
# EnvironmentFile reads it as "KEY=VALUE"
EnvironmentFile = config.sops.secrets.api_key.path;
ExecStart = "${pkgs.my-app}/bin/my-app";
};
};Since secrets are only available at runtime, you can source them in your shell configuration.
In Home Manager (modules/home/ops/sops.nix):
sops.secrets.github_token = {};
programs.zsh.initExtra = ''
# Source the secret if it exists
if [ -f ${config.sops.secrets.github_token.path} ]; then
export GITHUB_TOKEN=$(cat ${config.sops.secrets.github_token.path})
fi
'';If you need to generate a config file that combines multiple secrets:
sops.templates."config.env".content = ''
DATABASE_URL=postgres://user:${config.sops.placeholder.db_password}@localhost/db
API_KEY=${config.sops.placeholder.api_key}
'';
# The resulting file is at /run/secrets-render/config.env