My Nixos Configuration
A walkthrough of my latest NixOS configuration using Flakes, Home Manager, Snowfall, treefmt, 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, updated on July 9, 2024, and updated again on December 7, 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 one year (!) of using NixOS full-time. Recently I completely revised my config by using the Snowfall library 🔗, and it seemed as good a time as any to update this blog post.
Even though I feel pretty confident with my config now, I’m still learning. Expect to find some weirdness and jankiness.
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 this layout:
aires@Khanda ~/Development/nix-configuration (main) $ ls --group-directories-first -gh
total 40K
drwxr-xr-x 1 users 126 Dec 6 20:08 bin
drwxr-xr-x 1 users 22 Dec 6 20:08 lib
drwxr-xr-x 1 users 10 Dec 6 20:08 modules
drwxr-xr-x 1 users 8 Dec 6 20:08 overlays
drwxr-xr-x 1 users 86 Dec 7 11:53 packages
drwxr-xr-x 1 users 160 Dec 6 20:08 systems
-rw-r--r-- 1 users 14K Dec 7 11:46 flake.lock
-rw-r--r-- 1 users 4.5K Dec 6 20:08 flake.nix
-rw-r--r-- 1 users 5.8K Dec 6 20:08 README.md
-rw-r--r-- 1 users 420 Dec 7 11:41 treefmt.nix
Let’s break each of these down:
bin
contains shell scripts for various functions:format-drives.sh
is a helper for installing NixOS onto new systems.nixos-operations-script.sh
is a wrapper script fornixos-rebuild
that adds helpers for automatic updates and synchronizing the repository with git.compile-manuscript.sh
is a random bash script that I use to compile stories and books that I write in Markdown into PDF and ePub.
lib
contains helper functions for the config.modules
provides a huge number of options for configuring the system: applications, services, user accounts, etc.overlays
contains…well…overlays. As of this writing I only have one overlay here that makes thenixos-unstable
repository available aspkgs.unstable
.packages
contains packages (surprise!).systems
contains host-specific configuration files, as well as common configurations shared across hosts.
My repo follows the modules syntax 🔗. Every module is imported automatically, and you enable or disable specific modules in the host’s config. For example, there’s an entire module for installing and configuring Gnome 🔗, but to actually enable Gnome on a host, you need to add:
config.${namespace}.ui.desktops.gnome.enable = true;
${namespace} is a Snowfall-specific attribute that specifies that the module can be found in this config. Other users’ configs can have different namespaces, and this prevents options from conflicting with each other.
Note on secrets management ⚓
There’s some stuff that I need to run my systems, but I don’t want floating around the Internet: passwords, SSH configurations, certificates, even some packages containing proprietary files. I looked into secrets management tools like Sops-nix and Agenix, but they didn’t fit my needs: mainly, I wanted a secrets management solution that treats my secrets like any other Nix module.
I went with a solution outside the Nix ecosystem:
Transcrypt 🔗. Transcrypt is a script that transparently encrypts & decrypts any files or folders in your Git repo. You just set a password and encryption cipher, add a Transcrypt filter to your .gitattributes
file, then stage and commit your files.
modules/nixos/secrets/** filter=crypt diff=crypt merge=crypt
The major downside to this is that Nix isn’t aware that your files are private, and it will happily store the decrypted version in the Nix store, which is readable for all users. This is like having your passwords unencrypted in a text file. If you’re the only person using your PC, and you feel confident about your security practices, it should be fine (famous last words). But if this is a PC you share, I’d highly recommend trying Sops or Agenix instead.
The entrypoint: flake.nix ⚓
The main entrypoint to this configuration is flake.nix
. Here’s where I import a bunch of external Flakes including Snowfall, Home-manager, Nix-flatpak (for declarative Flatpaks), and others. I follow the stable NixOS releases by default (24.11 as of this writing), but sometimes it’s nice to have the latest and greatest, so I have the unstable repo available as an option. I can reference the stable repo using pkgs.[package name]
, and with the help of an overlay, I can reference unstable using pkgs.unstable.[package name]
.
# Configure nixpkgs
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
Snowfall takes a few basic options: The name of your configuration, the namespace you want to use, the root directory for your flake.nix file, etc. Below that, you declare your systems and the modules you want to import for those systems. Snowfall will automagically figure out where your systems’ configuration files are stored as long as you
follow its recommended directory structure 🔗. That’s why my systems
folder is organized first by CPU architecture, then by hostname.
In case I haven’t mentioned, Snowfall is very opinionated about how your configuration is structured 😅. But the benefit is that you don’t have to add a bunch of imports
statements everywhere.
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.
I tried to follow the
NixOS Options syntax 🔗 when making these. Like mentioned earlier, 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
.
# Example configuration for my Surface Laptop
${namespace} = {
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;
};
# Set nano as the default text editor
editor = "nano";
# Enable GPU support.
gpu.intel.enable = true;
# Enable power management via power-profiles-daemon
powerManagement.enable = true;
services = {
autoUpgrade = {
enable = true;
configDir = config.secrets.nixConfigFolder;
onCalendar = "weekly";
user = config.users.users.aires.name;
};
virtualization.enable = true;
};
ui.desktops.gnome.enable = true;
users.aires.enable = true;
};
Beautifying your code ⚓
Nix code can get really messy. Fortunately, there are tools that will automatically format your .nix
(and other code) files for you. The one I use is called
treefmt 🔗, specifically
treefmt-nix 🔗.
Treefmt works by pulling in other existing programs and applying them to matching files. For example, if you have bash scripts you want formatted, you can tell treefmt to pull in
beautysh
🔗 or
shellcheck
🔗. You can, of course, use any available Nix formatter, including the RFC format (
which is what I use 🔗). I also have
deadnix 🔗 pulling out and cleaning up unused code. It’s mostly for aesthetics, but it’s helped me catch a couple of bugs, so I recommend it. All you need to do is set it up and run nix fmt
.
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!
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 willing to share their configurations online.
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!
Previous: "Why is enabling automatic updates in NixOS so hard?" |