In my last post, I finally got NixOS dualbooting successfully on my Macbook Pro alongside macOS. In this post, I'll set up my desktop to dualboot NixOS and Windows 10. I'll also configure a github repository to track and sync my NixOS configuration for both of my devices.
My desktop PC is an aging mini-itx gaming machine with parts matching this pcpartpicker list. Needless to say, the config will be slightly different for this pc than my macbook pro. Let's see how the magic of Nix makes it easy to manage both with the same code.
I imagine the organization of my nix config will continue to evolve over time, but since it's all code and I'll be using version control, I feel pretty happy to experiment. The structure I'll be implementing today is pictured below and the actual configuration can be seen in the v0.2.0 tag on my github repo.
/home/ray/nix-config/
├── README.md
├── common-configuration.nix
└── hosts
├── nixos-mbp
│ ├── configuration.nix
│ ├── hardware-configuration.nix
│ └── firmware
└── nixos-desktop
├── configuration.nix
└── hardware-configuration.nix
Basic tools first
I use the Github desktop client. I admit it. I like it. I love it. I hate command line git. Sourcetree confuses me. I will grow one day. But today, I'm getting stuff done, not fighting for street cred.
I boot into the my Plasma NixOS environment that I set up for my Macbook Pro (as detailed in the previous post) and log in with my user account. I open Konsole
from the start-like menu and run
sudo nano ../../etc/nixos/configuration.nix
I scroll toward the bottom and add two system packages, so that my systemPackages config looks like this:
environment.systemPackages = with pkgs; [
git
(vscode-with-extensions.override {
vscodeExtensions = with vscode-extensions; [
bbenoist.nix
];
})
github-desktop
];
There's some explanation of how this works in my Day 2 Post and it's kind of easy to understand, so I won't go into details in this post. With that, I do a quick sudo nixos-rebuild switch
and boom I've got VSCode with Nix syntax highlighting as well as Github desktop installed. I'm ready to do this.
Version control for Nix Config
One does not simply initialize a git repo in /etc/nixos
. This is a protected area, where changes require root access. From what I can see there are a couple popular options for handling this:
- Use sudo commands & git cli to manage repo in
/etc/nixos
- Grant your user ownership of
/etc/nixos
- Configure nix to load config from another location
- Putting symlink(s) in
/etc/nixos
that point to another location
These are probably the best ways to do it right now. I also explored using multiple branches and worktrees but that got cumbersome fast. Ultimately, I just want something easy. To do that, I'll use one symlink and a repo in my home directory.
First, what is a symlink? A symlink is a symbolic link and differs from a hard link. Simply stated, a hard link is a reference to data and a symbolic link is a reference to a reference. It can get pretty technical if you look much deeper than that. But essentially, if I replace the file /etc/nixos/configuration.nix
with a symlink of the same name which targets the config file in my home directory repo, Nix will follow that path and evaluate the config files in my repo instead of /etc/nixos
.
Let's do it.
First, I cloned my existing repository to /home/ray/nix-config/
using Github desktop. Then I moved my current working files into that repo.
sudo cp -r /etc/nixos /home/ray/nix-config/
Next, I deleted everything in /etc/nixos
sudo rm -rf /etc/nixos/*
I created the symlink
sudo ln -s /home/ray/nix-config/configuration.nix /etc/nixos/configuration.nix
And finally, tested it out
sudo nixos-rebuild dry-activate
That created a new result file (actually another symlink!) which I added to my gitignore.
Since I'm not collaborating with anyone and I hope to be careful about my changes, I'll be working directly out of the main branch w/o pull requests. This is a little iffy because it means it's very easy to make quick changes to my device's active config. Ideally I could have the ease of Github desktop's git action UX and diff highlighting as well as some kind of confirmation for when I update the config that's actually in use by the computer. Perhaps someone can comment with advice! In any case, it's time to move on.
Refactoring config layout
My current config is essentially the NixOS default. The contents for my laptop are pinned in a release here.:
/etc/nixos/
├── configuration.nix
├── hardware-configuration.nix
To adapt to the new structure, I'll create a common-configuration.nix
file which is accessed by each device as well as a dedicated directory for configuration.nix
and hardware-configuration.nix
for each device. For each device, 1 symlink will be created pointing /etc/nixos/configuration.nix
to /home/ray/nix-config/HOSTNAME/configuration.nix
. I'll try to keep as much config in the common file as possible.
First, I'm creating the new file structure and leaving the common file empty. Nothing changing besides locations. That means removing the symlink I just built and creating a new one with
sudo rm /etc/nixos/configuration.nix
and
sudo ln -s /home/ray/nix-config/hosts/nixos-mbp/configuration.nix /etc/nixos/configuration.nix
Once I get that working, I create the common file as an "empty" config:
# NixOS Configuration common to both of my machines
{ config, pkgs, ... }:
{}
Next I update my imports in nixos-mbp/configuration.nix
imports =
[ # Common configuration
../../common-configuration.nix
# Include the results of the hardware scan.
./hardware-configuration.nix
# Firmware for keyboard, trackpad, etc
"${builtins.fetchGit { url = "https://github.com/kekrby/nixos-hardware.git"; }}/apple/t2"
];
The reference to the common file goes up to the root of my git repo and the reference to the hardware config looks in the same host directory. Lastly, the third part pulls in some firmware stuff we need for the macbook.
Now I'm going through the rest of the config file and moving anything that I think should be in the common config file. I end up with just 4 "uncommon" things:
- Hostname
- Imports
- Broadcom firmware
- Touchpad support
Next, I'm going to duplicate this file and add it to a new directory for my desktop computer. Normally I'd do this with the GUI, but here's the CLI command:
cp -r hosts/nixos-mbp/configuration.nix hosts/nixos-desktop/
Maybe I'm learning linux after all. Anyway, I open that new file in VSCode and remove the firmware import, the wifi firmware, and the touchpad entry. I change the hostname and my file looks like this:
# Config for nixos-mbp device
{ config, pkgs, ... }:
{
networking.hostName = "nixos-desktop"; # Define your hostname.
imports =
[ # Common configuration
../../common-configuration.nix
# Include the results of the hardware scan.
./hardware-configuration.nix
];
}
Now I think I'm ready to install on the PC!
Installing NixOS on PC
Now there's no reason to make this difficult. I downloaded the GNOME 64-bit graphical installer from the Nix site, flashed an 8GB drive with Balena Etcher I downloaded onto my Windows 10 system and spammed F12 when booting back up.
I clicked Next
3 times, entered a name and password, clicked next 2 more times, checked Allow unfree software
, clicked next, chose my 12 year old SSD, selected Erase Disk
with Swap (with Hibernate), clicked next, clicked install, waited a bit, checked a box to reboot, and hit done.
Booting up, I smashed F12 again and chose "Linux Boot Manager (P2: OCZ Agility3) and there I was, staring down the login screen with my name on it.
Configuring desktop with the repo config
Now I need to do a few things to get in sync with my laptop environment.
- Clone my nix-config repo
- Move my hardware-configuration.nix into it
- Replace my configuration.nix with a symlink to the repo file
To do that, I'll temporarily install git with
nix-env -i git
and I'll clone the repo into place:
git clone https://github.com/raymondgh/nix-config /home/ray/nix-config
Moving the hardware config into the repo:
mv /etc/nixos/hardware-configuration.nix /home/ray/nix-config/hosts/nixos-desktop
Removing the current config.nix file from etc/nixos
rm mnt/etc/nixos/configuration.nix
And then replacing it with the symlink:
ln -s /home/ray/nix-config/hosts/nixos-desktop/configuration.nix /etc/nixos/configuration.nix
One last thing to do that I learned the hard way -- grant my user ownership of the directory I cloned with root
sudo chown -R ray /home/ray/nix-config
And we should be good to rebuild!
sudo nixos-rebuild switch
and with that done, I ran:
reboot
Pushing back to Github
Now that our desktop has everything in place, it's time to give back. Opening Github desktop, I choose "add local repository" and select my /ray/nix-config
directory. It's instantly set up and all I need to do is commit and push to origin.
And with that, I've got two machines running nearly identical operating systems with a single codebase under version control managing their features.
Whenever I install something, I'll ask myself "Is this for both of my machines, or just one?" and put that new or changed nix expression in either the common-configuration.nix file or the HOSTNAME/configuration.nix file accordingly. Then when I log on to the other machine, I'll pull the latest from the repo, do a nixos-rebuild switch
and enjoy being totally in sync. Pretty snazzy!
What's next
Now I've really got my foundation landed. I should try to get something productive done. My next updates might be more quality of life (like getting sound to work on the laptop or making my desktop nicer) or functional (getting a software dev environment set up). At least from what I've read, once I get around to nix flakes or home manager, I'll have more challenges to figure out. As always, thanks for reading and let me know what you think!
Top comments (1)
Have your thoughts evolved on Config management?