My Nixos Configuration

3D illustration of an approach to a wall of a spaceship with complex metallic structure in blue and purple.
BlogLink to blog
Blog
6 min read

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:

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?"
art atmospheric breaks breakbeat buddhism chicago code disco fediverse fiction funk furry house house music kubernetes lgbt linux logseq mastodon mental health movies music nixos obsidian personal philosophy pkm poetry prompt second life social software soul technology writing