You finished nixos-install. The system boots. Now what?
Most NixOS guides end right after installation. They get you to a login prompt, say "good luck," and leave you staring at a terminal wondering if you should immediately start installing packages the old way. Don't.
NixOS is not Debian. It's not Arch. The habits you carry from those systems will fight you here — especially in the first hour after install. I found this out the hard way after moving my own daily driver from Debian 13 with KDE Plasma over to NixOS. I kept the terminal-only workflow I'd built up, but the package management mental model was a completely different animal, and the first few days involved a lot of "wait, why didn't that stick after rebuild?" This post covers what to do first, what to avoid, and the practices that would've saved me some of that pain.
The First Five Minutes
Before you install a single package, do these things:
1. Set your password.
passwd
This sounds obvious. It's the thing most people forget because the installer doesn't prompt you for it when you create your user in configuration.nix. You set an initialPassword during install — that's a placeholder, not your real password. Change it immediately.
2. Check your network.
ping -c 3 google.com
If this fails, your networking.networkmanager.enable = true; in configuration.nix might not be set, or you need to configure Wi-Fi separately. Fix this before anything else — you need internet to download packages.
3. Open your configuration file.
sudo nano /etc/nixos/configuration.nix
This file is the single source of truth for your entire system. Every package, every service, every user setting lives here. Get comfortable with it now, because you'll spend a lot of time here.
The NixOS Mental Shift
On traditional Linux, you install things imperatively:
apt install firefox
apt install vim
On NixOS, you declare what you want in configuration.nix, then rebuild:
environment.systemPackages = with pkgs; [
vim
wget
git
firefox
];
Then:
sudo nixos-rebuild switch
This is the fundamental shift. You don't install packages. You declare them. The nixos-rebuild command reads your config, resolves dependencies, downloads or builds what's needed, and makes it available system-wide.
The mental model: your config file is a recipe. nixos-rebuild switch is the chef. You write the recipe, the chef makes the meal. It's a genuinely different way to think about a machine than years of apt/dnf/pacman muscle memory prepare you for — give yourself a week or two before it clicks.
Best Practice #1: Declare Everything in configuration.nix
This is the cardinal rule. Every package you need should be declared in your config, not installed ad-hoc.
Why? Because your config is reproducible. If your hard drive dies tomorrow, you can reinstall NixOS, paste your config, run nixos-rebuild switch, and have your exact system back. That's impossible if you installed packages with nix-env or nix profile install — those leave no trace in your config.
Where to put things:
- System-wide packages (things every user needs):
environment.systemPackages - User-specific packages (your personal tools): Use Home Manager (covered below)
# In configuration.nix
environment.systemPackages = with pkgs; [
vim
wget
curl
git
unzip
htop
];
Best Practice #2: Set Up a Proper User
Don't leave your user account bare. Configure it properly in configuration.nix:
users.users.yourname = {
isNormalUser = true;
description = "Your Name";
extraGroups = [ "wheel" "networkmanager" "video" "audio" ];
shell = pkgs.zsh;
};
Key groups:
- wheel: Gives you
sudoaccess - networkmanager: Lets you manage Wi-Fi without root
- video/audio: Hardware access for media
Also enable the shell you want:
programs.zsh.enable = true;
Best Practice #3: Don't Use nix-env
This is the most common mistake new NixOS users make. On other distros, you install things with a package manager command. On NixOS, nix-env -iA nixpkgs.thing works — but it's the wrong way to do it.
Why nix-env is bad:
- It installs packages imperatively, outside your config
- Changes disappear on the next
nixos-rebuild switch - No reproducibility, no version control, no rollback
- Creates a "shadow" package set that conflicts with declared packages
If you need a one-off tool temporarily, use:
nix shell nixpkgs#ffmpeg
This gives you the tool for the current session without polluting your system. For anything you use regularly, add it to your config.
Best Practice #4: Set Up Home Manager Early
Home Manager is a tool that manages user-level configuration — dotfiles, user packages, shell config, Git settings, terminal emulators, and more. It's the companion to NixOS's system-level configuration.
Why set it up early? Because your user config (bash aliases, Git config, Neovim setup, terminal preferences) is just as important as your system config. Home Manager lets you declare all of it in Nix files, version-controlled alongside your system config.
Minimal setup with Flakes (recommended approach):
In your flake.nix:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager, ... }: {
nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./configuration.nix
home-manager.nixosModules.home-manager
{
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
home-manager.users.yourname = import ./home.nix;
}
];
};
};
}
Then create home.nix:
{ pkgs, ... }:
{
home.username = "yourname";
home.homeDirectory = "/home/yourname";
home.stateVersion = "26.05";
programs.git = {
enable = true;
userName = "Your Name";
userEmail = "[email protected]";
};
programs.zsh.enable = true;
home.packages = with pkgs; [
ripgrep
fd
bat
eza
];
}
Now git, zsh, and your CLI tools are declared in your config, reproducible, and version-controllable.
A quick note on home.stateVersion: set it once, to whatever release you're on when you first set up Home Manager, and then leave it alone. It's not a "keep this updated" field — it tells Home Manager which defaults to use for backward compatibility, and bumping it later can change behavior out from under you.
Best Practice #5: Use Flakes From Day One
Flakes are the modern way to manage Nix dependencies. They pin your nixpkgs to a specific commit, ensuring your system is reproducible regardless of what the latest nixos-unstable contains today.
Without Flakes: Your system resolves packages from whatever nix-channel points to today. That changes daily. Reproducibility suffers.
With Flakes: Your system resolves packages from a pinned commit in flake.lock. That never changes unless you explicitly update it. Reproducibility guaranteed.
Enable Flakes in your config:
nix.settings.experimental-features = [ "nix-command" "flakes" ];
Rebuild explicitly once flakes are enabled. The legacy sudo nixos-rebuild switch reads /etc/nixos/configuration.nix through NIX_PATH — but as soon as a flake.nix shows up in /etc/nixos, nixos-rebuild will prefer it automatically. Don't leave that ambiguous. Call it explicitly:
sudo nixos-rebuild switch --flake /etc/nixos#yourhostname
or, from inside the directory:
sudo nixos-rebuild switch --flake .#yourhostname
A word of caution on configuration.nix: moving to Flakes doesn't mean throwing configuration.nix away — it means the flake becomes the single entry point, and configuration.nix becomes one module it imports (as shown in the flake.nix example above). The conflict people run into is duplication, not the file's existence: if configuration.nix already imports hardware-configuration.nix, and you also list hardware-configuration.nix separately in your flake's modules array, NixOS's module system will throw a "defined multiple times" error the moment an option gets declared from both paths. Keep each file imported from exactly one place.
Then finish organizing the rest of your /etc/nixos setup into that flake.nix structure. This is the recommended path forward. Every blog post, every guide, every experienced NixOS user will tell you the same thing: Flakes are the future, and the future is now.
Best Practice #6: Keep Your Config in Version Control
Your /etc/nixos directory is a Git repository waiting to happen. Initialize it:
cd /etc/nixos
sudo git init
sudo git add .
sudo git commit -m "Initial NixOS config"
Now every change you make is tracked. You can see what changed, when, and why. If a nixos-rebuild breaks something, you can diff against the last working commit.
For bonus points, push this repo to GitHub or GitLab. Now your system config survives not just hard drive failures — it survives house fires.
Best Practice #7: Understand Generations
NixOS keeps every system configuration you've ever built as a "generation." When you run nixos-rebuild switch, it creates a new generation without deleting the old one.
List your generations the easy way:
nixos-rebuild list-generations
This gives you a clean, readable table with generation number, NixOS version, kernel, and build date. If you want the older, more verbose form (or need to target a specific profile), you can still fall back to:
sudo nix-env --list-generations --profile /nix/var/nix/profiles/system
Boot into a previous generation:
If your latest rebuild broke something, reboot and select the previous generation from the boot menu (systemd-boot or GRUB). Your system rolls back cleanly. You can also do it without rebooting:
sudo nixos-rebuild switch --rollback
This is your safety net. Use it. Don't fear nixos-rebuild switch — if it breaks, you have generations to fall back on.
Best Practice #8: Set Up Automatic Garbage Collection
Every generation keeps its packages alive in /nix/store, so if you never clean up, your disk usage only grows. This is the flip side of the safety net in Best Practice #7 — you want old generations around long enough to roll back to, but not forever.
Manual cleanup, deleting anything older than 30 days:
sudo nix-collect-garbage --delete-older-than 30d
Automatic cleanup, so you never have to remember to run it, in configuration.nix:
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
This runs weekly and removes generations (and their now-unreachable store paths) older than 30 days. Adjust the window to taste — tighter if you're low on disk, looser if you like a longer rollback history. Just remember: once a generation is gone, you can't roll back to it anymore, so don't set this to something aggressive on a system you're still actively breaking and fixing.
Best Practice #9: Use search.nixos.org
Before you install anything, search for it:
https://search.nixos.org/packages
This tells you:
- The exact package name in nixpkgs
- Whether it has a NixOS option (many packages like Docker, Nginx, PostgreSQL have dedicated NixOS options that integrate better than just installing the package)
- What version is available
Pro tip: If a package has a NixOS option, use the option. For example, don't just add nginx to systemPackages. Use:
services.nginx.enable = true;
services.nginx.virtualHosts."example.com" = { ... };
This configures nginx through NixOS's module system, with proper systemd integration, proper config file management, and proper defaults.
Best Practice #10: One Config File for System, Separate for User
The clean split that works:
/etc/nixos/
├── configuration.nix # System-wide: packages, services, boot, networking
├── hardware-configuration.nix # Auto-generated, don't touch
├── flake.nix # Dependencies and outputs
├── hosts/ # Host-specific overrides
├── modules/ # System-level modules (split by concern)
└── home/ # Home Manager configs per user
├── default.nix # Main home.nix
├── packages.nix # User packages
├── git.nix # Git config
└── shell.nix # Shell config
System things go in modules/. User things go in home/. The boundary is clear: system packages affect all users, user packages affect one person.
Curated Checklist: Best Practices Summary
Bookmark this. Run through it after your first install.
| # | Practice | Why |
|---|---|---|
| 1 | Set password immediately | passwd — don't use initial password |
| 2 | Verify network works | Fix before installing anything |
| 3 | Declare packages in config.nix | Reproducibility, rollback, version control |
| 4 | Don't use nix-env | Imperative installs break reproducibility |
| 5 | Set up Home Manager | User-level config deserves same rigor as system |
| 6 | Use Flakes from day one | Pin dependencies, ensure reproducibility |
| 7 | Rebuild with --flake host#name explicitly |
Avoid ambiguity once a flake.nix exists in /etc/nixos |
| 8 | Import each config file from one place only | Avoid "defined multiple times" errors between configuration.nix and flake.nix |
| 9 | Git-init /etc/nixos | Track every config change |
| 10 | Learn generations | Your safety net for broken rebuilds |
| 11 | Set up automatic garbage collection | nix.gc with --delete-older-than 30d keeps disk usage in check |
| 12 | Use search.nixos.org | Find correct package names and NixOS options |
| 13 | Use NixOS options over packages | Better integration, systemd management |
| 14 | Set proper user groups | wheel, networkmanager, video, audio |
| 15 | Enable flakes in config | nix.settings.experimental-features |
| 16 | Split system and user config | Clean separation of concerns |
| 17 | Only track nixos-unstable if you skip Flakes | Rolling release trades reproducibility for freshness — Flakes gives you both via pinning |
| 18 | Back up your config | Git push to remote, survive hardware failure |
The Bottom Line
NixOS rewards you for thinking declaratively from the start. The best practice is simple: declare everything in your config file, version control it, and use the tools (Flakes, Home Manager) that make it reproducible.
The habits you build in the first hour after install determine whether NixOS feels like magic or like a chore six months from day one. Declare your packages. Set up Home Manager. Use Flakes. Git-commit everything. The system you rebuild from that config will be identical to the one you're running today — and that's the whole point.