A comprehensive Nix configuration supporting multiple users across macOS and Linux platforms, featuring Determinate Nix 3.0, home-manager, nix-darwin, and secure secrets management with agenix.
- Multi-user support: Each user has independent configuration with personal settings, packages, and secrets
- Multi-platform: Full support for macOS (via nix-darwin) and Linux (via standalone home-manager)
- Secure secrets: Encrypted secrets management using agenix
- Flakes enabled: Modern Nix flakes for reproducible and modular configuration
- Minimal setup: Focused on simplicity and maintainability
- Dynamic detection: Build scripts automatically detect current user and platform
- vwh7mb on macbook-pro (macOS/Darwin, aarch64)
- eh2889 on rjds (Ubuntu Linux, x86_64)
- edwinhu on alarm (Arch Linux/Asahi, aarch64)
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- installgit clone <your-repository-url>
cd nixnix run .#build-switchThis command automatically detects:
- macOS: Uses
darwin-rebuild - NixOS: Uses
nixos-rebuild - Other Linux: Uses
home-managerstandalone
If you prefer explicit commands:
macOS (nix-darwin)
darwin-rebuild switch --flake .#vwh7mbLinux (home-manager standalone)
home-manager switch --flake .#eh2889NixOS (not currently configured)
sudo nixos-rebuild switch --flake .#<hostname>This configuration uses agenix with SSH key encryption for managing secrets like API keys. Secrets are stored encrypted in a private nix-secrets repo and decrypted at runtime.
- One SSH key (
~/.ssh/id_ed25519_agenix) is used across ALL systems - Secrets are encrypted in
~/nix-secrets/*.agefiles using that key's public key - On activation, home-manager's agenix module decrypts secrets to:
- macOS:
$(getconf DARWIN_USER_TEMP_DIR)/agenix/ - Linux:
$XDG_RUNTIME_DIR/agenix/
- macOS:
- Environment variables (e.g.,
$READWISE_TOKEN_FILE) point to decrypted file paths
| File | Purpose |
|---|---|
~/.ssh/id_ed25519_agenix |
Private key for decryption (same on all systems) |
~/nix-secrets/secrets.nix |
Lists which public keys can decrypt which secrets |
~/nix-secrets/*.age |
Encrypted secret files |
~/nix/modules/shared/home-secrets.nix |
Configures which secrets to decrypt and env vars |
Option A: Copy existing key from another system (recommended)
# From a system that already has secrets working
scp ~/.ssh/id_ed25519_agenix newhost:~/.ssh/id_ed25519_agenix
ssh newhost "chmod 600 ~/.ssh/id_ed25519_agenix"Option B: Create new key (only if starting fresh)
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_agenix -N ""
# Then add the public key to nix-secrets/secrets.nix and re-encryptcd ~/
git clone git@github.com:edwinhu/nix-secrets.gitIn hosts/linux/<hostname>/default.nix or hosts/darwin/<hostname>/default.nix:
{
imports = [
../../../modules/shared/home-secrets.nix
];
# ... rest of config
}cd ~/nix
nix run .#build-switch# Start new shell, then:
echo $READWISE_TOKEN_FILE
cat $READWISE_TOKEN_FILE-
Create encrypted secret file:
cd ~/nix-secrets nix run github:ryantm/agenix -- -e newsecret.age # Editor opens - paste secret value, save, exit
-
Register in secrets.nix (in nix-secrets repo):
"newsecret.age".publicKeys = users ++ systems;
-
Configure in home-secrets.nix (in nix repo):
# In age.secrets block: newsecret = { file = "${nix-secrets}/newsecret.age"; mode = "400"; }; # In home.sessionVariables block (to expose as env var): NEWSECRET_FILE = "${tempDir}/newsecret";
-
Commit both repos and rebuild:
# nix-secrets cd ~/nix-secrets && git add -A && git commit -m "Add newsecret" && git push # nix cd ~/nix && git add -A && git commit -m "Add newsecret config" && git push nix flake update nix-secrets nix run .#build-switch
cd ~/nix-secrets
nix run github:ryantm/agenix -- -e secret-name.age
git add secret-name.age && git commit -m "Update secret-name" && git push
cd ~/nix
nix flake update nix-secrets
nix run .#build-switchThe simple approach - copy the existing agenix key:
# From working system to new system
scp ~/.ssh/id_ed25519_agenix newhost:~/.ssh/id_ed25519_agenix
ssh newhost "chmod 600 ~/.ssh/id_ed25519_agenix"Then build on the new system - no changes to nix-secrets needed.
If you want the new system to have its own key:
-
Generate key on new system:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_agenix -N ""
-
Add public key to nix-secrets/secrets.nix:
let existingKey = "ssh-ed25519 AAAA... user@host1"; newKey = "ssh-ed25519 AAAA... user@newhost"; users = [ existingKey newKey ]; in { ... }
-
Re-encrypt all secrets:
cd ~/nix-secrets nix run github:ryantm/agenix -- -r git add -A && git commit -m "Add newhost key" && git push
-
Update and rebuild on all systems:
cd ~/nix nix flake update nix-secrets nix run .#build-switch
-
Update flake.nix - Add user to
userHosts:newuser = { system = "x86_64-linux"; # or "aarch64-darwin" for Mac host = "hostname"; fullName = "Full Name"; email = "email@example.com"; };
-
Add user to flake.nix:
- Add user info to the
userInfoattribute set inflake.nix - Map the user to their host in the
userHostsattribute
- Add user info to the
-
Create host configuration:
- For macOS:
hosts/darwin/hostname/ - For Linux:
hosts/linux/hostname/
- For macOS:
-
Set up secrets:
- Have the new user generate their SSH key:
ssh-keygen -t ed25519 - Get their age public key:
nix-shell -p ssh-to-age --run "ssh-to-age < ~/.ssh/id_ed25519.pub" - Add their SSH public key to
~/nix-secrets/secrets.nix - Re-encrypt secrets:
cd ~/nix-secrets && nix run github:ryantm/agenix -- -r
- Have the new user generate their SSH key:
nix/
├── flake.nix # Main configuration with user mappings
├── hosts/
│ ├── darwin/ # macOS host configurations
│ │ └── macbook-pro/ # vwh7mb's Mac
│ └── linux/ # Linux host configurations
│ └── rjds/ # eh2889's Ubuntu server
├── users/ # (Directory can be removed if empty)
├── modules/
│ ├── darwin/ # macOS-specific modules
│ ├── linux/ # Linux-specific modules
│ └── shared/ # Cross-platform modules
├── bin/ # Helper scripts
└── overlays/ # Package overlays
# Update flake inputs
nix flake update
# Rebuild with updates
nix run .#build-switch# Check flake
nix flake check
# Show flake info
nix flake show# Remove old generations
nix-collect-garbage -d
# Keep last 3 generations
nix-env --delete-generations +3If you get permission errors on macOS:
sudo chown -R $(whoami) /nix- Ensure age key exists:
ls ~/.ssh/id_ed25519_agenix - Check key permissions:
chmod 600 ~/.ssh/id_ed25519_agenix - Verify your public key is in the nix-secrets repository
- Update the secrets input:
nix flake update nix-secrets
- Check syntax:
nix flake check - Review recent changes:
git diff - Try building specific component:
nix build .#darwinConfigurations.vwh7mb.system
- Never commit private keys or unencrypted secrets
- Keep the
nix-secretsrepository private - Regularly rotate sensitive credentials
- Use secure methods for transferring keys between systems
- Key exists:
ls -la ~/.ssh/id_ed25519_agenix - Key permissions:
chmod 600 ~/.ssh/id_ed25519_agenix - Key matches nix-secrets: Public key in
~/nix-secrets/secrets.nixmust match your private key - home-secrets.nix imported: Host config must have
imports = [ ../../../modules/shared/home-secrets.nix ]; - Flake updated:
nix flake update nix-secrets - Rebuilt:
nix run .#build-switch - New shell: Start fresh shell after rebuild
| Problem | Solution |
|---|---|
READWISE_TOKEN_FILE is empty |
Start a new shell after rebuild |
| Decryption fails | Key mismatch - copy working key from another system |
| No agenix directory | home-secrets.nix not imported in host config |
agenix command not found |
Use nix run github:ryantm/agenix -- |
| Secrets not updating | Run nix flake update nix-secrets before rebuild |
- macOS:
$(getconf DARWIN_USER_TEMP_DIR)/agenix/(e.g.,/var/folders/.../T/agenix/) - Linux:
$XDG_RUNTIME_DIR/agenix/(e.g.,/run/user/1000/agenix/)
# Check if agenix service ran (Linux)
systemctl --user status agenix.service
# Check decrypted secrets exist
ls -la $XDG_RUNTIME_DIR/agenix/ # Linux
ls -la $(getconf DARWIN_USER_TEMP_DIR)/agenix/ # macOS
# Check env var is set
echo $READWISE_TOKEN_FILE
# Read secret value
cat $READWISE_TOKEN_FILE- Determinate Nix Installer
- nix-darwin
- home-manager
- agenix
- Original inspiration: dustinlyons/nixos-config
This configuration is based on dustinlyons/nixos-config, updated for compatibility with newer versions of Nix and related tools.