Since I started using Darwin based machines in 2003 I've used Fink, MacPorts, Stow and Homebrew.
For the last few years my go-to developer tools have been Homebrew, ASDF and Docker.
I wanted to something different to manage my development environment but with a couple of constraints:
- No need to compile packages
- Fast and reproducible
Nix had been under my radar for some time but as it had, and still has, an "incredibly steep learning curve" I was postponing the adoption.
Here are my notes about how I use it.
Install Nix using Determinate Nix Installer which has some good defaults
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm
Open a new terminal and run the following command to see things are working as expected
nix-shell -p hello --run "hello"
I use home-manager to install packages that I need on my day to day work which are not project specific. I recommend installing it with Nix Flakes.
I don't use any advanced features like dotfiles management. I just use it to define and install packages.
I've been migrating my shell.nix
to flake.nix
because it simplifies a lot version pinning. You can read more about Flakes here.
As an example this is the flake.nix
I use on my Rails projects:
{
description = "ruby 3.3 environment";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
inherit (nixpkgs.lib) genAttrs;
supportedSystems = [ "aarch64-linux" "aarch64-darwin" ];
forAllSystems = f: genAttrs supportedSystems (system: f system);
in
{
devShells = forAllSystems (system:
let pkgs = import nixpkgs { inherit system; };
in {
default = pkgs.mkShell {
packages = with pkgs; [
cmake
gh
git
git-crypt
gitleaks
gnupg
lefthook
libyaml
nodejs_20
openssl
overmind
pkg-config
postgresql_15
ruby_3_3
yarn
];
shellHook = ''
export PATH=./bin:$PATH
'';
};
});
};
}
To enable the flake you can run
nix develop
Or you can use nix-direnv to enable the console when you enter the folder.
I use a combination of nix-shell and direnv to install packages required on a particular project.
First of all install direnv
nix-channel --add https://nixos.org/channels/nixpkgs-unstable
nix-channel --update
nix-env --install --attr nixpkgs.direnv
Enable direnv
on your ~/.zshrc
configuration
if type direnv &>/dev/null; then
export DIRENV_LOG_FORMAT=""
eval "$(direnv hook zsh)"
fi
On your project add a shell.nix
with your package list
with (import <nixpkgs> {});
mkShell {
buildInputs = [
hello
];
}
Create an .envrc
file and allow its execution
echo "use nix" > .envrc
direnv allow
Now you can run an smoke test to execute the installed command
hello
That's it, you have hello
command available on that project. Now you can install terraform
, ruby
... or whatever tool you need which is specific to that project.
To run services I'm using a combination of
shell.nix
to define the packages and pin versions- direnv to start a
nix-shell
- hivemind to start the processes
As an example services/redis.nix
installs Hivemind and Redis and creates a Procfile
which is used by Hivemind to start the processes.
nix-shell services/redis.nix
Once nix-shell is enabled, run hivemind
to start the services.
Let's say you have a Rails application with a shell.nix
file which defines some of its dependencies. You can run nix-shell
to start a new shell with all the dependencies installed or you can run a nix-shell
command to execute a command inside the new shell.
nix-shell --run "bin/rails server"
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p restic
if [ ! -n "$RESTIC_REPOSITORY" ]; then
echo "error: \$RESTIC_REPOSITORY not set."
exit 1
fi
WORK_DIR=$(mktemp -d -t restic-restore-XXXXXX)
restic restore latest \
--target $WORK_DIR \
--include $HOME/Documents
nix develop --command bash -c "bin/rails server"
# Create a new profile
nix-env --switch-profile /nix/var/nix/profiles/per-user/fesplugas/foo
# List installed packages
nix-env --query
# Install a new package
nix-env -iA nixpkgs.hello
# Go back to your default profile
nix-env --switch-profile $HOME/.local/state/nix/profiles/profile