Providing reproducible environments for all developers
LazyNix makes Nix development environments accessible through simple YAML configuration.
- π Simple: Write YAML instead of Nix expressions
- π Reproducible: Powered by Nix flakes for deterministic builds
- π― Focused: Designed for DevShell only - when you need more, just use the generated
flake.nix
We recommend using Nix to install LazyNix. If you don't have Nix installed yet, get it from nixos.org/download.
Alternatively, you can use pre-built binaries from the release page or build from source.
Try LazyNix without installing anything. Run it directly from GitHub using nix run:
# Display help
nix run github:shunsock/lazynix -- --help
# Initialize a new project
nix run github:shunsock/lazynix -- init
# Enter development environment
nix run github:shunsock/lazynix -- developFor permanent installation, add LazyNix to your Nix profile:
# Install from GitHub
nix profile install github:shunsock/lazynix
# Then use the lnix command directly
lnix --help
lnix init
lnix developDownload platform-specific binaries from GitHub Releases.
curl -L -o lnix https://github.com/shunsock/lazynix/releases/latest/download/lnix-x86_64-linux
chmod +x lnix
sudo mv lnix /usr/local/bin/curl -L -o lnix https://github.com/shunsock/lazynix/releases/latest/download/lnix-aarch64-linux
chmod +x lnix
sudo mv lnix /usr/local/bin/curl -L -o lnix https://github.com/shunsock/lazynix/releases/latest/download/lnix-aarch64-darwin
chmod +x lnix
sudo mv lnix /usr/local/bin/Clone the repository and build using Nix:
# Clone the repository
git clone https://github.com/shunsock/lazynix.git
cd lazynix
# Build with Nix
nix build
# Run the built binary
./result/bin/lnix --helpCreate a new LazyNix configuration in your project directory:
lnix initThis creates two files:
- π
lazynix.yaml- Your environment configuration (edit this) - βοΈ
flake.nix- Generated Nix flake (auto-generated, don't edit)
Edit lazynix.yaml to specify your development tools. Find packages at search.nixos.org.
devShell:
allowUnfree: true
package:
stable:
- name: python312
- name: uv
unstable: []
pinned: []
shellHook:
- "echo Python $(python --version) ready!"
- "echo uv $(uv --version) ready!"
env:
# Load from .env files
dotenv:
- .env
# Define variables directly
envvar:
- name: PYTHONPATH
value: ./src
- name: DEBUG
value: "true"Activate your configured environment:
lnix developLazyNix will automatically:
- π Read your
lazynix.yamlconfiguration - π§ Generate the
flake.nixfile - π Update
flake.lockwith pinned dependencies (with--update) - π Enter the Nix development shell with all specified packages
Regenerate flake.nix from lazynix.yaml without entering a shell or running any commands:
lnix generateHandy for CI validation, applying edits to lazynix.yaml, or preparing to migrate to Pure Nix. When no pinned packages are configured, this runs fully offline without invoking Nix.
LazyNix ships nine subcommands. All commands accept the global flags described below.
| Subcommand | Description | Flags |
|---|---|---|
init |
Create lazynix.yaml and flake.nix from templates |
--force (-f) β overwrite existing files |
update |
Update flake.lock without entering a shell |
β |
generate |
Regenerate flake.nix from lazynix.yaml without entering the shell |
β |
develop |
Generate flake.nix and enter nix develop |
--update β update flake.lock first |
run [--] <command>... |
Run a single command inside the dev environment | --update, --no-regen (skip regenerating flake.nix) |
test |
Run test commands defined under devShell.test: |
--update |
task <name> [args...] |
Run a named task from devShell.task:; trailing args expand into {{.CLI_ARGS}} |
β |
lint |
Validate every declared package (stable + unstable + pinned) via nix eval, and verify pinned versions can still be resolved |
--verbose (-v), --arch <target> |
search <package> |
Look up available versions via nix-versions |
--version <semver> (-v), --json (-j), --one (-1) |
-C, --config-dir <DIR>β directory containinglazynix.yamlandlazynix-settings.yaml(env:LAZYNIX_CONFIG_DIR, default: current directory)--versionβ print the CLI version and exit
lintexits with code1when any package fails validation, otherwise0.run,task, andtestpropagate the exit code of the underlying child process.lintvalidatesstable,unstable, andpinnedpackages. For eachpinnedentry,lintalso asksnix-versionswhether the requested version can still be resolved, catching typos in the version constraint before the nextlnix develop.
By default, LazyNix looks for lazynix.yaml and lazynix-settings.yaml in the current directory. You can customize this location using either a CLI flag or environment variable.
1. CLI Flag (Recommended for one-off usage)
Use the --config-dir flag (or -C short form) before the subcommand:
lnix --config-dir ./configs develop
lnix -C ./configs develop # Short form2. Environment Variable (Recommended for persistent setup)
Set the LAZYNIX_CONFIG_DIR environment variable:
LAZYNIX_CONFIG_DIR=./configs lnix develop
# Or export for the entire session
export LAZYNIX_CONFIG_DIR=./configs
lnix init
lnix developLazyNix supports an optional lazynix-settings.yaml file for system-level customization. This file is completely optional - LazyNix works perfectly without it using sensible defaults.
When to use settings:
- Override nixpkgs versions (use older/newer packages)
By default, LazyNix uses nixos-25.11 for stable packages. You can override this in lazynix-settings.yaml:
# lazynix-settings.yaml
override-stable-package: "github:myorg/nixpkgs/custom-branch"override-stable-package only affects the stable channel; the
unstable channel is hardcoded to github:NixOS/nixpkgs/nixos-unstable.
The devShell.package.pinned list lets you pin a package to an exact
version, resolved through nix-versions and locked into the generated
flake. This is the recommended way to control language runtimes such
as go, node, or python down to the patch level.
Workflow:
-
Find a candidate version:
lnix search go -v '>=1.21,<1.22' -
Add the resolved name and version to
lazynix.yaml:devShell: package: stable: - name: python312 pinned: - name: go version: "1.21.13"
-
Run
lnix develop(orrun/test). LazyNix asksnix-versionsfor the exactnixpkgscommit that ships the requested version, then embeds that commit into the generatedflake.nixas its input URL. Subsequent runs reuse the commit encoded inflake.nixand skip the resolver call β theflake.nixis the cache.lazynix.yamlis never touched.
Alias definitions can be sourced from external files via
devShell.shellAlias. Each entry is a path β relative, absolute, or
~-prefixed β to a shell script whose alias definitions will be
loaded into the dev shell.
devShell:
allowUnfree: true
package:
stable:
- name: bash
shellAlias:
- ./aliases.sh
- ~/.bash_aliases
- /etc/aliases.shRelative paths are resolved against $PWD; ~ is expanded to the
user's home directory; absolute paths are used as-is.
Two related sections describe reusable commands that run inside the dev shell:
devShell.testβ a flat list of shell commands.lnix testruns them in order and stops on the first failure. Use this for smoke tests you want to run without remembering a task name.devShell.taskβ a named map of workflows, each with an optionaldescriptionand a list ofcommands. Run a task withlnix task <name>. Any trailing arguments are substituted into the{{.CLI_ARGS}}placeholder inside the task's commands, so a single task can accept variable arguments.
devShell:
allowUnfree: true
package:
stable:
- name: python312
- name: uv
task:
fmt:
description: "Format Python sources"
commands:
- "uv run ruff format ."
review:
description: "Run a specific pytest, forwarded via CLI_ARGS"
commands:
- "uv run pytest {{.CLI_ARGS}}"
test:
- "uv run pytest"
- "uv run mypy src/"Example invocations:
lnix task fmt
lnix task review tests/test_api.py::test_auth # expands into {{.CLI_ARGS}}
lnix test- Reproducible Development Environments: Consistent, shareable dev setups
- Simple Configuration Interface: YAML instead of Nix expressions
- Cover All Nix Features: No build definitions, overlays, or modules
- Replace Nix: It's a thin layer on top of Nix flakes
- Manage System Configuration: Only development environments
When you need advanced Nix features, migration is seamless. LazyNix generates a standard flake.nix, so:
- βοΈ Run
lnix generateto produce the latestflake.nixfromlazynix.yaml - ποΈ Delete
lazynix.yaml - βοΈ Continue editing
flake.nixdirectly
That's all! Your development environment keeps working without any changes.
We welcome contributions!
This project is licensed under the MIT License.
