D4no0

D4no0

Using multiple elixir/erlang versions from nixos

I was wondering, what is the most painless way to get started with multiple elixir/erlang versions on nixos?

I’ve tried to install asdf, however when it comes to compiling OTP, I cannot configure correctly the dependencies required by OTP when building it from source, I am more than positive that the packages I’m using are the incorrect ones.

I also have zero idea how things like flakes, home-manager works (and I don’t have them set up yet), so it would be great to get a turnkey solution for the time being, until I get my head around all the concepts of nix.

Marked As Solved

D4no0

D4no0

I managed to get the project up and running using the sample shell.nix config, it is great how easy that was and how it worked out of the box. It will take some time to setup the language server (as I want to use elixir-ls), but having the initial configuration that works is the core thing I was searching for hours and couldn’t find.

For now I will stick to nix-shell, as it works and I actually understand at least partly what is going on there :smiley: , but this doesn’t mean that @joelpaulkoch 's answer is not valid too, maybe I will try it later when I will have more time.

To keep maximum visibility for other people searching for this too I will leave both answers in this post and mark this post as the correct answer:

Thanks again @cmkarlsson and @joelpaulkoch for the swift response and detailed answers!

Also Liked

joelpaulkoch

joelpaulkoch

Have you tried following this post: Nix Flake Template for Elixir? - #5 by c4710n ? (And in particular this repository: GitHub - nix-giant/nix-dev-templates: A collection of Nix flake templates for development.)

You’ll have to enable flakes, but you can also do that temporarily by passing a cli argument as described here: https://nixos.wiki/wiki/flakes

I believe the following should work:

Inside your project directory run nix --experimental-features 'nix-command flakes' flake init --template github:c4710n/nix-dev-templates#elixir

Then you can change your version in flake.nix, you can search for available versions here: NixOS Search

Then run nix --experimental-features 'nix-command flakes' develop and you should have your version available.

I’m not sure about the enabling flakes temporarily part, so I hope that works.

Edit: It could be that the updated version is not picked up by nix because the old version is still in flake.lock, or when it’s not staged in git. I think I remember running into this at some point.

cmkarlsson

cmkarlsson

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:

  1. Create your project directory $ mkdir ~/tmp/g
  2. Create a shell.nix file with content below
  3. 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!

cmkarlsson

cmkarlsson

Yes, it is a bit unfortunate. From my understanding they are two different ways to do the same thing. I am sure flakes are great but I haven’t explored them much so cannot give any assistance regarding them.

nix-shell is the standard (or perhaps old?) way of specifying development shells. Flakes can also specify development shells but they can also do much more.

I should mention that my instructions above requires a channel named nixpkgs on your system. This is the default if you install nixos but if you have configured flakes for your system, the shell.nix might need to be adapted.

Yes. You should be able to do the same as well as modifying compiler flags if that is necessary. I had a quick look at the package so I think it should be enough to substitute elixir for version and then specify the correct revision, owner and repo in the src override.
Completely untested and more what I would try first:

   myerlang = packages.elixir.overrideAttrs(old: {
     name = "erlang-26.2.5.4";
     src = fetchFromGitHub { 
       rev = "v26.2.5.4";
       sha256 = "sha256-SOMETHINGESLE-NIX-WILL-TELL-YOU-WHAT-IT-IS_WHEN-YOU-BUILD";
       owner = "erlang";
       repo = "otp";
     };
   });

  ... 
  basePackages [ ... , myerlang, ... ]

I am not at all sure what the name aned rev should be above though.

Where Next?

Popular in Questions Top

pgiesin
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database. Dep...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

Other popular topics Top

JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New

We're in Beta

About us Mission Statement