Typically, every project needs its own set of tools and dependencies. Go projects require specific Go versions, infrastructure projects need particular Terraform releases, and you may have multiple projects that require different versions of the same tool.
The traditional approach involves using version managers (asdf, gvm, tfenv, nvm) or relying on everyone on the team to have the correct versions installed. Version managers often require manual switching between projects, and global installations inevitably conflict across different projects.
Nix flakes solve this by declaring your exact project dependencies in a flake.nix file. When you enter the project directory, the right environment is automatically set up. No global installs, no version conflicts, and it works identically for everyone on any machine.
Here's a typical flake.nix:
{ description = "Development environment for my-project"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; nixpkgs-terraform.url = "github:stackbuilders/nixpkgs-terraform"; }; nixConfig = { extra-substituters = "https://nixpkgs-terraform.cachix.org"; extra-trusted-public-keys = "nixpkgs-terraform.cachix.org-1:8Sit092rIdAVENA3ZVeH9hzSiqI/jng6JiCrQ1Dmusw="; }; outputs = { nixpkgs, flake-utils, nixpkgs-terraform, ... }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; config.allowUnfree = true; }; goVersion = "1.24"; go = pkgs."go_${builtins.replaceStrings ["."] ["_"] goVersion}"; # Pin specific Terraform version terraform = nixpkgs-terraform.packages.${system}."1.13.1"; in { devShells.default = pkgs.mkShell { buildInputs = [ # Go go pkgs.gotools pkgs.gopls # Terraform terraform pkgs.terraform-ls pkgs.tflint # Cloud tools pkgs.awscli2 # Dev tools pkgs.just pkgs.direnv ]; shellHook = '' echo "🚀 Development environment loaded" echo "Go version: $(go version)" echo "Terraform: $(terraform version)" ''; }; }); } What This Gets You
When you cd into the project directory (with direnv configured):
- Installs exact versions of Go (1.24) and Terraform (1.13.1)
- Makes them available only in this shell session
- Doesn't pollute your global system
- Works identically on macOS and Linux
- Works identically for anyone who clones the repo
Pinning Versions
The key is how versions are pinned. For Go:
goVersion = "1.24"; go = pkgs."go_${builtins.replaceStrings ["."] ["_"] goVersion}"; This dynamically constructs go_1_24 from the version string.
For Terraform, use nixpkgs-terraform to pin specific versions:
terraform = nixpkgs-terraform.packages.${system}."1.13.1"; This gives you exact version control without manually tracking package names.
Direnv Integration
Add a .envrc file to load the environment when you cd into the project automatically:
use flake dotenv Now, the environment is activated automatically, and your .env file is loaded.
Getting Started
- Install Nix with flakes enabled:
sh <(curl -L https://nixos.org/nix/install) - Enable flakes in
~/.config/nix/nix.conf:
experimental-features = nix-command flakes Add a
flake.nixto your project (use the example above)Add a
.envrcfile:
use flake dotenv - Run
direnv allow
When you cd into the project, you'll see:
$ direnv allow 🚀 Development environment loaded Go version: go version go1.24.3 darwin/arm64 Terraform: Terraform v1.13.1 That's it. You now have a reproducible development environment that works consistently on any machine.
Top comments (0)