My Nixos Configuration
A walkthrough of my latest NixOS configuration using Flakes, Home Manager, and a poor man's secrets management solution. Photo by Alejandro Miranda from PxHere: https://pxhere.com/en/photo/1596629
Contents
Note: this blog was originally published March 2, 2024.
Introduction ⚓
I’m officially a NixOS fanboy. I’ve seen the light, and it is declarative, idempotent, and reproducible. If you don’t know what I’m talking about, check out my earlier blog on what NixOS is and why it’s a big deal 🔗. The short answer is: NixOS lets you define your entire system’s configuration in plain text, so you can very easily rebuild any system from scratch.
In this blog post, I want to show off my configuration that I’ve built over almost a year of using NixOS full time. I’ve been gradually bolting on new features and functions as I’ve gotten more comfortable with Nix, and now that I’ve had time to refine my configuration, I’m excited to share it. A lot of this might look janky (and it is), but it’s worked well for me so far, and I hope it can give you some inspiration for your own Nix journey!
Pulling my configuration from GitHub ⚓
My configuration is hosted publicly on GitHub. You can check it out here 🔗, or if you’re feeling brave, clone it:
git clone https://github.com/8bitbuddhist/nix-configuration.git
You should see four folders and three files:
aires@Khanda ~/Development/nix-configuration (main) $ ls --group-directories-first -gh
total 32K
drwxr-xr-x 1 users 84 Oct 18 11:29 bin
drwxr-xr-x 1 users 168 Oct 6 13:42 hosts
drwxr-xr-x 1 users 150 Oct 16 16:07 modules
drwxr-xr-x 1 users 180 Oct 17 10:51 packages
-rw-r--r-- 1 users 13K Oct 18 11:31 flake.lock
-rw-r--r-- 1 users 4.3K Oct 17 13:41 flake.nix
-rw-r--r-- 1 users 6.3K Oct 18 11:29 README.md
Let’s start with the folders:
bin
contains executable helper scripts.format-drives.sh
helps with initial setup and installation, andnixos-operations-script.sh
is a nixos-rebuild wrapper.hosts
contains host-specific configuration files, as well as common configurations shared across hosts.modules
defines every shared/common NixOS definition, from applications to services to user files. When creating this folder, I tried to follow the Nix Options syntax. In other words, every module isimported
automatically, but you enable or disable specific modules in the host config. For example, there’s an entire module for installing and configuring Gnome 🔗, but to actually enable Gnome on a host, you need to addaux.system.ui.gnome.enable = true
to your host configuration.packages
is for any custom packages.
Managing secrets ⚓
Some of the data in my configuration is sensitive, but necessary. For example, SSH configurations, hashed user account passwords, domain names, etc. These are stored in the modules/secrets
folder, and I’m using
git-crypt 🔗 to encrypt them. When decrypted, this is just a regular nix
file containing an option with default values (those default values being my secrets). You can drop in your own secrets module using whatever transparent encryption method you prefer. Just keep in mind that any secrets you define using this method will become world-readable in the Nix store.
I took this approach because I feel it’s a good balance between ease-of-use and security. I tried (and failed) to get Sops-nix 🔗 and Agenix running, and I wanted to do weird stuff like encrypt non-Nix files. Git-crypt makes this entire process transparent.
The downside to this approach is that it’s not 100% secure. When running nixos-rebuild
, Nix handles my secrets files the same way it does any other file, which means storing it in /nix/store
as a plain text, world-readable file. Anyone with access to my computers, including other users, can read my secrets files by browsing the Nix store. I’m the only one who admins these systems, so I don’t mind the risk, but be wary if you use this method on a shared system.
The entrypoint: flake.nix ⚓
The main entrypoint to this configuration is flake.nix
. Here’s where I import a bunch of external Flakes including Home-manager, Nix-flatpak (for declarative Flatpaks), and others. I use the stable NixOS branch (currently 24.05), but I also have the unstable repository available in case I want newer software. For stable packages, I can use pkgs.<packagename>
, and for unstable packages, I can use pkgs.unstable.<packagename>
.
Check it out here 🔗.
nixpkgs.overlays = [
(final: _prev: {
# Allow packages from the unstable repo by using 'pkgs.unstable'
unstable = import inputs.nixpkgs-unstable {
system = final.system;
config.allowUnfree = true;
};
})
];
Each host gets a separate folder under hosts/
. Normally each host just has two files: default.nix
and hardware-configuration.nix
. In some cases,
like with Shura 🔗, I have some extra files that get deployed specifically to that host.
Each host imports
a set of default modules defined in flake.nix
🔗. This set includes autoimport.nix
, which is responsible for automatically importing every Nix file in the modules
folder.
The modules ⚓
Each aspect of my systems are split out into different modules. For the most part, these modules are self-contained, but some reference (or require) others. For example, enabling Gnome indirectly enables both audio and Flatpaks 🔗. This is mostly to make sure I don’t forget something critical when enabling/disabling certain features.
Module options follow a proposed template created for
Auxolotl 🔗, a sort of spin-off distribution of NixOS. The top-level namespace for options is aux.system
, and the modules form a hierarchy beneath that. For example, here’s the configuration for my Microsoft Surface Pro laptop:
aux.system = {
# Enable to allow unfree (e.g. closed source) packages.
# Some settings may override this (e.g. enabling Nvidia GPU support).
# https://nixos.org/manual/nixpkgs/stable/#sec-allow-unfree
allowUnfree = true;
apps = {
development.enable = true;
media.enable = true;
office.enable = true;
recording.enable = true;
social.enable = true;
writing.enable = true;
};
# Enable Secure Boot support.
bootloader = {
enable = true;
secureboot.enable = true;
tpm2.enable = true;
};
# Change the default text editor. Options are "emacs", "nano", or "vim".
editor = "nano";
# Enable GPU support.
gpu.intel.enable = true;
# Change how long old generations are kept for.
retentionPeriod = "14d";
services = {
autoUpgrade = {
enable = true;
configDir = config.secrets.nixConfigFolder;
onCalendar = "weekly";
user = config.users.users.aires.name;
};
virtualization.enable = true;
};
ui = {
desktops.gnome = {
enable = true;
experimental = {
tripleBuffering.enable = true;
vrr.enable = true;
};
};
flatpak = {
# Enable Flatpak support.
enable = true;
# Define Flatpak packages to install.
packages = [
"com.github.tchx84.Flatseal"
"com.github.wwmm.easyeffects"
"md.obsidian.Obsidian"
"org.keepassxc.KeePassXC"
"org.mozilla.firefox"
];
useBindFS = true;
};
};
I tried to follow the
NixOS options 🔗 syntax when making these. Like I mentioned, all modules are imported by default, and you selectively enable them for each host. Some modules have additional options, like
Duplicacy Web 🔗, which requires a directory for storing its own configuration. Otherwise, it’s a simple true
or false
.
Beautifying the code ⚓
The last thing I did before making this public is to try and clean up my Nix code and follow standard formatting. For that, I used a linter called nixfmt 🔗. Using nixfmt is simple: just specify the linting style you want to use in your flake.nix file, then run:
nix fmt
It’ll recursively look for any .nix
files and adjust their formatting automatically. This will edit and save your files, so I’d only recommend doing this if you have a backup of your config files, or you’ve checked them into your repository already! You can also add it as a
Git hook 🔗 so it runs whenever you commit files to your repo.
Building your Nix skills ⚓
Sometimes the best way to learn about Nix is to poke around other people’s configurations and see how they do things. That’s how I got this far, and I appreciate everyone in the community brave enough to post their configurations online for others to read.
Want to check out other people’s configurations, or learn more about how to write your own? My NixOS 🔗 is a great resource, but so is simply searching “nixos configuration github” in DuckDuckGo or your preferred search engine. You can also check out the nixos-configuration topic in GitHub 🔗 to browse repos.
Most importantly, have fun! There’s a lot to learn with Nix, so try not to feel too overwhelmed. Take your time, make iterative changes, and remember to commit and back up your files frequently! One of NixOS’ strengths is that you can always open a previous generation, so even if you completely mess up your config files, you can revert back just by rebooting (assuming you didn’t delete your past generations).
Previous: "Why is enabling automatic updates in NixOS so hard?" |