I finally had some time to play around with Nix - the immutable package manager and build system. This was on my agenda since a long time, but I finally took the plunge on my M1 OSX system. I by no means understand Nix fully yet, but making progress and it is usable to me already.
Getting Started
I started by reading all of zero-to-nix.com - a great website which explains the first baby steps. For installing, I used the unofficial installer DeterminateSystems/nix-installer for reasons explained on the website, which ran through smoothly.
After doing the tutorials at zero-to-nix.com, I wondered how to continue. After all, I had two main goals:
- have a reproducible global dev tool setup (basically a replacement for Homebrew)
- have reproducible dev environments (basically replacing docker)
For the second use case, reproducible dev environments, I quickly settled on cachix/devenv. I also checked out jetpack-io/devbox which does fundamentally the same thing; but which hides the Nix files completely underneath - so that was too restrictive for my tastes.
When reading about the ecosystem, I saw there are lots of patterns how one could use Nix; and this is in flux right now, as the community irons out flakes (which many people seem to be using already though). So I for now decided on the following:
- Embrace Nix Flakes: I'll only use nix flakes, especially after reading this blog post.
- Only use the nix tool, instead of the older nix-env etc tools. It seems the new world and the old world sometimes are incompatible, and you need to decide which way to go consistently.
- Only go declarative: I will never install a package by typing a CLI command, but always editing some configfile and running a "converge" script.
I have read that flakes and nix are still considered alpha; but from my perspective right now it feels 95% done - and maybe other projects would have called this a finished product by now. So I feel good about using this.
Global Dev Setup
For installing tools globally, I've read about nix profiles, but I was unsure about how to use them with Flakes. I then stumbled over this forum post, which is the way I am doing it now:
- I created a folder in ~/nix-sebastian (does not matter) containing my global Nix configuration.
- In this folder, I placed a flake.nix (content see below), effectively making this folder a flake.
- Then, for initial installation, I ran nix profile install . (inside the folder). This registered the flake.nix as part of the global nix profile. You can see that nix profile list shows the profile.
- To update, you need to run nix profile upgrade 0 (assuming you only have this single profile installed) - if you have multiple profiles installed, you need to use the number displayed at the front of the listing.
Pitfall: you are not allowed to re-run nix profile install ., as this will install the profile a second time - and this will lead to conflicts. This is what I stumbled over at first :)
flake.nix skeleton
The following is the skeleton file I am using for global installation. To understand the syntax, this post about language basics and this post about functions have been helpful for me.
# flake.nix skeleton:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
};
outputs = { self, nixpkgs }: {
packages."aarch64-darwin".default = let
pkgs = nixpkgs.legacyPackages."aarch64-darwin";
in pkgs.buildEnv {
name = "home-packages";
paths = with pkgs; [
# general tools
git
# ... add your tools here
];
};
};
}
Development Environments with devenv
I cannot say much about devenv.sh, except that it works as intended :) The docs are a bit sparse, but I found the devenv.nix file reference to be extremely helpful. It shows what you can all install and how you can customize the environment - I especially like the customizability of languages, and that the system supports services and scripts. As an example, for a Neos/Flow PHP application, I am using the following devenv.nix. It does not yet install all optimal PHP extensions, but it already sets up a database with permissions :)
In case you wonder, I used the "Advanced (declaratively with flakes)" installation method of devenv.sh described here.
# devenv.nix
{ pkgs, ... }:
{
languages.php.enable = true;
languages.php.package = pkgs.php82;
services.mysql = {
enable = true;
initialDatabases = [
{ name = "neos"; }
];
ensureUsers = [
{
name = "neos";
ensurePermissions = {
"neos.*" = "ALL PRIVILEGES";
};
}
];
};
starship.enable = true;
}
Bonus and Closing Thoughts
So far I like what I saw :) I only played around with this for a few hours, but I was already able to remove most of my homebrew packages and convert them to Nix. I am curious where I will end up :)
In case you are interested, below is my current global flake.nix file, with the following specials:
- install devenv
- install diverse language toolchains (Rust, php, deno, node, JDK, go)
- install PHP with extensions
- custom-compile a PHP extension
my current global flake.nix file
# global flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
unstable.url = "nixpkgs/nixos-unstable";
devenv.url = "github:cachix/devenv/latest";
# rust, see https://github.com/nix-community/fenix#usage
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "unstable";
};
};
outputs = { self, nixpkgs, unstable, devenv, fenix }: {
packages."aarch64-darwin".default = let
pkgs = nixpkgs.legacyPackages."aarch64-darwin";
unstablePkgs = unstable.legacyPackages."aarch64-darwin";
# see https://github.com/Mic92/nixos-wiki-test/blob/196aa6c3463bf52128d3ffb07218d0999b2ca617/PHP.md?plain=1#L38
php-vips = pkgs.php81.buildPecl {
pname = "vips";
version = "1.0.13";
sha256 = "TmVYQ+XugVDJJ8EIU9+g0qO5JLwkU+2PteWiqQ5ob48=";
buildInputs = [ pkgs.vips pkgs.pkg-config ];
};
php81 = pkgs.php81.buildEnv {
extensions = ({ enabled, all }: enabled ++ (with all; [
xdebug
opcache
redis
php-vips
]));
extraConfig = "memory_limit = 2G";
};
in pkgs.buildEnv {
name = "home-packages";
paths = with pkgs; [
# general tools
git
bitwarden-cli
ffmpeg
gnupg
curl
wget
jq
gnused
ripgrep
tmux
pandoc
# dev tools
devenv.packages.aarch64-darwin.devenv
mitmproxy
cfssl
dive
graphviz
lnav # log file nav
watchman
vector
kubectl
httpie
certstrap
lima
caddy
mutagen
mutagen-compose
upx
age
dbmate
gomplate
jwt-cli
ipcalc
ncdu
inetutils # telnet
# infrastructure
ansible
k3d
k9s
natscli
nats-server
redis
trivy
# programming environments
deno
nodejs
yarn
php81
symfony-cli
fenix.packages."aarch64-darwin".minimal.toolchain # rust
jdk17
# go
go
unstablePkgs.goreleaser
protobuf # TODO remove
protoc-gen-go # TODO remove
protoc-gen-go-grpc # TODO remove
golangci-lint # TODO remove
buf # TODO remove
gum # TODO REMOVE
#mariadb
#postgresql
# to remove
awscli
google-cloud-sdk
platformio
arduino-cli
openfortivpn
hcloud
pgcli
# mobile dev
# TODO Flutter does not work
cocoapods
fastlane
];
};
};
}