NixOS for Homelab: Declarative System Configuration
Discover how NixOS transforms homelab management with its declarative approach, offering reproducibility, atomic updates, and instant rollbacks for your infrastructure.
Table of Contents
- The Problem with Traditional Linux
- What Makes NixOS Different
- The Nix Language: A Brief Introduction
- Flakes: Modern Configuration Management
- Home Manager: User-Space Configuration
- Homelab Examples: Putting It All Together
- Basic System Configuration
- Running Services
- Container Management
- Rebuilding Your System
- Migration Guide: From Traditional Linux to NixOS
- 1. Document Your Current Setup
- 2. Start with a VM or Secondary Machine
- 3. Begin with Minimal Configuration
- 4. Add Services One at a Time
- 5. Adopt Version Control Early
- Comparison: NixOS vs Ansible vs Docker Compose
- When to Choose NixOS
- When to Choose Ansible
- When to Choose Docker Compose
- The Hybrid Approach
- The Trade-Off: Learning Curve vs. Long-Term Gain
- Common Pitfalls for Newcomers
- Getting Started
- Is NixOS Right for Your Homelab?
If you’ve ever spent hours debugging a server only to realize you made a manual configuration change six months ago and forgot about it, NixOS might be the solution you didn’t know you needed.
For homelab enthusiasts tired of configuration drift, unreproducible setups, and the dreaded “but it works on my machine” phenomenon, NixOS offers a fundamentally different approach: your entire system—packages, services, configurations, even the bootloader—is defined in code.
The Problem with Traditional Linux
Traditional Linux distributions follow an imperative model. You install packages with apt install or pacman -S. You edit configuration files in /etc/. You run commands to enable services. Over time, your system accumulates changes that exist nowhere but on that specific machine.
When something breaks, you’re left wondering: What did I change? Was it that package update last week? Did I modify a config file manually six months ago?
The homelab community often turns to tools like Ansible or Docker Compose to manage infrastructure. These help, but they manage applications or configuration state—not the entire system. The underlying operating system still drifts.
What Makes NixOS Different
NixOS treats your entire system as a single declarative configuration file. Rather than running commands to change your system state, you describe your desired state and let NixOS figure out how to achieve it.
This approach brings several key benefits:
Reproducibility: The same configuration file produces identical systems. Spin up a new server, apply your config, and you get an exact replica—every service, package, and setting identical to your original setup.
Atomic Updates: System changes are atomic. Either the entire update succeeds, or nothing changes. No more half-applied updates leaving your system in an inconsistent state.
Instant Rollbacks: Every system change creates a new “generation.” If an update breaks something, reboot and select the previous generation from the boot menu. Your system is back to working order in seconds.
Version Control Everything: Since your entire system is defined in configuration files, you can store those files in Git. Every change is tracked, every decision documented. Need to know why you enabled a particular service? Check the commit history.
The Nix Language: A Brief Introduction
NixOS configurations are written in the Nix language—a purely functional, lazily evaluated language designed specifically for describing software builds and configurations.
Don’t let “functional language” scare you. For homelab use, you’ll primarily work with attribute sets (similar to JSON objects) and basic functions:
# An attribute set (think: JSON object)
{
services.nginx.enable = true;
services.nginx.virtualHosts."localhost" = {
locations."/" = {
root = "/var/www/html";
};
};
}
The Nix language’s pure functional nature means functions always produce the same output for the same input—essential for reproducibility. If you define a service with certain settings today and rebuild in six months, you’ll get the exact same result.
Flakes: Modern Configuration Management
While NixOS can be configured without them, flakes have become the modern standard for managing NixOS systems. Flakes provide explicit dependency management, version pinning, and better reproducibility.
A flake is defined in a flake.nix file, which specifies:
- Inputs: Dependencies like
nixpkgs(the package repository) andhome-manager(user configuration) - Outputs: Your system configurations
{
description = "My Homelab NixOS Configuration";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
home-manager = {
url = "github:nix-community/home-manager/release-24.05";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager, ... }: {
nixosConfigurations.homelab = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
home-manager.nixosModules.home-manager
];
};
};
}
The accompanying flake.lock file pins exact versions of all dependencies. This solves the “works on my machine” problem—if you share your flake, anyone can reproduce your exact setup.
Home Manager: User-Space Configuration
NixOS manages system-level configuration beautifully, but what about your personal dotfiles, shell configurations, and user-specific packages? That’s where home-manager comes in.
Home Manager extends NixOS’s declarative approach to your user environment:
{ config, pkgs, ... }:
{
home.username = "myuser";
home.homeDirectory = "/home/myuser";
home.stateVersion = "24.05";
# Personal packages
home.packages = with pkgs; [
neovim
ripgrep
fzf
btop
];
# Git configuration
programs.git = {
enable = true;
userName = "Your Name";
userEmail = "[email protected]";
extraConfig.init.defaultBranch = "main";
};
# Shell configuration
programs.bash = {
enable = true;
shellAliases = {
ll = "ls -la";
".." = "cd ..";
update = "sudo nixos-rebuild switch --flake .#homelab";
};
};
}
With home-manager, your entire development environment—editor configs, shell preferences, installed tools—becomes as reproducible as your system configuration.
Homelab Examples: Putting It All Together
Let’s walk through a practical homelab setup. Imagine you want to run a web server, a database, and a few containers.
Basic System Configuration
# configuration.nix
{ config, pkgs, ... }:
{
imports = [
/etc/nixos/hardware-configuration.nix
];
# Bootloader
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Networking
networking.hostName = "homelab-server";
networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [ 22 80 443 ];
# Essential packages
environment.systemPackages = with pkgs; [
vim
git
htop
tmux
curl
];
# SSH access
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
settings.PermitRootLogin = "no";
};
# Automatic updates
system.autoUpgrade = {
enable = true;
allowReboot = false;
};
# Garbage collection
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 7d";
};
# Enable flakes
nix.settings.experimental-features = [ "nix-command" "flakes" ];
}
Running Services
NixOS includes service modules for most common applications. Enabling a service is often just a few lines:
# PostgreSQL database
services.postgresql = {
enable = true;
package = pkgs.postgresql_15;
ensureDatabases = [ "myapp" ];
ensureUsers = [
{
name = "myapp";
ensureDBOwnership = true;
}
];
};
# Nginx reverse proxy
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts."service.local" = {
locations."/" = {
proxyPass = "http://127.0.0.1:8080";
};
};
};
# Jellyfin media server
services.jellyfin = {
enable = true;
openFirewall = true;
};
Container Management
For applications without native NixOS modules, Docker containers integrate cleanly:
virtualisation.docker.enable = true;
virtualisation.oci-containers.containers = {
pihole = {
image = "pihole/pihole:latest";
ports = [ "53:53/tcp" "53:53/udp" "80:80" ];
volumes = [
"/etc/pihole:/etc/pihole"
"/etc/dnsmasq.d:/etc/dnsmasq.d"
];
environment = {
TZ = "America/New_York";
};
};
};
Even better, tools like compose2nix can convert your existing Docker Compose files into native NixOS container definitions, letting you manage containers declaratively.
Rebuilding Your System
After modifying your configuration, apply changes with:
sudo nixos-rebuild switch --flake .#homelab
This command:
- Evaluates your configuration
- Builds a new system generation
- Activates the new generation
- Creates a boot entry for rollback
If something goes wrong, reboot and select the previous generation. No panic, no manual restoration.
Migration Guide: From Traditional Linux to NixOS
Transitioning to NixOS requires a mindset shift, but the process can be gradual.
1. Document Your Current Setup
Before migrating, inventory your existing system:
# On Debian/Ubuntu
dpkg -l > packages.txt
# On Arch Linux
pacman -Qe > packages.txt
# On RHEL/Fedora
rpm -qa > packages.txt
2. Start with a VM or Secondary Machine
Don’t migrate production directly. Create a NixOS VM or use a secondary machine to learn the ropes. The learning curve is real, but manageable with practice.
3. Begin with Minimal Configuration
{ config, pkgs, ... }:
{
boot.loader.grub.enable = true;
networking.hostName = "my-nixos";
services.openssh.enable = true;
environment.systemPackages = with pkgs; [
vim
git
curl
];
users.users.myuser = {
isNormalUser = true;
extraGroups = [ "wheel" ];
};
}
Start simple. Add complexity incrementally.
4. Add Services One at a Time
Migrate services gradually. Enable one service, test it, then add the next. This approach makes troubleshooting easier and helps you learn NixOS service configuration patterns.
5. Adopt Version Control Early
From day one, store your configuration in Git:
cd /etc/nixos
git init
git add .
git commit -m "Initial NixOS configuration"
Every change should be a commit. This practice proves invaluable when debugging.
Comparison: NixOS vs Ansible vs Docker Compose
How does NixOS stack up against other infrastructure tools? The answer depends on your needs.
| Aspect | NixOS | Ansible | Docker Compose |
|---|---|---|---|
| Scope | Entire OS | Multi-host config | Container apps |
| Reproducibility | Highest (bit-for-bit) | High (idempotent) | Medium |
| Rollback | Instant, built-in | Manual | Manual |
| Learning Curve | Steep | Moderate | Low |
| Multi-host | Colmena/nixos-anywhere | Native | Swarm/K8s required |
| State | Config = state | External scripts | docker-compose.yml |
When to Choose NixOS
- You want complete system reproducibility
- You manage single or few machines (homelab scale)
- You value atomic updates and rollbacks
- You’re willing to invest in learning
When to Choose Ansible
- Managing heterogeneous infrastructure (different OS types)
- Multi-server deployments across many hosts
- Team familiarity with YAML/Python
- Managing existing non-NixOS systems
When to Choose Docker Compose
- Application isolation is primary goal
- Quick container orchestration needed
- Team is familiar with Docker ecosystem
- Portability across environments
The Hybrid Approach
You’re not forced to choose exclusively. Many homelabs run NixOS as the host system with Docker Compose for specific applications, combining system-level reproducibility with container flexibility.
# NixOS manages the host
virtualisation.docker.enable = true;
environment.systemPackages = with pkgs; [ docker-compose ];
# Docker Compose manages applications
# (Run compose files normally)
The Trade-Off: Learning Curve vs. Long-Term Gain
Let’s be honest: NixOS has a steeper learning curve than traditional Linux distributions. The Nix language is unlike anything most administrators have encountered. Concepts like derivations, the Nix store, and flakes require new mental models.
But here’s the payoff: Once you’ve invested that learning time, maintaining your homelab becomes trivial. Need to rebuild a server? One command spins up an identical system. Want to try a risky configuration change? Apply it, test, and if something breaks, rollback is one reboot away.
The initial time investment pays dividends over years of homelab management.
Common Pitfalls for Newcomers
Learning NixOS comes with some common mistakes:
1. Trying to Edit /etc Files Manually
In NixOS, /etc files are generated from your configuration. Edit configuration.nix instead.
2. Ignoring Home Manager
Users often put personal packages and configs in system configuration. Use home-manager for user-space settings.
3. Skipping Flakes
Legacy NixOS guides often discuss older patterns. Start with flakes from the beginning—they’re the modern standard.
4. Not Using Version Control
Every configuration should be in Git. If you’re editing files without commits, you’re missing the benefit.
5. Over-Engineering Early
Start with a simple configuration. Refactor into modules as you learn. Don’t try to build the perfect structure immediately.
Getting Started
Ready to try NixOS? Here’s a minimal path forward:
- Download the ISO: Get the minimal ISO from nixos.org
- Create a VM: Use VirtualBox, Proxmox, or your preferred virtualization platform
- Follow the Manual: The NixOS manual provides installation instructions
- Start Small: Configure a basic system with SSH access first
- Add Services: Enable one service at a time, testing after each
- Learn Flakes: Once comfortable, transition to a flake-based setup
The NixOS & Flakes book provides an excellent introduction to flakes once you’ve got the basics down.
Is NixOS Right for Your Homelab?
NixOS isn’t for everyone. If you prefer quick setup over long-term maintainability, or if you’re uncomfortable with learning new paradigms, traditional distributions may serve you better.
But if you:
- Have experienced configuration drift
- Want exact reproducibility across machines
- Value version-controlled infrastructure
- Appreciate atomic updates and instant rollbacks
- Are willing to invest in learning
Then NixOS might be exactly what your homelab needs.
The ecosystem has matured significantly. Flakes are stable. Home-manager is well-maintained. Community documentation has improved. There’s never been a better time to dive in.
Your homelab deserves infrastructure you can rebuild from a single configuration file. NixOS makes that possible.

Comments
Powered by GitHub Discussions