Personal NixOS/MacOS declarative config.
  • Nix 48.6%
  • Lua 41.2%
  • Jinja 5.5%
  • Nushell 3.7%
  • CSS 0.5%
  • Other 0.5%
Find a file
2026-11-13 21:21:25 +01:00
.opencode chore: various updates 2026-04-29 21:57:03 +02:00
archive chore: remove stale parts of configuration 2026-10-05 12:54:31 +02:00
aspects feat: remove explicit fzf support - nushell do not need it 2026-11-13 21:21:25 +01:00
hosts feat: propogate skills to claude 2026-11-03 15:02:05 +01:00
my chore: revert no longer actual patch 2026-11-03 14:54:14 +01:00
website fix: proper cursor-pointers for website 2026-10-17 11:34:49 +02:00
.gitignore feat(website): source URL 2026-10-09 15:44:31 +02:00
.luarc.json feat(nvim): reorganize LSP keybindings under <leader>l 2025-06-21 21:01:29 +02:00
consts.nix feat: switch to per-host GPG keys 2026-05-04 17:16:30 +02:00
flake.lock feat: remove explicit fzf support - nushell do not need it 2026-11-13 21:21:25 +01:00
flake.nix feat: remove explicit fzf support - nushell do not need it 2026-11-13 21:21:25 +01:00
opencode.jsonc feat: update OpenCode and related tooling 2026-10-25 00:52:21 +02:00
README.md feat: proper self-describing website building 2026-10-11 19:07:47 +02:00
secrets.nix feat: use API key with ctx7 2026-10-24 13:54:50 +02:00
statix.toml chore: use inherit syntax for cleaner nix imports 2025-02-16 00:09:27 +01:00

Personal Nix-based Configuration for my Machines

Brainmade

Personal dotfiles and system configurations managed with Nix flakes.

This repo is brainmade. I use LLMs mostly to speedup mundane tasks, write boilerplates or to explore options. I never trust LLMs to decide what is good and what is bad.

This Nix Flake follow ideas of dendritic pattern.

What this repo is

This repository contains my personal configurations for NixOS, Ubuntu (via system-manager) and formerly macOS machines. It is done as a Nix flake which implements a top-level orchestrator for nearly all my configurations and tools.

Path
flake.nix Entry point that wires everything together.
my/ All my custom Nix module options and helpers, all namespaced under my.*.
aspects/ Feature bundles: groups of NixOS, home-manager, and other modules. Can have associated folders with config files.
hosts/ Per-machine definitions that define system type and list of aspects to apply. Also contains host-specific adjustments.
secrets.nix Declares what's encrypted and which SSH keys can decrypt each secret.
website/ Static configuration overview: metadata and build modules, Nushell builder, templates, styles, and fonts.
archive/ Patterns documented for future reference. I add document here when remove some features from implementation. For example, when I remove support for Darwin when no longer have any macOS machine.

Standalone flake outputs

Some flake outputs are useful on their own — you don't need to install my system or home-manager configurations to use them.

Output Description Command
#meta Builds a JSON file with host details, aspect descriptions, etc. nix build git+https://codeberg.org/ffloyd/nix#meta
#website Builds the static configuration overview with host-specific adjustments and source dialogs for hosts, aspects, features, and adjustments. nix build git+https://codeberg.org/ffloyd/nix#website
#tidewave-app Tidewave desktop app packaged for x86_64-linux platform nix run git+https://codeberg.org/ffloyd/nix#tidewave-app
#tidewave-cli Tidewave CLI packaged for x86_64-linux platform nix run git+https://codeberg.org/ffloyd/nix#tidewave-cli

In a locally cloned repo, use . instead of the URL: nix run .#tidewave-app.

To build and view the website locally:

nix build .#website && xdg-open result/index.html

If you are new to Nix

If you are just curious about Nix, this repo might look a bit dense at first, and that is normal. As an entry point to the Nix ecosystem, I recommend to check out the following resources:

  • Official Nix(OS) website - for general information about Nix and NixOS.
  • Vimjoyer Youtube channel - this guy is amazing at explaining Nix and NixOS concepts in a very approachable and practical way. His channel was my entry point to Nix and I highly recommend it.
  • Understanding of the following tools is essential to understand this repo. Also check related Vimjoyer's videos.

Why Nix?

Now I can do "ASDF peasants" jokes.

On a more serious note, well... it's a toolkit that

  • can manage dotfiles
  • supports NixOS, other Linux system and even macOS
  • allows you to configure your system declaratively and reproducibly
  • makes reverting any changes as easy as reverting a commit
  • replaces ASDF, mise and similar "dev shell" tools. Provides reproducibility, not just replayability.
  • can be used to define and build Docker images (and they will be reproducible, not just replayable like with Dockerfiles)
  • and more

Just a single toolkit. While ASDF, homebrew, and other tools I mentioned much simpler and easier to use, they underperform in many aspects in comparison to Nix. One I mentioned here is the reproducibility vs replayability. Let me explain on example of a Dockerfile. When you have line like RUN apt-get update && apt-get install -y nodejs in your Dockerfile, it will install the latest version of nodejs at the moment of building the image. This is replayable, but not reproducible, because the same Dockerfile can produce different outputs at different times. It's only the tip of the iceberg, Dockerfiles are not designed for reproducibility in general and have non-deterministic nature. Like ASDF, like mise, and many other popular tools.

It's ok for many contexts to not have those strong guarantees and take some risks in exchange for simplicity. But personally, I prefer to master complex toolkit that is fun to learn, rather than waste time on simpler but boring tasks like fixing broken ASDF shims, dealing with Homebrew's quirks after system upgrade, and similar. With Nix such issues are less likely to happen, and when they do, they are usually easier and more comfortable and fun to fix. Moreover, Nix has a massive amount of interesting engineering decisions to learn from and become better engineer.

You cannot avoid having problems and errors, but you can choose which kind of errors you want to deal with and pick the ones that gives you more fun.

In the end, I may not save a lot of time with Nix - it takes time to learn and Nix is pretty complex. In exchange, I avoid a lot of frustration and have a lot of fun in the process. After all, let be honest - we do things not for efficiency, satisfaction is what we are really after, and Nix is a very satisfying tool to master. Like Vim, Emacs, Terraform - I have similar experience with them.

Setup on NixOS

The repository should be cloned into the nix folder in the user's home directory.

Installation command (in the root folder of the repository):

sudo nixos-rebuild switch --flake .

Setup on Ubuntu

The repository should be cloned into the nix folder in the user's home directory.

Install Nix with flakes enabled, then run:

# System-level configuration
nix run 'github:numtide/system-manager' -- switch --flake . --sudo

# User-level configuration
nix run home-manager/master -- switch --flake .

Configuration Philosophy

This configuration leans toward an objective-based organization approach instead of a more traditional category-based organization. Rather than grouping configurations by technical category (LSP, UI, Git tools), (when possible) modules are organized around specific objectives or goals.

When it makes sense, each feature module's naming and documentation focus on what it needs to achieve rather than what technology it uses. This approach is inspired by OKR (Objectives and Key Results) methodology, adapted for configuration management. The same philosophy drives both the Neovim configuration and the Nix module structure.

It helps me stay focused on the end goals and avoid concentrating too much on technical details.

Not every module here strictly follows this philosophy and it's by design. If you have done OKRs, you would understand that defining good objectives is hard and often takes a lot of time and practice. While it forces you to ask yourself "why" more often, sometimes it's just better to put something in a separate file without overthinking.

Architecture

Over time I designed or adopted various concepts to organize configurations.

flake.parts and dendritic pattern

I was truly impressed by the idea of a dendritic pattern. TL;DR: it is about shifting from:

Each Nix file structure is defined by its caller. You need to understand where a file is used to understand how to read it. Shared values are passed via specialArgs and imports.

to something less cognitively demanding:

Each Nix file except for flake.nix is a flake.parts module. Knowing what flake.parts is and how it works is enough to understand the structure of any file in the repository. You can directly access any value from any other module.

It also allows all Nix files to be merged together automatically using import-tree instead of manually importing each file.

Oh my

I had to go beyond standard flake.parts capabilities. All Nix files that implement custom options and their behavior are located in the my/ folder. All custom options are prefixed with my. to avoid potential conflicts with options from other flake.parts modules.

Aspects and hosts

Simply speaking, main building blocks of the configuration are:

  • NixOS modules - for NixOS system-level configuration.
  • home-manager modules - for user-level configurations and for shared modules that work both for NixOS and macOS.
  • (in the past) nix-darwin modules - for macOS system-level configurations.
  • system-manager modules - for system-level configs on Ubuntu.

While for one host it would be ok to define those modules and import them into one machine configuration, I have multiple machines and their module sets are not identical. So I introduced my.hosts and my.aspects options.

Aspect is a concrete, replaceable implementation of a capability. It is a collection of modules that should provide that capability as a coherent unit. For example, noctalia-5-desktop implements one desktop environment using Niri, Noctalia v5, and its greeter on NixOS. A different desktop environment can replace it by providing another aspect and changing the host's aspect list.

Host is a concrete machine definition that I use, with its hostname, username, system type, and list of aspects that should be enabled on it.

Actual machine configurations are generated from host definitions and aspects. See my/hosts.nix and my/aspects.nix for options documentation and implementation details.

The aspects/ folder contains aspect definitions. Each aspect is either a single file or a folder with multiple implementation files. A folder-based aspect normally has an a_meta.nix file for metadata.

The hosts/ folder contains host definitions (hosts/host-alias.nix) and host-specific modules (hosts/host-alias/*.nix).

Shared constants

my.consts is a loosely typed attrset that serves as a container for global constants. flake.parts makes it available for any Nix file. Shared values like email addresses and locale settings are set in consts.nix.

Secret management

Secrets are managed with agenix. Decription happens at activation time into an in-memory file system (or ~/.secrets/ for home-manager). Encrypted .age files live in companion directories next to their aspect definitions. The secrets.nix file declares which SSH keys can decrypt each secret.

Helpers

Nix has an impressive standard library, but sometimes I need specific functions that are not available there. When possible, I keep them module-local. For the rest, I have my.helpers - a collection of helper functions that are used across different modules and aspects. Check my/helpers.nix for details.

Directly symlinked dotfiles

Configuration files generated by Nix are immutable. Updating them requires re-evaluating the configuration, which takes 20-50 seconds on my machines. While it is ok or even beneficial for most of my cases, it is not ideal for dotfiles that I edit often or need a faster feedback loop for.

Good examples are AI-related files, Neovim configuration, and terminal configuration.

Dotfiles live in companion directories next to their aspect definitions (e.g. aspects/noctalia-5-desktop/niri.kdl next to aspects/noctalia-5-desktop/niri.nix). Not every file in those folders is symlinked — only the ones I edit often. my.helpers.mkDirectSymlink creates symlinks from the repository directly to the home directory, bypassing the Nix store. It takes a path relative to the calling file and resolves it to a repo-relative path under ~/nix/.

# In aspects/terminal.nix:
home.file.".config/kitty/kitty.conf".source = mkDirectSymlink config ./terminal/kitty.conf;

This allows editing dotfiles in place without waiting for Nix to rebuild.

The reverse pattern also exists: host-local settings that are generated from Nix expressions and then included by a directly symlinked dotfile. For example, aspects/noctalia-5-desktop/niri.kdl includes a generated ~/.config/niri/host-local.kdl, which each host produces in its hosts/<host>/niri.nix module.

Shell command naming

Shell aliases and custom Nushell functions follow a shared convention so that personal helpers are easy to discover through completion:

  • Local system and environment maintenance tasks use os-<command>; e. g.: os-rebuild, os-firmware-check-updates.
  • Other non-trivial aliases and custom functions use ,<category>-<command>, such as ,server-ssh, ,gpg-export-public, and ,pdf-compress-very-much.
  • Simple shortcuts for common commands stay unprefixed: la (for ls -la), vi (for nvim).

Type , followed by Tab to discover category-based helpers, or narrow the list with a prefix such as ,server-. Use os- followed by Tab for system tasks.

doit { os-rebuild }
doit { ,server-update }

Common tasks

Inspection

  • Overview of hosts, aspects, and adjustments: nix build .#website && xdg-open result/index.html

Maintenance

  • Format code: nix fmt .
  • Check flake validity: nix flake check --all-systems
    • for the deeper check that includes buiding everything that can be built locally: nix flake check --build-all (not only the current host configuration)
  • Upgrade dependencies: nix flake update
  • Upgrade specific input: nix flake update agenix

Secrets

  • Edit a secret: agenix -e aspects/base/agenix/test.age
  • Rekey all secrets after changing recipients: agenix --rekey
  • The secrets.nix file declares which SSH public keys can decrypt each secret.

Validation & Testing (NixOS)

  • Test if configuration builds: nixos-rebuild dry-build --flake .
  • Preview activation changes: nixos-rebuild dry-activate --flake .
  • Lint: statix check

Applying Changes

See aliases in ./aspects/base/nix.nix.