How to create a Nix development shell for Phoenix?

Coming from Haskell, I have been enjoying learning Elixir and would like to learn how Phoenix works. Creating a development shell is a little tricky because of postgresql, which I also want installed in the shell and not on the system. Can anyone point me to working examples? The few I found aren’t working properly.

Thank you

Hi!

I tend to use the following shell.nix for my phoenix development.

The goal is to have each project run their own of everything so I run process locally and on domain socket only to avoid having to fiddle with different ports for different projects.

It is not completely automatic though. After the shell is installed you need to manually start postgres and create the postgres user needed for phoenix development.

Prerequisites:

Files needed

  • shell.nix
  • .envrc

shell.nix:

with import <nixpkgs> {}; # This should probably be pinned to something. For me it points to 24.05 channel
let
  basePackages = [
    beam.packages.erlangR26.elixir_1_15
    beam.packages.erlangR26.erlang
    elixir_ls
    inotify-tools
    nodejs
    yarn
    postgresql
    process-compose
  ];
  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"

    set -e
    export PGDIR=${PROJECT_ROOT}/postgres
    export PGHOST=$PGDIR
    export PGDATA=$PGDIR/data
    export PGLOG=$PGDIR/log
    export DATABASE_URL="postgresql:///postgres?host=$PGDIR"

    if test ! -d $PGDIR; then
      mkdir $PGDIR
    fi

   if [ ! -d $PGDATA ]; then
     echo 'Initializing postgresql database...'
     initdb $PGDATA --auth=trust >/dev/null
   fi

    '';

  in mkShell {
    buildInputs = basePackages;
    shellHook = hooks;
  }

.envrc:

use nix

Initialize Shell

$ mkdir projects/thing
$ cd projects/thing
$ vim shell.nix # (with content from above)
$ vim .envrc  # (with content from above)
$ direnv allow # Allow direnv to control the directory

Now direnv should make sure all the software and configuration in shell.nix is installed.

Start postgres

Once installed I run postgres as my own user on domain sockets only:

$ cd project/thing
$ postgres -h '' -k $PGHOST # -h '' disables network to run on domain socket only, -k points to socket file directory
2024-08-20 14:43:57.679 NZST [2506190] LOG:  starting PostgreSQL 15.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 13.2.0, 64-bit
2024-08-20 14:43:57.687 NZST [2506190] LOG:  listening on Unix socket "/home/martink/projects/thing/postgres/.s.PGSQL.5432"
2024-08-20 14:43:57.707 NZST [2506193] LOG:  database system was shut down at 2024-08-20 14:43:39 NZST
2024-08-20 14:43:57.717 NZST [2506190] LOG:  database system is ready to accept connections

I tend to run postgres in the foreground so that I know what is happening. Usually I am using process-compose for this.

In another shell:

$ cd project/thing
$ createuser -d thing # create a postgres user with create database rights

Install and setup phoenix

Now the project is ready to install phoenix. Because of the MIX_HOME and HEX_HOME variables point to local directory we have an isolated environment

$ cd project/thing
$ mix archive.install hex phx_new
$ mix phx.new .

Modify the dev/config.exs so the correct user is used and you connect to the correct socket:

config :thing, Thing.Repo,
  username: "thing",
  socket_dir: System.get_env("PGHOST"),
  database: "thing_dev",
  stacktrace: true,
  show_sensitive_data_on_connection_error: true,
  pool_size: 10
$ cd project/thing
$ mix ecto.create
$ iex -S mix phx.server

Hopefully now you should be able to have a fully self-container phoenix development environment.

I’ve tested this on NixOS 24.05

7 Likes

Wow, thank you so much. I’m excited to try this tomorrow.

Get BlueMail for Android

I just needed to change erlangR26 to erlang_26, but I was able to mix ecto.create and enter the repl without errors. Your instructions were great. I use NixOS as well, and wish these little gems were more easily findable. Thanks again.

1 Like