Intro

NixOS really is the endgame of Linux in many respects, due to its reproducibility and stability. Because of flakes โ€” which allow you to pin your packages to a specific commit โ€” and Home Manager, which enables you to version control and reproduce your user configuration, the combination of these tools makes NixOS both rock solid like Debian and bleeding edge like Arch.

"In many respects, if Arch and Debian had a son, his name would be NixOS."

Let's jump into it.

Install from Minimal ISO

Reference: Official NixOS Installation Handbook

Load up Live ISO

Let's start by partitioning. I'm using the minimal ISO. Switch to root and set up the partition scheme:

shell
sudo -i
lsblk
cfdisk /dev/vda

# GPT labels:
# 1G  โ€” type: EFI
# 4G  โ€” type: swap
# rest โ€” type: Linux Filesystem

Now format and label those partitions (the NixOS handbook recommends labels):

shell
mkfs.ext4 -L nixos /dev/vda3
mkswap -L swap /dev/vda2
mkfs.fat -F 32 -n boot /dev/vda1

Mount them:

shell
mount /dev/vda3 /mnt
mount --mkdir /dev/vda1 /mnt/boot
swapon /dev/vda2
shell
[root@nixos:~]# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
vda    253:0    0   50G  0 disk
โ”œโ”€vda1 253:1    0    1G  0 part /mnt/boot
โ”œโ”€vda2 253:2    0    4G  0 part [SWAP]
โ””โ”€vda3 253:3    0   45G  0 part /mnt

Initial NixOS Config

Generate the config and create the extra files:

shell
nixos-generate-config --root /mnt
cd /mnt/etc/nixos/
touch flake.nix home.nix

flake.nix

The flake defines where packages come from so both configuration.nix and home.nix can inherit them consistently. A few things worth noting:

  • nixpkgs is shorthand for github:NixOS/nixpkgs/nixos-25.05
  • inputs.nixpkgs.follows = "nixpkgs" prevents Home Manager from pulling its own version of nixpkgs
  • The modules section tells the flake to build the system using configuration.nix and configure Home Manager for our user via home.nix
  • Including Home Manager as a NixOS module means everything applies in one shot with nixos-rebuild switch โ€” no separate bootstrapping
nix
{
  description = "NixOS from Scratch";

  inputs = {
    nixpkgs.url = "nixpkgs/nixos-25.05";
    home-manager = {
      url = "github:nix-community/home-manager/release-25.05";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, home-manager, ... }: {
    nixosConfigurations.nixos-btw = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ./configuration.nix
        home-manager.nixosModules.home-manager
        {
          home-manager = {
            useGlobalPkgs = true;
            useUserPackages = true;
            users.tony = import ./home.nix;
            backupFileExtension = "backup";
          };
        }
      ];
    };
  };
}

configuration.nix

Here's a clean configuration with the ly display manager, qtile window manager, and the essentials:

nix
{ config, lib, pkgs, ... }:

{
  imports = [ ./hardware-configuration.nix ];

  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  networking.hostName = "nixos";
  networking.networkmanager.enable = true;

  time.timeZone = "America/Los_Angeles";

  services.displayManager.ly.enable = true;
  services.xserver = {
    enable = true;
    autoRepeatDelay = 200;
    autoRepeatInterval = 35;
    windowManager.qtile.enable = true;
  };

  users.users.tony = {
    isNormalUser = true;
    extraGroups = [ "wheel" ];
    packages = with pkgs; [ tree ];
  };

  programs.firefox.enable = true;

  environment.systemPackages = with pkgs; [
    vim wget alacritty git
  ];

  fonts.packages = with pkgs; [
    nerd-fonts.jetbrains-mono
  ];

  nix.settings.experimental-features = [ "nix-command" "flakes" ];
  system.stateVersion = "25.05";
}

home.nix

A minimal home.nix to start. We'll heavily modify this after the first boot:

nix
{ config, pkgs, ... }:

{
  home.username = "tony";
  home.homeDirectory = "/home/tony";
  programs.git.enable = true;
  home.stateVersion = "25.05";
  programs.bash = {
    enable = true;
    shellAliases = {
      btw = "echo i use nixos, btw";
    };
  };
}

Install

shell
nixos-install --flake /mnt/etc/nixos#nixos-btw

# Set your password before rebooting
nixos-enter --root /mnt -c 'passwd tony'
reboot

Post Install, First Boot

Open a terminal (Super+Enter in Qtile) and confirm Home Manager worked by running the alias we set:

shell
btw
# โ†’ i use nixos, btw

Create Dotfiles Directory

shell
mkdir ~/nixos-dotfiles
sudo cp -R /etc/nixos/* ~/nixos-dotfiles/.
mkdir ~/nixos-dotfiles/config
git clone https://github.com/tonybanters/qtile ~/nixos-dotfiles/config/qtile

Using mkOutOfStoreSymlink tells NixOS to create a live symlink from our dotfiles into ~/.config, so we can make live edits without running nixos-rebuild each time.

nix
{ config, ... }

let
  dotfiles = "${config.home.homeDirectory}/nixos-dotfiles/config";
  create_symlink = path: config.lib.file.mkOutOfStoreSymlink path;
  configs = {
    qtile = "qtile";
    nvim = "nvim";
  };
in

{
  xdg.configFile = builtins.mapAttrs (name: subpath: {
    source = create_symlink "${dotfiles}/${subpath}";
    recursive = true;
  }) configs;
}

Turning Neovim Into Its Own .nix File

Move Neovim out of the main packages list into modules/neovim.nix. This keeps things modular and portable:

nix
{ config, pkgs, lib, ...}

{
  home.packages = with pkgs; [
    ripgrep fd fzf
    lua-language-server
    nil nixpkgs-fmt
    nodejs
  ];

  programs.neovim = {
    enable = true;
    viAlias = true;
    vimAlias = true;
  };
}

Then import it in home.nix:

nix
{pkgs, config, lib ...}:

{
  imports = [ ./modules/neovim.nix ];
  # ...
}

Final Thoughts

That's a complete NixOS setup from bare metal. You've got flakes pinning your packages, Home Manager handling your user config, live symlinks for your dotfiles, and Neovim in its own module. In the next installment we'll layer in unstable packages for tools where you want the bleeding edge.

If this was useful, check out the other tutorials on this site, or subscribe to the newsletter for new posts.