Context info:
OS: MacOS 13.6.8
Nix version: 2.23.3
Direnv version: 2.34.0 (installed via homebrew), hooked into oh-my-zsh via plugin
Shell: Zsh/oh-my-zsh
I’m attempting to use nix-shell and direnv on MacOS to create a lightweight, reproducible development environment for a Django web application.
The issue I’m having is that:
I want the shell environment to start Postgres when you enter the shell and (in the most ideal situation) have it automatically stop when the shell is exited.
Right now, my shell.nix
(see first code snippet below) works great when calling nix-shell
directly. But this doesn’t work with direnv / nix-direnv. With direnv, postgres is immediately stopped because the EXIT trap function is immediately triggered OR without the trap function the shell hangs indefintely and my shell prompt isn’t returned.
This is my shell.nix
:
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
with pkgs;
stdenv.mkDerivation {
name = "pokegotchi";
buildInputs = [];
nativeBuildInputs = [
nodejs_20
python312
postgresql_16
google-cloud-sdk
google-cloud-sql-proxy
firebase-tools
];
postgresConf = pkgs.writeText "postgresql.conf" ''
# Add custom settings
log_timezone = 'UTC'
logging_collector = on
log_directory = 'pg_log'
log_filename = 'pgsql_%a.log'
log_truncate_on_rotation = on
log_rotation_age = 1d
'';
# ENV variables
PGDATA = "${toString ./.data/postgres}";
shellHook = ''
export PGHOST="$PGDATA"
[ ! -d $PGDATA ] && pg_ctl initdb -o "-U postgres" && cat "$postgresConf" >> $PGDATA/postgresql.conf
pg_ctl -o "-p 5555 -k $PGDATA" start
function end {
pg_ctl stop
}
trap end EXIT
'';
}
Using nix-shell
directly:
This works perfectly! But this environment isn’t cached because we’re not using nix-direnv.
➜ pokegotchi git:(main) ✗ nix-shell
waiting for server to start....2024-08-02 09:07:13.016 UTC [5623] LOG: redirecting log output to logging collector process
2024-08-02 09:07:13.016 UTC [5623] HINT: Future log output will appear in directory "pg_log".
done
server started
[nix-shell:~/Documents/workspace/pokegotchi]$ psql -p 5555 -U postgres
psql (16.3)
Type "help" for help.
postgres=# \q
[nix-shell:~/Documents/workspace/pokegotchi]$ exit
exit
waiting for server to shut down.... done
server stopped
➜ pokegotchi git:(main) ✗
Using direnv
with the trap on EXIT function:
Postgres is immediately shut down.
➜ pokegotchi git:(main) ✗ cd ../pokegotchi
direnv: loading ~/Documents/workspace/pokegotchi/.envrc
direnv: loading https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.5/direnvrc (sha256-RuwIS+QKFj/T9M2TFXScjBsLR6V3A17YVoEW/Q6AZ1w=)
direnv: using nix --pure
copying '«unknown»/' to the storedirenv: ([/opt/homebrew/bin/direnv export zsh]) is taking a while to execute. Use CTRL-C to give up.
direnv: nix-direnv: Renewed cache
waiting for server to start....2024-08-02 09:14:55.101 UTC [14121] LOG: redirecting log output to logging collector process
2024-08-02 09:14:55.101 UTC [14121] HINT: Future log output will appear in directory "pg_log".
done
server started
waiting for server to shut down.... done
server stopped
➜ pokegotchi git:(main) ✗
Using direnv
without the trap on EXIT function:
Having postgres running seems to hang the process and the shell prompt-doesn’t return. This is is not because pg_ctrl
is not returning. E.g. if you put echo "Hello world"
on the line below this is also executed.
➜ pokegotchi git:(main) ✗ cd ../pokegotchi
direnv: loading ~/Documents/workspace/pokegotchi/.envrc
direnv: loading https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.5/direnvrc (sha256-RuwIS+QKFj/T9M2TFXScjBsLR6V3A17YVoEW/Q6AZ1w=)
direnv: using nix --pure
copying '«unknown»/' to the storedirenv: ([/opt/homebrew/bin/direnv export zsh]) is taking a while to execute. Use CTRL-C to give up.
direnv: nix-direnv: Renewed cache
waiting for server to start....2024-08-02 09:22:03.458 UTC [22216] LOG: redirecting log output to logging collector process
2024-08-02 09:22:03.458 UTC [22216] HINT: Future log output will appear in directory "pg_log".
done
server started
My .envrc
contents:
if ! has nix_direnv_version || ! nix_direnv_version 3.0.5; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.5/direnvrc" "sha256-RuwIS+QKFj/T9M2TFXScjBsLR6V3A17YVoEW/Q6AZ1w="
fi
use nix --pure
This is less about packaging the Django application for deployment and more about corralling a bunch of development tools: postgres, nodejs/npm, python/pip, firebase CLI (for local emulators), google-cloud-sdk, google-cloud-sql-proxy.
You would otherwise have to install each of these tools at a system level separately, all via different means, all at different versions depending on when you first setup the tools and if you’ve updated them since.
I was originally inspired to try this from the following gist and another article: