Table of Contents
If you’ve ever inherited a server that nobody understands, fought with outdated scripts, or tried to document a setup only to forget what half of it meant a week later — you’re not alone.
I got tired of all that.
So I started using NixOS. It’s a weird Linux distro where everything is defined in config files, but once it clicks, it’s a total game-changer. You write your system like code, commit it to Git, and suddenly your servers are predictable, reproducible, and way less stressful.
Of course, this can be used on your daily device as well, you will get the same benefits, and the great thing is, macos also supports it. 😊
In this guide we will mainly focus on servers, because it has the biggest benefits on them for most of us. Also, this guide is focused on teaching and describing the core motivators towards this approach. Do not expect advanced stuff, but a clear example is being shown later.
Let me show you why I think it’s the best approach — and how you can get started.
The Problem with Traditional Server Configuration
You know the story.
There’s a guy who sets up the server. He installs packages, tweaks configs, maybe writes a bash script or two… and then disappears. No one knows what he did, or how to recreate it. Maybe there’s a README. Maybe.
Even if you do have documentation, there’s no guarantee it’s actually correct or up to date. People forget to update it or just says “install Docker” and calls it a job done. Even worse, people think they updated it, but the system drifts over time — someone tweaks a setting directly on the machine, forgets to tell anyone, and now you’re in config hell.
Now it’s your problem.
There Has to Be a Better Way
What if the system’s configuration wasn’t a mystery?
What if you could just look at a file and know exactly how a server is supposed to be built — from user accounts, to services, to file systems, to installed packages?
Even better: what if you could build that same exact system again, on any machine, with zero guesswork?
That’s where Nix and NixOS come in.
The NixOS Approach
Nix is a powerful package manager. NixOS is a full Linux distribution built around it.
With NixOS, you write a configuration file (literally just a .nix
file) that describes exactly how your system should look. Users, packages, services, partitions, networking — everything is declared.
Then, NixOS builds the system from that config. No magic. No surprises.
It is:
- Declarative: Define what you want, not how to get there.
- Reproducible: Build the same system on any machine.
- Rollback-able: Every change creates a new “generation” you can roll back to if something breaks.
Core Concepts of Nix & NixOS
With NixOS, you don’t “configure” a system by manually running commands or editing files on the server.
Instead, you write a configuration file (or several) that declares what the system should look like.
Example defining a service and a user:
services.nginx.enable = true;
users.users.alice = {
isNormalUser = true;
extraGroups = [ "wheel" ];
};
This tells NixOS:
- “This machine should have Nginx running.”
- “There should be a user named Alice, and she should have admin rights.”
You define the desired end state, and NixOS figures out how to get there. It’s infrastructure as code, built into the OS.
Every package, service, and configuration in Nix is stored in a content-addressed store. That means:
- The system you build today can be rebuilt exactly the same way tomorrow.
- You can build the same system on your laptop, your server, your Raspberry Pi, etc.
No “it works on my machine” issues. No “depends on version X.Y of a package.” Everything is pinned and controlled.
This also means you can:
- Share configs across machines
- Roll out updates with confidence
- Archive known-good states forever
It’s like Docker, but for your whole operating system.
Every time you apply a new configuration (nixos-rebuild switch
), NixOS creates a new generation. Think of it like a snapshot of the system state at that moment.
If something breaks? Just roll back:
sudo nixos-rebuild switch --rollback
Or even pick a previous generation at boot time from the GRUB menu.
This makes experimentation safe:
- Try new services without fear
- Break your system and undo it with one command
- Update without worrying about “what if this goes wrong”
It’s like git, but for your system state.
Powerful Tools in the Nix Ecosystem
Nix isn’t just a package manager or an OS — it’s a whole ecosystem of tools that make configuring and managing systems way more powerful (and less painful). Here’s a breakdown of some of the most useful ones.
Nix Flakes
Flakes are like modules or blueprints. They help you structure your config in a reusable, versioned way.
With flakes, you can:
- Define multiple systems (e.g. laptop, server, container) in one place
- Pin dependencies to exact versions
- Share your config with others (or your future self)
Home Manager
Want to manage per-user configs like dotfiles, shells, themes, and secrets?
Home Manager lets you:
- Set up ZSH/Bash configs, themes, aliases
- Define per-user packages and environment variables
- Manage dotfiles and editor settings in a clean, consistent way
Instead of manually editing your .bashrc
, .vimrc
, .zshrc
, etc., you can define everything in a Nix config and rebuild your user environment just like your system.
programs.zsh.enable = true;
programs.zsh.ohMyZsh.enable = true;
home.file.".vimrc".text = "set number";
Each user can have their own config — no more “one size fits none” setups.
Secrets with agenix
Handling secrets is tricky in any config system. agenix makes it sane with Nix.
How it works:
- Secrets are encrypted using
age
, and stored safely in your git repo - Only specific machines (based on their SSH keys) can decrypt them at build time
- No more plain-text secrets in config files or env variables
Use case:
- API keys, passwords, tokens, DB creds
- Split secrets per host or per user
- Store and share encrypted secrets with confidence
Package Customization (Overlays)
Overlays let you override or extend existing Nix packages. Want to patch a package? Add your own version of something? Build from a Git repo instead of a release tarball? Use overlays.
Use case:
- Replace
neovim
with a custom fork - Apply patches to broken dependencies
- Add local packages without modifying the upstream repo
Super useful for fine-tuning your stack or testing bleeding-edge features.
You can write your own packages in Nix as well. The learning curve is real, but once you get the hang of it, it’s ridiculously powerful.
Use case:
- Package your in-house software with pinned dependencies
- Share CLI tools across your team without weird install instructions
- Deploy custom software in servers/containers exactly how you want
Containers and Partitions
Managing containers and partitions should not be hard if you use the right tool for it.
You can define full-blown containers in Nix, with the same declarative power as the rest of the system.
Use case:
- Define containers just like you’d define a service
- Control the base image, packages, users, and behavior
- Replace or complement Docker/Podman for full reproducibility
On the other hand, need to manage disk layouts in code too? Use disko.
Disko lets you:
- Define partitions, formats, mount points, and RAID/LVM layouts in Nix
- Use the same layout across multiple machines
- Automate the boring and error-prone setup step during installs
No more manually typing fdisk
and hoping you didn’t mess it up.
Where to learn more about Nix and NixOS?
Getting into Nix can feel overwhelming at first — but there’s a solid community and a growing number of resources that make the learning curve way more manageable.
Before you dive into flakes, overlays, or secret management, it’s important to get a grip on the basics:
- Zero to Nix — A fantastic beginner-friendly guide that walks through Nix fundamentals, package management, and basic system configuration.
- The Official NixOS Manual — Covers everything from installation to advanced modules. A bit dense, but incredibly valuable once you’re familiar with the basics.
One of the best ways to learn Nix is by studying real-world setups. Many developers share their full configs publicly — and they’re full of useful patterns, tricks, and module structures.
One of the bests: https://github.com/ryan4yin/nix-config
How I like my configuration?
Now let’s see how I would setup a completely new server.
Real Example: Deploying to Hetzner
To deploy a configuration to a remote server, you typically use tools like nixos-rebuild
with the --target-host
flag, or more advanced tools like NixOps, Morph, or Colmena. These tools manage the process of transferring configuration files and building or switching the system remotely.
You have two main strategies: build locally and send the result to the server, or send the configuration and build remotely. The choice between these depends on available compute power, bandwidth, and the desired trust model.
Building locally is often faster if your development machine is more powerful or has cached dependencies, and it avoids needing a full Nix build environment on the target. This can be done using nix copy
to transfer closures or with nixos-rebuild --build-host
to build locally.
Remote builds, on the other hand, are useful when the server has unique hardware, kernel modules, or system-specific dependencies. They also reduce the amount of data transferred over the network, since only the configuration needs to be sent, not the full derivation result.
I prefer remote building, but you can decide.
If you clone my configuration make sure you replace my SSH keys in the configuration, especially in the init-server.sh and in the user related settings.
My config: https://github.com/TheVitya/nix
Commands used in the video:
curl -sL https://raw.githubusercontent.com/TheVitya/nix/main/init-server.sh | bash
power off
ssh HOST_NAME
copy the hardware config
rsync -av –exclude-from=’rsync-exclude.txt’ . HOST_NAME:/etc/nixos/
ssh HOST_NAME
cd /etc/nixos
nixos-rebuild switch –flake .#HOST_NAME
Conclusion
Nix and NixOS is a tool that makes my workflows reliable. For a long time I was just playing around and trying to make things happen, but I had to realize that would not go long. Thankfully, this approach works now.