# lorri: a Nix shell that rebuilds itself before you notice

> lorri watches your shell.nix or flake.nix, rebuilds the development environment in the background, and applies the result on your next shell prompt, so entering a project directory gives you the current shell rather than the one you built an hour ago. It is a Go daemon with a shell hook, built to replace nix-shell and to keep working while direnv integrations break.

**nix-community/lorri** — Your project’s nix-env [maintainer=@Profpatsch,@nyarly]

- Repository: https://github.com/nix-community/lorri
- Stars: 879 · Forks: 28
- Language: Go
- License: Apache-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/nix-community-lorri

## The environment updates on the next prompt, not at the next cd

lorri is described as a nix-shell replacement for project development. It monitors your shell.nix and flake.nix and rebuilds the development environment in the background, automatically applying changes on your next shell prompt. The distinction from nix-shell is the whole point. A plain nix-shell blocks while it builds, so every entry into a project directory costs you the build time, and every change to a dependency costs you that again. lorri moves the build out of the critical path: the new shell is constructed while you are doing something else, and the shell hook swaps the environment in when you next press enter. The result, as the README puts it, is that development tools stay in sync with the current Nix shell configuration, even as you switch branches, without blocking your use of the terminal. The emphasis on quick feedback is consistent with that design.

## The cached environment is what makes the instant start possible

There is a second mechanism that the README compares with the direnv integration, and it explains why lorri feels different. Unlike the use nix direnv integration, lorri caches the last shell environment and applies it instantly, so you can start work where you left off, and after a few seconds the new environment is ready to load. The split between the two behaviours is the design. The cache is what gives you an immediate environment on entry, and the background build is what keeps that cache fresh. The cached environment is loaded even when the daemon is not running, but the daemon must be running for the environment to be updated when shell.nix changes. That distinction is worth remembering, because it explains the confusing case where a shell looks right but a dependency edit seems to have had no effect.

## Registering a project with watch and unwatch

lorri only loads environments for projects it knows about, so there is an explicit registration step. Run lorri watch in the project directory and it will auto-detect your shell.nix or flake.nix, and suggest nix flake init if neither exists. Once a project is registered, the daemon rebuilds it when its shell definition changes, and lorri supports both shell.nix and devShells in flake.nix files, so a project does not have to choose between the two styles. To stop managing a project, run lorri unwatch, which removes its garbage collection root and stops the daemon from rebuilding it. That GC root detail is not incidental: it is what keeps the built store paths alive for a watched project, and removing it is what allows Nix to collect them again. The repository includes a garbage-collection.md document at the root, which suggests this interaction is a known source of confusion.

## Two install paths, and the daemon service gap on NixOS

On NixOS, or with home-manager on Linux, and a Nixpkgs channel at least as recent as nixos-19.09, you enable the daemon service by setting services.lorri.enable = true; in your configuration.nix or home.nix, which automatically installs the lorri command. There is a known issue preventing the daemon from starting automatically upon installation, so until it is resolved you have to reload the user daemon by hand with systemctl --user daemon-reload, or reboot. Elsewhere, install lorri from Nixpkgs with nix-env -i lorri if your channel is recent enough, or from the repository itself.

```console
# With flakes:
$ nix profile install github:nix-community/lorri

# Without flakes:
$ nix-env -if https://github.com/nix-community/lorri/archive/canon.tar.gz
```

The archive URL points at the canon branch, which is also the default branch of the repository. For testing you can start the daemon in a separate terminal with lorri daemon, and contrib/daemon.md documents ways to start it automatically in the background.

## The shell hook is three lines of rc file

Once the daemon runs, the shell integration is a small snippet in your rc file, and it differs only in how the shell reads it.

```bash
# bash (~/.bashrc)
eval "$(lorri hook bash)"

# zsh (~/.zshrc)
eval "$(lorri hook zsh)"

# fish (~/.config/fish/config.fish)
lorri hook fish | source
```

bash and zsh use command substitution into eval, while fish pipes the output into source, because fish does not read from the same kind of substitution. The hook is what applies the cached environment on directory change and what prompts the daemon to rebuild. After installing it, open a new shell and cd into the project directory, and lorri will start building the environment automatically. The implementation reuses direnv's shell integration modules, which the README credits as a strong inspiration for the project.

## Editor integration is the part that still leans on direnv

Since lorri manages the shell environment directly, any editor launched from a shell with the hook active inherits the correct environment automatically. That covers most cases, and the README suggests trying without a direnv plugin first. The gap is per-buffer environment switching in Emacs: with the native lorri hook, that requires envrc.el or something similar, and there is a direnv-mode tutorial in contrib/emacs.md describing editor-level integration. Editors such as vscode proxy debug commands through the user shell, so the environment usually follows, but for projects that do not change often it can be enough to cd into the directory and start the editor from inside the changed environment. If you really rely on direnv integration such as emacs-direnv or vscode-direnv, the README points at the direnv adapter as the alternative to a full migration.

```bash
eval "$(lorri export direnv-adapter)"
```

That is what you put in your .envrc, and the separate lorri direnv command keeps working during a transition but is deprecated.

## The Go implementation and its two dependencies

The repository is Go, with a module file requiring go 1.26 and two direct dependencies: fsnotify for filesystem watching, and zombiezen.com/go/sqlite for the database. The sqlite choice is notable, because it is a pure Go implementation rather than a cgo binding, which keeps the build free of a C toolchain and consistent with the Nix workflow the tool serves. The source layout at the root is a decent map of the design: daemon.go for the long-running process, watch.go for project registration and file watching, build_loop.go for the background rebuild cycle, builder.go for the build itself, communicate.go for the channel between daemon and client, envjson.go for the serialised environment, export.go for the shell hook and the direnv adapter, paths.go for path handling, and lorri_db.go for the sqlite state. There is also a lorri.scd script for completions, a go.work file, and separate nix and contrib directories.

## Where lorri is the wrong choice

Four limits apply. It needs Nix to be useful, since the whole model is a Nix development shell, so it is not a general environment manager. It supports Linux and macOS, and the README is blunt that macOS support is brittle and that the project is looking for funding to improve it. It does not cover per-buffer environment switching in Emacs without envrc.el or a similar tool. And it is a single-developer-funded project whose future work is tied to a business support offering and a roadmap document that describes planned improvements needing funding. On versions, the released tags are 1.7.1 from 2024-08-16, 1.7.0 from 2024-06-26 and 1.6.0 from 2022-10-02, while the last push to the repository was on 2026-04-29, so the code has moved since the last tag. The licence field on the repository says Apache-2.0.

## Conclusion

Use lorri when you work in Nix across several projects and the cost of a stale development shell has become visible, because the background rebuild plus the cached environment removes the wait without asking you to change how you enter a directory. Do not adopt it on macOS expecting parity, since the README calls that support brittle and says the project is looking for funding to improve it, and skip it if you depend on direnv per-buffer environment switching in Emacs, where the native hook does not cover the use case. Verify first that you have a shell.nix or flake.nix in the project, that your shell is one of bash, zsh or fish for the hook snippet, and that the daemon is running, since the cached environment loads even when the daemon is stopped but updates do not happen without it.

## FAQ

### What does lorri do that nix-shell does not?

lorri rebuilds the development environment in the background and applies the changes on your next shell prompt, so entering a project directory does not block on a build. It also caches the last shell environment and applies it instantly, unlike the use nix direnv integration.

### How do I install lorri on NixOS or home-manager?

Set services.lorri.enable = true; in your configuration.nix or home.nix, which installs the command and the daemon service. There is a known issue that stops the daemon starting automatically on installation, so you need to run systemctl --user daemon-reload by hand or reboot.

### Does lorri work with flakes as well as shell.nix?

Yes, it supports both shell.nix and devShells in flake.nix files. If a project has neither, lorri watch will suggest how to create one with nix flake init.

### Can I still use direnv with lorri?

The lorri direnv command continues to work during the transition but is deprecated. To migrate, add the native hook to your shell rc file and remove your .envrc, and if you rely on direnv for editor integration such as emacs-direnv you can use the direnv adapter with eval "$(lorri export direnv-adapter)" in your .envrc instead.

### Does the environment still load if the lorri daemon is not running?

The cached environment is loaded even when the daemon is stopped. The daemon must be running for the environment to be updated when shell.nix changes, which is the distinction to keep in mind when a dependency edit appears to have no effect.

## Sources

- [Issues](https://github.com/nix-community/lorri/issues)
- [License: Apache-2.0](https://github.com/nix-community/lorri/blob/canon/LICENSE)
- [nix-community/lorri on GitHub](https://github.com/nix-community/lorri)
- [README](https://github.com/nix-community/lorri/blob/canon/README.md)
- [Releases](https://github.com/nix-community/lorri/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nix-community-lorri
