I recently bought a new MacBook. My old MacBook had become bloated with random CLI tools, version managers and Homebrew packages from all over the internet. I always seemed to have install issues with Node and npm
(I'd tried both nvm
and n
at various points) and had no way of knowing what was installed, where it came from or how to remove it.
So while marvelling at the smooth aluminium exterior of my new laptop, I had a strong desire to keep the interior system as clean and tidy as possible too. And it just so happens I'd been hearing about something exciting called Nix at the time.
Nix is an open source tool which describes itself as a "package manager". You can use Nix to manage the packages on your system such as git and Node, like you would with Homebrew. But the "package manager" part is just the tip of the iceberg: in this post I'll explain why Nix's ability to setup dev environments can give you frontend developer superpowers.
Install Nix
Feel free to read about what Nix can do below before installing. But if you want to follow along, here's what you need to do.
If you're on a Mac like me, you'll need to setup a new /nix
volume to install Nix. You can follow Solution 1 of this GitHub comment - it's a pain, but it works, and once you've done it once you don't need to worry about it again. Since us Mac users are likely on zsh, I also had to add this for initial setup (it's normally added to a bash profile):
❯ cat ~/.zprofile
. ~/.nix-profile/etc/profile.d/nix.sh
If you're on Linux, just head to the Nix download page and install.
If you're on Windows, then our path ends here my friend. Maybe try Linux on Windows?
Nix the dabbler
Sometimes you just want to try hacking on something for a short time - but to do that you need to install a lot of software. For example in 2017 I'd been playing around with Machine Learning Python frameworks, and had a ton of (outdated) tools lying around my system, clogging up my CLI path and taking up space. Not ideal.
Let's imagine we wanted to play with PureScript. If you run the command:
nix-shell -p purescript
We're now in a shell with access to the PureScript CLI:
[nix-shell:~]$ purs --version
0.13.6
If you type exit
or close that window, then boom PureScript is gone without a trace! (Well, it's leftover in a cache in case you decide to use it again later on, but don't concern yourself with that).
Just like the npx
command, we can even run CLIs for a one-time-only use:
nix-shell -p nodejs --run 'node -v'
Nix the environment
Let's take this a step further. I recently needed to make a small change to a Haskell program I'd made as a personal project. Now I don't want to manually type all the nix-shell -p ...
packages every time I need to hack on my Haskell project, but I also don't want lots of Haskell compilers and package managers clogging up my precious system.
Enter the shell.nix
file. Here's my very simple Haskell setup:
with (import <nixpkgs> {});
let ghc = haskell.compiler.ghc865;
in haskell.lib.buildStackProject {
inherit ghc;
name = "myEnv";
buildInputs = [ pcre ];
}
This is the Nix language - think of shell.nix
as a powerful Webpack config file. I won't go into details here on how this works (for that see the resources at the end), but we're essentially describing the environment we need Nix to setup for the project. We can even specify which version of programs we need (for example, Node 12 instead of Node 10).
To get into the correct dev environment to build and hack on the project, you simply run nix-shell
in the directory with the shell.nix
file.
I can commit shell.nix
, share this repo on GitHub - and any other Nix user (or myself in the future) who clones this repo is guaranteed to have the same dev environment.
Do you feel the power at your fingertips yet? No more "works on my machine" problems. No need to manually switch Node versions between repos (goodbye nvm
!). No more over-the-top Docker environments necessary either (Docker, we've had a great run, but maybe it's for the best?).
Nix the package manager
nix-shell
is great, but there are some tools we do want installed at all times, like git and Node.
We can view what Nix has globally installed for us (including itself):
❯ nix-env --query --installed
home-manager-path
nix-2.3.4
We can see what packages are available to install, for example see versions of Node:
❯ nix-env -qa nodejs
nixpkgs.nodejs nodejs-10.20.1
nixpkgs.nodejs-10_x nodejs-10.20.1
nixpkgs.nodejs-12_x nodejs-12.16.3
nixpkgs.nodejs-13_x nodejs-13.14.0
nixpkgs.nodejs-14_x nodejs-14.3.0
Where are the packages coming from? It turns out there's a gigantic 240k+ commit GitHub repo where we're all encouraged to write Nix files describing dependencies. For example, here's Node's v12.nix. It's this herculean crowd-sourced effort that enables you to smoothly add and remove packages as you please.
We can then add new programs into our global environment with nix-env -i ...
:
❯ nix-env -i nodejs
installing 'nodejs-10.20.1'
building '/nix/store/vvanv82vvw2lyc79bs3plnygphnpvvva-user-environment.drv'...
created 5005 symlinks in user environment
Nix will even list your entire install history, just like a git history:
❯ nix-env --list-generations
1 2020-05-23 10:53:43
2 2020-09-20 19:07:11 (current)
Perhaps our last install was a huge mistake (it installed Java or something) and we need an undo button. Fortunately, just like git histories, your install history (known as "generations") is immutable and can be easily undone:
❯ nix-env --rollback
switching from generation 2 to 1
Phew! Now you can play around with installing tools, without worrying about permanently messing up your dev environment.
You can use Nix in this way as a simple package manager, but actually I never use nix-env
. There's an alternative, even better way.
Nix the operating system
There's a version of Linux available called NixOS. It takes the ideas of Nix - describing your environment in a .nix
file - to the entire operating system. The entire OS is configured in /etc/nixos/configuration.nix
, which makes it perfect for spinning up identical machines.
That's great for the DevOps crowd, but not so useful for us Frontend Developers who are are enjoying our Mac experience.
Instead, I've been using a community tool called Home Manager to achieve a similar experience. It's more focussed on your user setup instead of the entire system (like NixOS is), but that suits us. Instead of manually installing our tools like Node through nix-env
, we can simply list in a .nix
file all the tools we want and all the configs and dotfiles we want.
If we think of typical setups (like Homebrew and git config
commands) as jQuery, where we're always mutating the state of things, then think of Home Manager as a pure React component: a declarative function that we can run again and again without fear.
Here's a shortened example of my ~/.config/nixpkgs/home.nix
file:
{ config, pkgs, ... }:
{
programs.home-manager.enable = true;
home.stateVersion = "20.03";
home.packages = [
pkgs.iterm2
pkgs.nodejs-12_x
pkgs.cheat
];
home.sessionVariables = {
EDITOR = "vim";
};
home.file = {
".zshrc".text = ''
# Aliases
alias ls="ls -a"
alias ll="ls -lhA"
'';
".macos".text = ''
# Disable the “Are you sure you want to open this application?” dialog
defaults write com.apple.LaunchServices LSQuarantine -bool false
'';
};
programs.vim = {
enable = true;
settings = {
number = true;
};
};
# https://github.com/rycee/home-manager/blob/master/modules/programs/vscode.nix
programs.vscode = {
enable = true;
userSettings = {
"editor.tabSize" = 2;
};
extensions = [
# some can be found here: https://github.com/NixOS/nixpkgs/blob/master/pkgs/misc/vscode-extensions/default.nix
pkgs.vscode-extensions.vscodevim.vim
pkgs.vscode-extensions.bbenoist.Nix
];
};
}
You can see I've listed some packages I want like Node and cheat. I've written to some dotfiles manually (like .macos
) but also used some of Home Manager's settings to configure some like vim. No need to share dotfiles anymore, we can just share home.nix
files!
The difference between home.packages
and the rest of the file can be a little confusing. home.packages
lists the packages you can install from that big GitHub repo called nixpkgs we used with nix-env
earlier. But those are just packages and don't say anything about how to configure them, e.g. through a dotfile.
For configuration, we can make use of Home Manager's modules, which are also stored in a big directory on the GitHub repo (though nowhere near as big as nixpkgs). It works in a similar way to NixOS but has some differences, which is why it has its own GitHub repo.
To edit home.nix
I just run:
home-manager edit
And when I'm ready to try the changes out on my system, I run:
home-manager switch
That's it! You can feel safe and secure knowing that you can revert to a previous version of your home.nix
file if something goes wrong.
And since my home.nix
file is backed up, if anything happened to my MacBook, getting back to my desired environment would take no effort at all (except for installing Xcode of course, that takes forever). What's more, if you have a personal and work laptop, it's easy to keep your dev environment synced between them.
Nix the time sink
So far I've given Nix a glowing review, so there must be a catch, right? Well, it's taken me a long time to get my Nix environment to the state I want it to be, and that's the catch. Nix really takes up a lot of time to learn and get right.
Firstly there's a whole new language to learn to know how to read and write .nix
files.
Then what happens if a Nix package you want to install hasn't been added yet? Well, you need to spend time contributing it.
True story: I needed to use the audiowaveform CLI tool briefly, but it's not on Nix. It is on Homebrew though, and I was being lazy, so I booted up my old MacBook and used it there. 🙈
And then you find out there's no Home Manager module for what you need - you need to submit a PR for that too.
GUI applications (e.g. Chrome) are difficult / impossible to manage through Home Manager too, and I wouldn't recommend doing so. For example I can't change my VS Code settings within the app, since the settings.json
file is "Read Only". It's great that Nix is immutable like that, but it takes away the convenience in some cases.
I hope this post has helped explain some of the core concepts around Nix, but going through all of the details can be tedious to debug and get right. It's another layer of setup you need to figure out, which is tough if no one else on your team is doing it (until you inevitably convince them, of course).
The community is not enormous and the existing documentation leaves a lot to be desired. It's certainly not a smooth developer experience yet.
But don't let any of that deter you! 😁
Nix for Frontend Developers
I've been using Nix for my frontend work for the past few months and it's been working a treat. It certainly was a lot to learn upfront, but the magic of setting up my tools in an organised manner (and not breaking them again) has been 100% worth it.
I've got my regular tools like Node listed in a Home Manager home.nix
file. My dotfiles and program configs are there too.
I've got projects in other languages (like the Haskell one) using a shell.nix
file to easily get my environment setup. Web projects often rely on just Node and npm
, but you can use a shell.nix
file to specify the minimum Node version supported too.
In theory Nix could completely replace npm
, but that goes so against the grain I wouldn't recommend even trying. A local node_modules
and npm install
still works fine. Global npm modules must still be handled through Nix packages though.
VS Code is the text editor of choice for many frontend developers. As mentioned earlier, you can use Home Manager to install it, write its settings and install extensions through the home.nix
file. But every extension needs to be added as a module (and most are not there), so it's simply easier at the moment to download from the website. And with Settings Sync, you're already getting most of the benefits Nix would provide anyway.
That's it from me! If Nix seems to your liking and you're willing to get your hands dirty for some developer superpowers, here's a bunch of resources to take you further:
- The official docs of course
- Burke Libbey's YouTube videos on Nix (highly recommend!)
- A nice summary of Nix commands through use cases
- A tour of Nix to learn the language
- The official Home Manager manual
- Some tips on Node with Nix
- The friendly Discord channel
Happy Nix hacking!
Top comments (1)
Great introduction and overview. Thanks for posting this.