This is a demonstration and tutorial project on how to build and manage system configurations using NixOS, Flakes, and sops-nix. Through this project, you can learn how to declaratively manage system configurations and hardware profiles across multiple machines, as well as securely handle sensitive data (Secrets).
This project integrates the following powerful and modern tools in the Nix ecosystem:
- NixOS & Flakes: Core system and package management for fully reproducible system builds.
- sops-nix: Integrates with Mozilla SOPS to securely encrypt and manage secrets (e.g., passwords, API Tokens) in Nix systems.
- disko: Declarative disk partitioning and formatting tool.
- nixos-facter: Generates hardware configuration reports (
facter.json), eliminating the need to manually write traditionalhardware-configuration.nixfiles. - nixos-anywhere: Fully automated NixOS installation to any remote machine via SSH.
- deploy-rs: A flexible NixOS remote deployment tool that builds and pushes Flake configurations to remote servers.
- just: A handy command runner that encapsulates complex commands commonly used in this project.
flake.nix: The entry point for the entire system configuration. It definesnixpkgsdependencies, system configurations for multiple machines likeinit,blinker, andpoach, as well asdeploy-rsdeployment nodes.sops.nix: Thesops-nixrelated module configuration, specifying how to decrypt secrets using the machine's local SSH Host Key..sops.yaml: The SOPS configuration file, defining encryption keys (Age Keys) and encryption rules for specific files.secrets.yaml: Secret files encrypted via SOPS (e.g., Cloudflare API Token).justfile: Contains automated script commands, such as building, initializing machines, or pushing updates.facter.json/poach/facter.json: Hardware configuration reports generated bynixos-facter.blinker/,poach/,templates/: Directories containing specific NixOS configuration files for various nodes (machines) and templates.
This project does not require the host machine to run NixOS. You only need to have the Nix package manager (with Flakes enabled) and just installed. Please install them according to your operating system or distribution's standard methods.
For Nix, ensure Flakes support is enabled by adding the following to ~/.config/nix/nix.conf:
experimental-features = nix-command flakesNote: The following steps will use standard commands like mkpasswd, ssh-to-age, age-keygen, and sops. Please ensure these tools are installed on your host machine via your package manager.
⚠️ Important: By default, thehashedPasswordandopenssh.authorizedKeys.keysfor the default user in the provided template configurations (templates/minimal/configuration.nixandblinker/configuration.nix) are set to empty strings"". You must generate and fill in your own values before deploying.
-
Generate a Hashed Password: Use
mkpasswdto generate a secure password hash.mkpasswd -m sha-512
Copy the generated hash and paste it into the
hashedPasswordfield in yourconfiguration.nix. -
Set your SSH Public Key: Ensure you have generated an SSH key pair (e.g., using
ssh-keygen -t ed25519). Copy the contents of your public key (usually~/.ssh/id_ed25519.pub) and paste it into theopenssh.authorizedKeys.keyslist.
With the just script, you can install the init template configuration to a new server with a single click.
Replace <Target_IP> with the actual IP address, which will automatically log in using root:
just nixos-anywhere <Target_IP>Behind the scenes, this command calls nixos-anywhere, combines nixos-facter to dynamically generate the target machine's hardware configuration, and uses disko for disk partitioning to achieve a "one-click installation." After the installation is complete, the machine will automatically reboot and generate its own unique SSH Host Key.
After the basic installation is complete, we can then set up the specific system configuration and secrets for it. This project uses Age as the encryption backend for sops-nix.
⚠️ Warning: Thekey.txtin the project directory is for demonstration and testing purposes only. In a real environment, never commit files containing private keys to a version control system (Git)! Your private keys should be properly kept in a secure local directory (e.g.,~/.config/sops/age/keys.txt).
We strongly recommend following the best practices in this article by Michael Stapelberg — deriving the Age key directly from your existing SSH key:
-
Generate the administrator's Age key from a personal SSH private key: Use
ssh-to-ageto convert your SSH private key into an Age private key, and save it in the default location that SOPS reads.mkdir -p ~/.config/sops/age/ ssh-to-age -private-key -i ~/.ssh/id_ed25519 -o ~/.config/sops/age/keys.txt
Then, use
age-keygen -y ~/.config/sops/age/keys.txtto get the Age public key (Recipient) corresponding to this private key, so it can be added to the configuration later. -
Retrieve the newly installed server's Age public key: Directly read the SSH Host public key of the newly installed machine and convert it to an Age public key:
ssh nixos@<Target_IP> cat /etc/ssh/ssh_host_ed25519_key.pub | nix run nixpkgs#ssh-to-age
-
Update SOPS encryption rules (
.sops.yaml): Add the "Administrator's (Your) Age Public Key" and "Server's Age Public Key" obtained in the above steps to thekeyssection of.sops.yaml, and adjust thecreation_rulesto ensure that secret files are encrypted with both public keys simultaneously. This way, you can decrypt and edit locally, while the server can decrypt and read at runtime. -
Edit the secrets: Since we have stored the private key in
~/.config/sops/age/keys.txt(SOPS's default reading path), editing encrypted secret files becomes very simple:sops secrets.yaml
After editing and saving, SOPS will automatically re-encrypt the file.
Once the target machine has been initialized and the secrets (sops) have been set up for that machine, you can push the full configuration profile (e.g., blinker) to the server:
# Build the flake by default
just build
# Deploy the configuration to a specific node (e.g., blinker)
just deploy blinkerThis command reads the deploy.nodes defined in flake.nix, compiles the system configuration locally, and then pushes it to the specified remote machine and automatically applies the changes (Switch). At this time, the server will correctly load sops-nix and use its SSH Host Key to decrypt the necessary data.
If you want to use this project as a basis to manage your own cluster:
- Add a new machine:
- Create a folder for the corresponding machine and write a dedicated
configuration.nix. - Use
nixos-facterto get the machine'sfacter.jsonand place it in the project. - Add the definition of this machine in the
nixosConfigurationsofflake.nix. - Add the connection and deployment settings for this machine in the
deploy.nodesblock.
- Create a folder for the corresponding machine and write a dedicated
- Switch NixOS channels:
You can refer to the examples of
blinkerandpoachto flexibly apply the stable channel (nixpkgs) or the unstable development channel (nixpkgs-unstable) for different machines.