DEV Community

Sven Lito
Sven Lito

Posted on

Using Nix Flakes for Reproducible Dev Environments

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)"
          '';
        };
      });
}
Enter fullscreen mode Exit fullscreen mode

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}";
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Now, the environment is activated automatically, and your .env file is loaded.

Getting Started

  1. Install Nix with flakes enabled:
sh <(curl -L https://nixos.org/nix/install)
Enter fullscreen mode Exit fullscreen mode
  1. Enable flakes in ~/.config/nix/nix.conf:
experimental-features = nix-command flakes
Enter fullscreen mode Exit fullscreen mode
  1. Add a flake.nix to your project (use the example above)

  2. Add a .envrc file:

use flake
dotenv
Enter fullscreen mode Exit fullscreen mode
  1. 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
Enter fullscreen mode Exit fullscreen mode

That's it. You now have a reproducible development environment that works consistently on any machine.

Top comments (0)