Reproducible everything: my NixOS + devenv stack
12 September 2025 · 6 min read
- NixOS
- devenv
- Devops
- Flakes
If you’ve heard me talk you know the punchline already, so here’s the architecture instead.
One repo, three surfaces
flake.nix ← single entrypoint, pinned nixpkgs 26.05
devenv.nix ← the dev shell (node, postgres, s3, git-hooks)
nixos/ ← production machine configuration Dev (devenv up), CI (nix flake check + action runners inside the
shell), and production (nixos-rebuild switch on a target host) all read
the same arm of the flake. The “works on my machine” bug dies here.
devenv does the boring work
devenv gives me:
languages.javascriptwith pinned Node + pnpm, autopnpm install- services (postgres, garage) that start when you enter the shell
- git-hooks (prettier, typos, eslint) so review isn’t a style discussion
Secrets the boring way
- Local dev: gitignored
.env, loaded by devenv’s dotenv integration - Production:
sops-nixdecrypts age-encrypted secrets into/run/secrets/at activation — the running service just reads its env var
No plaintext credentials in git history, not even “temporary” ones.
The gotchas you’ll actually hit
- Erase your darlings in
flake.lock— pin it, commit it, or your CI and prod will diverge on a Monday. - Prisma on NixOS — point the engine binaries at nixpkgs via
PRISMA_*_BINARYenv vars, don’t let the npm client download its own. - Systemd hardening is free —
DynamicUser,StateDirectory,PrivateTmp, andIPAddressDeny=anywith explicit allows. Half-day of work, years of fewer 3am pages.