- Add flake.nix + flake.lock pinning nixpkgs-unstable - Add nix/shell.nix with PostgreSQL, Rust, Elixir LSP, pre-commit hooks - Add shell.nix flake-compat shim for non-flake nix-shell users - Bump Dockerfile to Elixir 1.20.2 / OTP 29.0.3 - Add nix.md with setup and troubleshooting docs - Gitignore Nix runtime state and generated pre-commit config Co-Authored-By: Claude <noreply@anthropic.com>
5 KiB
Nix + direnv Setup
This project uses Nix flakes to provide a reproducible dev environment that activates automatically when you cd into the project directory. No more "works on my machine" — every dev gets the same Elixir, Erlang, Rust, and PostgreSQL.
How It Works
- Nix flakes (
flake.nix) declare every tool and library the project needs, pinned viaflake.lock. - direnv watches for
.envrcand loads/unloads the environment as you enter/leave the directory. - nix-direnv bridges the two, so
use flakein.envrctriggersnix develop.
The result: cd into the repo and you're ready to run mix phx.server. cd out and your shell is clean. No global installs, no version conflicts, no manual setup.
File Layout
| File | Purpose |
|---|---|
flake.nix |
Top-level flake: pins nixpkgs, Erlang/Elixir versions, defines dev shell |
flake.lock |
Locked hashes for every dependency — reproducible across machines |
nix/shell.nix |
Dev shell definition: packages, env vars, PostgreSQL service scripts, pre-commit hooks |
shell.nix |
Flake-compat shim so nix-shell works without --extra-experimental-features flakes |
.envrc |
Contains use flake (gitignored — create it yourself on each machine) |
One-Time Setup
1. Install Nix (multi-user)
sh <(curl -L https://nixos.org/nix/install)
Follow the prompts. The multi-user install (--daemon) is recommended on macOS. When it finishes, open a new terminal so the Nix daemon is running.
Enable flakes. Add this to ~/.config/nix/nix.conf (create the file if needed):
experimental-features = nix-command flakes
Verify:
nix --version
nix flake --help
2. Install direnv
brew install direnv
Add the direnv hook to your shell's rc file. Running brew info direnv will print the exact line — pick the one for your shell:
fish (~/.config/fish/config.fish):
direnv hook fish | source
zsh (~/.zshrc):
eval "$(direnv hook zsh)"
3. Install nix-direnv
brew install nix-direnv
Then configure direnv to load it. Create (or append to) ~/.config/direnv/direnvrc:
mkdir -p ~/.config/direnv
cat > ~/.config/direnv/direnvrc << 'EOF'
source "$(brew --prefix nix-direnv)/share/nix-direnv/direnvrc"
EOF
If you installed nix-direnv via nix profile instead of Homebrew, the source line is:
source $HOME/.nix-profile/share/nix-direnv/direnvrc
4. Create .envrc
echo 'use flake' > .envrc
This is gitignored — each dev creates their own. You can add personal overrides (e.g., extra env vars) here.
5. Restart your shell
Open a new terminal tab or exec $SHELL so the direnv hook takes effect.
6. First time entering the project directory
cd ~/dev/prop
direnv allow # one-time approval — thereafter it loads automatically
The first build may take several minutes while Nix downloads and compiles packages (Erlang, Elixir, Rust, PostgreSQL, etc.). Subsequent entries are instant — direnv caches the shell.
Daily Use
Once set up, just cd into the repo. The shell activates automatically:
- PostgreSQL starts on port 5432 with
prop_devandprop_testdatabases created start-services/stop-servicescommands are available to manually control PostgreSQL- Mix/Hex are sandboxed to
.nix-mix/.nix-hex(doesn't pollute~/.mix) - Pre-commit hooks (mix format, credo, nixfmt, cargo fmt, cargo clippy) install automatically
- Services stop automatically when you
cdout of the directory
Troubleshooting
direnv: error .envrc is blocked
Run direnv allow in the project directory.
nix develop fails with "experimental Nix feature 'nix-command' is disabled"
Add experimental-features = nix-command flakes to ~/.config/nix/nix.conf.
Empty shell, no Elixir available
Run nix develop --command elixir --version to see build output. Check for syntax errors in flake.nix or nix/shell.nix:
nix flake check
Flake can't see new or modified files Nix flakes only see files tracked by git. If you added a new nix file, stage it first:
git add -N <file>
Nix build fails with a hash mismatch in shell.nix (flake-compat)
The flake-compat tarball hash in shell.nix is stale. Update it to the actual hash shown in the error message.
direnv not triggering on cd
Check your shell's rc file has the hook line from step 2. Run direnv status in the project directory.
I added a new package to nix/shell.nix — how do I reload?
direnv watches flake.nix and flake.lock for changes and rebuilds automatically. If it doesn't pick up the change, run direnv reload.
Updating Dependencies
The flake pins everything via flake.lock. To update all inputs to their latest versions:
nix flake update
To update just nixpkgs (e.g., to get a newer Erlang/Elixir):
nix flake lock --update-input nixpkgs
Commit the updated flake.lock so everyone gets the same versions.