The Holy Grail of Development Environments: My Journey to Nix
Original Article: The Holy Grail of Development Environments: My Journey to Nix
Summary
The quest for a truly reproducible development environment is a familiar battle for DevOps and platform engineers. Over the years, solving the notorious “it works on my machine” problem has driven the evolution of development tooling — from heavy virtual machines to containerized toolchains and custom host bootstrap scripts.
In this article, I trace my journey across different development environment paradigms, the hurdles encountered with each approach, and why Nix and Nix Flakes proved to be the ultimate solution for delivering deterministic, declarative environments across local machines, cloud workspaces (GitHub Codespaces / Gitpod), and CI pipelines.
The Evolution
The Docker Era: Containerising CLI Tools
The initial impulse was to encapsulate tooling (Terraform, Ansible, AWS CLI, Kubernetes utilities) into container images and create shell aliases (docker_tools_alias) mapping host volumes. While portable, this approach introduces subtle friction: file permission mismatches, overhead running sub-commands, and awkward interactions with the host filesystem.
The Bash Era (ibt): Custom Bootstrap Scripts
To avoid container boundaries on the host, the next phase involved custom Bash orchestration scripts within infra-bootstrap-tools. While effective for bootstrapping machines and Gitpod instances, maintaining custom bash wrappers across disparate package managers quickly becomes an unsustainable maintenance burden.
The Nix Paradigm: True Reproducibility
Adopting Nix fundamentally transforms environment management:
- Declarative Specifications: Environments are defined declaratively in
flake.nixorshell.nix. - Isolated Dependency Store: Packages live in
/nix/storewith cryptographic hashes, preventing version clashes and global state corruption. - Cross-Environment Consistency: Developers get the identical toolchain locally on Linux/macOS, in cloud dev environments, and inside CI runners.
- Frictionless Activation: Combined with
direnvandnix-direnv, entering a repository directory automatically sets up the exact runtime tools and dependencies without polluting the global environment.
Key Takeaways
- Containerizing CLI tools solves isolation but creates unnecessary workflow and filesystem friction.
- Custom bash installers duplicate package manager logic and require constant maintenance.
- Nix provides a functional, deterministic package management model that unifies local and remote development setups.




