tonybtw.com

tonybtw.com

https://git.tonybtw.com/tonybtw.com.git git://git.tonybtw.com/tonybtw.com.git
17,133 bytes raw
1
#+title: How to Install NixOS From Scratch | Flakes + Home Manager Full Guide
2
#+author: Tony, btw
3
#+date: 2025-09-05
4
#+HUGO_TITLE: How to Install NixOS From Scratch | Flakes + Home Manager Full Guide
5
#+HUGO_FRONT_MATTER_FORMAT: yaml
6
#+HUGO_CUSTOM_FRONT_MATTER: :image "/img/nixos-from-scratch.png" :showTableOfContents true
7
#+HUGO_BASE_DIR: ~/tonybtw/www/tonybtw.com
8
#+HUGO_SECTION: tutorial/nixos-from-scratch
9
#+EXPORT_FILE_NAME: index
10
#+OPTIONS: toc:nil broken-links:mark
11
#+HUGO_AUTO_SET_HEADLINE_SECTION: nil
12
#+DESCRIPTION: This is the 4th installment of the NixOS Tutorials. It emphasizes going from Zero to Hero and starting out with flakes and home manager before running nixos-install.
13
14
* Table Of Contents: :toc::noexport:
15
- [[#intro][Intro:]]
16
- [[#install-from-minimal-iso][Install from Minimal ISO]]
17
  - [[#load-up-live-iso][Load up Live ISO]]
18
  - [[#initial-nixos-config][Initial NixOS Config]]
19
  - [[#flakenix][flake.nix]]
20
  - [[#configurationnix][configuration.nix]]
21
  - [[#homenix][home.nix]]
22
  - [[#install][Install:]]
23
- [[#post-install-first-boot][Post Install, First Boot]]
24
  - [[#create-dotfiles-directory][Create Dotfiles Directory]]
25
  - [[#modularize-home-manager-directories-with-symlinks][Modularize home manager directories with symlinks]]
26
  - [[#make-it-extensible-with-a-for-loop][Make it extensible with a for loop]]
27
- [[#turning-neovim-into-its-own-nix-file-the-right-way][Turning Neovim into it's own .nix file (the right way)]]
28
- [[#final-thoughts][Final Thoughts]]
29
30
* Intro:
31
What's up guys, my name is Tony, and today I'm going to give you a quick and painless guide
32
on daily driving NixOS from Scratch, with Flakes and Home manager.
33
34
As I've said before, NixOS really is the end-game of linux in many respects, due to its reproducibility, and it's stability in nature. 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 powerful tools really make NixOS both rock solid like debian, and bleeding edge like Arch.
35
36
#+begin_quote
37
"In many respects, if Arch, and Debian had a son, his name would be NixOS."
38
– Tony
39
#+end_quote
40
41
Let's jump into the tutorial.
42
43
* Install from Minimal ISO
44
https://nixos.org/manual/nixos/stable/index.html#ch-installation
45
46
You can use the graphical installer, but I'm going to use the minimal iso today, because I'm like that.
47
48
** Load up Live ISO
49
50
Let's start by installing NixOS. I've loaded up the minimal ISO, and I have already created a video on installing NixOS like this, so I'll run through tihs part pretty quick. Feel free to follow along the written guide in the link below the subscribe button for this part.
51
52
This set of commands is going to dictate what hardware.nix looks like. If you've installed arch before, a lot of this stuf will look familiar to you. Let's get into it.
53
54
Let's switch into sudo here, and set up our partition scheme.
55
#+begin_src
56
sudo -i
57
lsblk
58
cfdisk /dev/vda
59
60
gpt labels
61
62
1G type: EFI
63
4G type: swap
64
remaining space, type: Linux Filesystem
65
#+end_src
66
67
Alright, let's sanity check that with an lsblk to confirm, and ther we go. We see our 3 partitions we just made. Let's make those file systems.
68
69
And the nixos handbook recommends we use labels for our partitions, so let's go ahead and do so.
70
#+begin_src sh
71
mkfs.ext4 -L nixos /dev/vda3
72
mkswap -L swap /dev/vda2
73
mkfs.fat -F 32 -n boot /dev/vda1
74
#+end_src
75
76
Alright so we have made those file systems, its time to mount them like so:
77
#+begin_src sh
78
mount /dev/vda3 /mnt
79
mount --mkdir /dev/vda1 /mnt/boot
80
swapon /dev/vda2
81
#+end_src
82
83
#+begin_src sh
84
[root@nixos:~]# lsblk
85
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
86
loop0    7:0    0  1.5G  1 loop /nix/.ro-store
87
sr0     11:0    1  1.6G  0 rom  /iso
88
vda    253:0    0   50G  0 disk
89
├─vda1 253:1    0    1G  0 part /mnt/boot
90
├─vda2 253:2    0    4G  0 part [SWAP]
91
└─vda3 253:3    0   45G  0 part /mnt
92
#+end_src
93
94
Alright this looks good, and we're ready to move onto actually generating the config. Please note, I'm using vim here, but if you want to use a different editor, I would just run nixos-install right now, and rebooting, and then creating home.nix and flake.nix. If you're built different like me, just use vim.
95
96
** Initial NixOS Config
97
98
[[https://nixos.org/manual/nixos/stable/index.html#sec-installation-manual][Official NixOS Installation Handbook]]
99
100
Alright, we're ready to generate the config file, so let's do so with the following command:
101
102
This part is going to be a lot of configuration and text editing, so if you want everything I've put into these files, feel free to follow along with my written article:
103
[[https://www.tonybtw.com/tutorial/nixos-from-scratch][NixOS from Scratch]]
104
105
#+begin_src
106
nixos-generate-config --root /mnt
107
cd /mnt/etc/nixos/
108
touch flake.nix home.nix
109
#+end_src
110
111
Let's create flake.nix, and home.nix
112
113
(For those watching the video and are curious about my vimrc, I did import it to the live iso image.)
114
#+begin_src vim
115
filetype plugin indent on
116
set expandtab
117
set shiftwidth=4
118
set softtabstop=4
119
set tabstop=4
120
set number
121
set relativenumber
122
set smartindent
123
set showmatch
124
set backspace=indent,eol,start
125
syntax on
126
#+end_src
127
128
** flake.nix
129
130
Jumping into our flake.nix, this is where we define where our packages come from, so both configuration.nix and home.nix can inherit them through the flake and use them consistently.
131
132
Couple of things worth noting here:
133
134
1. nixpkgs is shorthand for github:NixOS/nixpkgs/nixos-25.05
135
2. inputs.nixpkgs.follows = "nixpkgs": This prevents home-manager from pulling its own version of nixpkgs, keeping everything consistent and avoiding mismatched package sets.
136
3. This modules section tells our flake to build the system using configuration.nix, and to configure Home Manager for the tony user using home.nix, with some options set inline.
137
4. We include home-manager as a NixOS module here because we want Home Manager to be managed by the flake itself — meaning we don’t need to bootstrap it separately, and we don’t need to run home-manager switch. Instead, everything gets applied in one go with nixos-rebuild switch.
138
139
vim flake.nix
140
#+begin_src nix
141
{
142
  description = "NixOS from Scratch";
143
144
  inputs = {
145
    nixpkgs.url = "nixpkgs/nixos-25.05";
146
    home-manager = {
147
      url = "github:nix-community/home-manager/release-25.05";
148
      inputs.nixpkgs.follows = "nixpkgs";
149
    };
150
  };
151
152
  outputs = { self, nixpkgs, home-manager, ... }: {
153
    nixosConfigurations.nixos-btw = nixpkgs.lib.nixosSystem {
154
      system = "x86_64-linux";
155
      modules = [
156
        ./configuration.nix
157
        home-manager.nixosModules.home-manager
158
        {
159
          home-manager = {
160
            useGlobalPkgs = true;
161
            useUserPackages = true;
162
            users.tony = import ./home.nix;
163
            backupFileExtension = "backup";
164
          };
165
        }
166
      ];
167
    };
168
  };
169
}
170
#+end_src
171
172
We're ready to move onto our configuration.nix file.
173
174
** configuration.nix
175
176
List
177
178
We're going to do a lot to this default config file, mainly just deleting comments and whatnot, but heres a list of stuff I'm going to do to it:
179
180
1. Delete comments
181
2. Change hostname
182
3. Remove wpa supplicant (keep if you are on wifi)
183
4. Change timezone
184
5. Delete proxy settings
185
6. Change xserver.enable to its own attribute set
186
   - Enable `ly` display manager: [[https://github.com/fairyglade/ly][ly display manager]]
187
   - Enable qtile
188
   - Modify Key Repeat Settings: `xset r rate 200 35 &`
189
7. Delete rest of comments until User section
190
8. Enable firefox at the system level
191
9. Remove comments on systempackages, add git, and alacritty at the system level
192
10. Delete rest of comments, and then add nerd font package
193
11. add nix-command and flakes =)
194
195
#+begin_src nix
196
{ config, lib, pkgs, ... }:
197
198
{
199
  imports =
200
    [
201
      ./hardware-configuration.nix
202
    ];
203
204
  boot.loader.systemd-boot.enable = true;
205
  boot.loader.efi.canTouchEfiVariables = true;
206
207
  networking.hostName = "nixos";
208
  networking.networkmanager.enable = true;
209
210
  time.timeZone = "America/Los_Angeles";
211
212
  services.displayManager.ly.enable = true;
213
  services.xserver = {
214
    enable = true;
215
    autoRepeatDelay = 200;
216
    autoRepeatInterval = 35;
217
    windowManager.qtile.enable = true;
218
  };
219
220
  users.users.tony = {
221
    isNormalUser = true;
222
    extraGroups = [ "wheel" ];
223
    packages = with pkgs; [
224
      tree
225
    ];
226
  };
227
228
  programs.firefox.enable = true;
229
230
  environment.systemPackages = with pkgs; [
231
    vim
232
    wget
233
    alacritty
234
    git
235
  ];
236
237
  fonts.packages = with pkgs; [
238
    nerd-fonts.jetbrains-mono
239
  ];
240
241
  nix.settings.experimental-features = [ "nix-command" "flakes" ];
242
  system.stateVersion = "25.05";
243
244
}
245
#+end_src
246
247
** home.nix
248
Let's set up our home.nix. we'll heavily modify this after installing nixos and logging in for the first time.
249
250
Just going to specify the home directory, enable git, and for a sanity check, let's setup a bash alias so we can confirm everything worked when we initially log in.
251
252
vim home.nix
253
#+begin_src nix
254
{ config, pkgs, ... }:
255
256
{
257
  home.username = "tony";
258
  home.homeDirectory = "/home/tony";
259
  programs.git.enable = true;
260
  home.stateVersion = "25.05";
261
  programs.bash = {
262
    enable = true;
263
    shellAliases = {
264
      btw = "echo i use nixos, btw";
265
    };
266
  };
267
}
268
#+end_src
269
270
271
** Install:
272
273
Alright we're finally ready to install this. We can do that with this command here, to specify the location of the flake.
274
#+begin_src sh
275
nixos-install --flake /mnt/etc/nixos#nixos-btw
276
277
## type your password
278
nixos-enter --root /mnt -c 'passwd tony'
279
reboot
280
#+end_src
281
Make sure to create this password otherwise you wont be able to log in
282
283
We can safely ignore this warning — it's just complaining that /boot is readable by all users, which is fine for us in this context.
284
285
Let's boot into our system!
286
287
* Post Install, First Boot
288
289
Lets open up a terminal to confirm our home manager worked. Remember we put that bash alias in our home.nix file? Let's run it.
290
Super Enter opens a terminal by default in Qtile.
291
292
#+begin_src sh
293
btw // I use nixos, btw
294
#+end_src
295
296
** Create Dotfiles Directory
297
298
We're going to create our dotfiles directory in our home folder so we can handle all of our nixos files right here in our home folder. This will help for version controlling everything too.
299
300
#+begin_src sh
301
mkdir ~/nixos-dotfiles
302
sudo cp -R /etc/nixos/* ~/nixos-dotfiles/.
303
mkdir ~/nixos-dotfiles/config
304
git clone https://github.com/tonybanters/qtile ~/nixos-dotfiles/config/qtile
305
vim ~/nixos-dotfiles/home.nix
306
#+end_src
307
308
Inside of home.nix, add this line:
309
#+begin_src nix
310
home.file.".config/qtile".source = ./config/qtile;
311
#+end_src
312
313
This will tell home manager to look for this file for our qtile config.
314
315
Lets also grab our neovim config, so we can edit these files a bit easier.
316
317
#+begin_src nix
318
git clone https://github.com/tonybanters/nvim ~/nixos-dotfiles/config/nvim
319
home.file.".config/nvim".source = ./config/nvim;
320
#+end_src
321
322
To get my nvim config to work, we need a few packages while we're in this file.
323
#+begin_src nix
324
home.packages = with pkgs; [
325
  neovim
326
  ripgrep
327
  nil
328
  nixpkgs-fmt
329
  nodejs
330
  gcc
331
];
332
#+end_src
333
334
Alright, we're ready to rebuild our system for the first time. Let's run this command: It tells nixos to use a flake, and points to the flake directory, and specifies the hostname.
335
run:
336
#+begin_src sh
337
sudo nixos-rebuild switch --flake ~/nixos-dotfiles#nixos-btw
338
#+end_src
339
Remember to use your hostname instead of `nixos-btw`
340
341
Alright that looks like it worked, lets confirm it with our refresh qtile keybind. Super Control R
342
Wow, incredible. it worked. That's my qtile config right there, inspired by DT's xmonad and qtile config.
343
Let's check out neovim, see if that worked.
344
345
Beautiful, we see lazy installing our plugins. Awesome.
346
Now our qtile config, and our neovim configs are good to go, but this isn't quite right yet.
347
348
This is suboptimal for 2 main reasons:
349
350
    1. we cant edit the config files because Root owns it
351
    2. we cant get live updates when we DO edit them with sudo because they are not symlinked to our actual directory.
352
353
Solution: *mkOutOfStoreSymlink*
354
355
** Modularize home manager directories with symlinks
356
We can use mkOutOfStoreSymlink to tell nixos to create a symlink of our dotfiles right into ~/.config, instead of in /etc/nixos/store, or whatever.
357
Let's use xdg.configFile instead of home.file, because xdg.configFile is for dotfiles that are stored in ~/.config
358
We only need to use home.file if the file is stored somewhere else.
359
And let's specify recursive = true, so that nixos looks for the entire directory, with all subdirectories when creating the symlink.
360
361
phase 1
362
#+begin_src nix
363
xdg.configFile."qtile" = {
364
    source = config.lib.file.mkOutOfStoreSymlink "/home/tony/nixos-dotfiles/config/qtile";
365
    recursive = true;
366
};
367
xdg.configFile."nvim" = {
368
    source = config.lib.file.mkOutOfStoreSymlink "/home/tony/nixos-dotfiles/config/nvim";
369
    recursive = true;
370
};
371
#+end_src
372
373
Alright, we can run sudo nixos-rebuild switch again, and we should be able to make live updates here.
374
375
#+begin_src sh
376
ln -sf ~/nixos-dotfiles/config/
377
#+end_src
378
379
We see the symlinks are actually pointing to ~/.config this time.
380
381
Let's edit our qtile config again, and a hot reload should work. Awesome.
382
383
We can clean this up by declaring dotfiles, and creating a variable for mkOutOfStoreSymlink (its too verbose this way).
384
#+begin_src nix
385
{ config, ... }
386
387
let
388
    dotfiles = "${config.home.homeDirectory}/nixos-dotfiles/config";
389
    create_symlink = path: config.lib.file.mkOutOfStoreSymlink path;
390
in
391
392
{
393
    xdg.configFile."qtile" = {
394
        source = create_symlink "${dotfiles}/qtile";
395
        recursive = true;
396
    };
397
    xdg.configFile."nvim" = {
398
        source = create_symlink "${dotfiles}/nvim";
399
        recursive = true;
400
    };
401
402
}
403
#+end_src
404
405
** Make it extensible with a for loop
406
407
And one more step would be to just make a for loop to iterate over a list of configs, and that way we can remove code duplication.
408
#+begin_src nix
409
{ config, ... }
410
411
let
412
    dotfiles = "${config.home.homeDirectory}/nixos-dotfiles/config";
413
    create_symlink = path: config.lib.file.mkOutOfStoreSymlink path;
414
    # Standard .config/directory
415
    configs = {
416
        qtile = "qtile";
417
        nvim = "nvim";
418
    };
419
in
420
421
{
422
    # Iterate over xdg configs and map them accordingly
423
    xdg.configFile = builtins.mapAttrs (name: subpath: {
424
        source = create_symlink "${dotfiles}/${subpath}";
425
        recursive = true;
426
    }) configs;
427
428
}
429
#+end_src
430
431
This will make it much easier to add dotfile configs to our system. With this forloop we can actually just clone our entire dotfiles repo and throw it in here.
432
433
* Turning Neovim into it's own .nix file (the right way)
434
If we take Neovim out of this giant home.packages list, we can move it into its own file, and put all of its dependencies in that file as well, to keep things organized. This is beneficial for 2 reasons.
435
436
1. If someone just wants my neovim, they can take neovim.nix and my neovim config, and be good to go.
437
2. If we need to add lsps, or specific packages for just neovim, but we aren't using neovim on a different machine, we dont need to import the entire neovim suite.
438
3. Scales: you can drop in more modules later (emacs.nix, alacritty.nix, etc.) without cluttering home.nix.
439
440
Let's do this by making a folder called `modules` and putting `neovim.nix` in that folder.
441
442
The structure will look like so:
443
444
#+begin_src
445
├── home.nix
446
└── modules
447
    └── neovim.nix
448
#+end_src
449
450
Inside of that neovim.nix file, we can do the following:
451
452
#+begin_src nix
453
{ config, pkgs, lib, ...}
454
455
{
456
  # Install Neovim and dependencies
457
  home.packages = with pkgs; [
458
    # Tools required for Telescope
459
    ripgrep
460
    fd
461
    fzf
462
463
    # Language Servers
464
    lua-language-server
465
    nil # nix language server
466
    nixpkgs-fmt # nix formatter
467
468
    # Needed for lazy.nvim
469
    nodejs
470
  ];
471
472
  programs.neovim = {
473
    enable = true;
474
    viAlias = true;
475
    vimAlias = true;
476
477
    # optional: If you want to manage your plugins with nix, instead of with lazy.nvim,
478
    # you can do it with the plugins key.
479
    # plugins = with pkgs.vimPlugins; [
480
    #     telescope-nvim
481
    #     nvim-treesitter
482
    #     nvim-lspconfig
483
    #     # Any other packages you want pinned at.
484
    # ];
485
  };
486
487
}
488
#+end_src
489
490
Then in our home.nix, we would just import this like so:
491
492
#+begin_src nix
493
{pkgs, config, lib ...}:
494
495
{
496
  imports = [
497
    ./modules/neovim.nix
498
  ];
499
  ...
500
}
501
502
#+end_src
503
504
* Final Thoughts
505
506
In the next installment of "NixOS From Scratch", we'll create 2 package variables, and use unstable to update stuff that we don't care if it breaks our system, and lock the core utils to stable.
507
508
This has been heavily 'programming oriented', but this will leapfrog you forward in your nixos journey. Let me know if you like this sort of content, or what else you would like to see.
509
510
Thanks so much for checking out this tutorial. If you got value from it, and you want to find more tutorials like this, check out
511
my youtube channel here: [[https://youtube.com/@tony-btw][YouTube]], or my website here: [[https://www.tonybtw.com][tony,btw]]
512
513
You can support me here: [[https://ko-fi.com/tonybtw][kofi]]