Welcome to NixOS!
To paraphrase a famous french philosopher. “I’m sorry for the long post, I didn’t have time to make it shorter.”
Quick Introduction
The latest stable (24.05) release of nixos comes with erlang 24, 25, 26, 27.
If you have NixOS installed you can try any of them out by running nix-shell
> nix-shell -p erlang_25
[nix-shell:~/tmp/g]$ erl
Erlang/OTP 25 [erts-13.2.2.9] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]
Eshell V13.2.2.9 (abort with ^G)
1>
Once you exit the shell erlang_25
goes aways and everything is cleaned up. nix-shell
is great to try different kind of software without messing anything up. You can find all the packages here https://search.nixos.org
Setting up erlang/elixir dev environments
To make the versions you use more convenient and bundle up other pieces of software I setup shell development environments.
Lets explore:
- Create your project directory
$ mkdir ~/tmp/g
- Create a
shell.nix
file with content below - Enable shell environment with
$ nix-shell
My standard simple shell.nix
file for erlang/elixir development looks like this:
with import <nixpkgs> {};
let
basePackages = [
erlang_26
elixir_1_15
lexical
inotify-tools
];
PROJECT_ROOT = builtins.toString ./.;
## Hooks are not necessary but make things more convenient.
hooks = ''
mkdir -p .nix-mix
mkdir -p .nix-hex
export MIX_HOME=${PROJECT_ROOT}/.nix-mix
export HEX_HOME=${PROJECT_ROOT}/.nix-hex
export PATH=$MIX_HOME/bin:$PATH
export PATH=$HEX_HOME/bin:$PATH
export LANG=en_NZ.UTF-8
export ERL_AFLAGS="-kernel shell_history enabled"
'';
in mkShell {
buildInputs = basePackages;
shellHook = hooks;
}
I put this shell.nix
file in the root of my project directory. I can the cd
into the dir and run nix-shell
$ cd ~/tmp/g
$ nix-shell
$
And I have access to all the tools listed in the basePackages.
To make things a more convenient I use nix-direnv (GitHub - nix-community/nix-direnv: A fast, persistent use_nix/use_flake implementation for direnv [maintainer=@Mic92 / @bbenne10]) which means I can just cd
into the directory and it automatically enables my environment and keeps my preferred shell (I use fish, whereas the standard nix-shell gives you bash).
Overriding elixir version
I am generally happy with using the erlang versions that come with NixOS. However, I often want to run a newer elixir version. Please look at the following shell.nix
file for building elixir from github.
with import <nixpkgs> {};
## OVERCOURSE. The above statements uses Nix Channels and the channel named nixpkgs.
## If this channel is updated then your versions might change. This is one of the reasons I think
## flakes were introduced.
## However. If you want to pin your packages to a specific version of the nix repository then you can do it like
## This:
# # pkgs = import (builtins.fetchTarball {
## url = "https://github.com/NixOS/nixpkgs/archive/23c10dbe320e6957f2607d8a22f9e0e36f56a235.tar.gz";
## }) {};
## Once you get the hang of things I would recommend you to do this. Then it means, whoever uses your `shell.nix` file be it now or in the future will have the exact same setup.
let
# First I specify which beam packages base package I want to use to
# base my overrides on. In this case erlang_27
packages = beam.packagesWith beam.interpreters.erlang_27;
# Then I override the elixir package by fetching from github.
# If you try with a new version, make sure to change the sha256 slightly
# otherwise nix is "smart" enough to pick up the version which matches the hash
elixir = packages.elixir.overrideAttrs(old: {
name = "elixir-1.17.2";
src = fetchFromGitHub {
rev = "v1.17.2";
sha256 = "sha256-8rb2f4CvJzio3QgoxvCv1iz8HooXze0tWUJ4Sc13dxg=";
owner = "elixir-lang";
repo = "elixir";
};
});
# And often I also want to compile elixir-ls with the new elixir version
elixir_ls = packages.elixir-ls.override {
elixir = elixir;
mixRelease = packages.mixRelease.override { elixir = elixir; };
};
# Then I specify my development packages
basePackages = [
elixir
elixir_ls
lexical
packages.erlang
];
PROJECT_ROOT = builtins.toString ./.;
hooks = ''
mkdir -p .nix-mix
mkdir -p .nix-hex
export MIX_HOME=${PROJECT_ROOT}/.nix-mix
export HEX_HOME=${PROJECT_ROOT}/.nix-hex
export PATH=$MIX_HOME/bin:$PATH
export PATH=$HEX_HOME/bin:$PATH
export LANG=en_NZ.UTF-8
export ERL_AFLAGS="-kernel shell_history enabled"
'';
in mkShell {
buildInputs = basePackages;
shellHook = hooks;
}
For a setup with a separate postgresql database per project please check out the answer here:
I am a big fan of NixOS. WIth the development shells it makes it so easy for me to setup nearly all my projects development requirements and it has saved me so much time over the years I have been using it. And they just keep on working!