This can be a pain point to use Neovim with LazyVim declaratively. One of the major reason is LazyVim can write back configurations, which you will need to capture it back in your repo.
I keep my Neovim configuration in my NixOS repo. Rebuild the workstation, and Home Manager puts the files in place again.
But LazyVim also saves things from inside the editor. Enable an extra, or update plugins, and it writes a JSON file. Those writes need somewhere writable. The Nix store is read-only.
I am aware of the Nixvim (opens in a new tab). It is super simplified and controlled. But I need to change declarations each time I need to change LazyVim config.
My setup gives those two files writable links back to the repo. I can use LazyVim normally, then review what changed with git diff.
TL;DR
- Keep your Lua configuration in
app-configs/nvim/. This is actual layout of my nix repo directory. - Keep
lazyvim.jsonandlazy-lock.jsoninapp-configs/nvim-state/. - Let Home Manager link the Lua files normally, and give those two JSON files writable links.
- You need to commit nix repo changes once it is done via Neovim, it does not automatically commit or push anything.
Before you start
Here, we will install Neovim and set up LazyVim.
Use a terminal with true-color support, such as Ghostty or Kitty.
The examples use ~/git-projects/nixos-config, a Home Manager module called home.nix, and a flake host called nixos. Replace the repo path and host name with yours throughout. Start with a clean Git working tree and close Neovim.
| Part | Managed by |
|---|---|
| Neovim executable and command-line tools | NixOS packages |
| Lua settings, keymaps and plugin declarations | Home Manager, from the repo |
Selected LazyVim extras (lazyvim.json) | LazyVim, through a writable link |
Plugin revisions (lazy-lock.json) | lazy.nvim, through a writable link |
| Downloaded plugins, caches and editor history | Normal user data outside Git |
The repo records your configuration and plugin revisions without packaging every plugin through Nix. Plugin installation still needs downloads, and NixOS generations do not roll back these writable files.
How my NixOS repo is laid out (partial view):
nixos-config├── app-configs│ ├── nvim 👈 this one is the declaration│ ├── nvim-state 👈 this one coming back from the lazyvim config├── modules├── recovery├── configuration.nix 👈├── flake.lock├── flake.nix├── hardware-configuration.nix├── home.nix 👈├── packages.nix├── shell.nixI use the 👈 marker to show in the list above, which files to touch.
1. Declare Neovim and its tools
In configuration.nix, append these packages to your existing environment.systemPackages list. If your Neovim is inside packages.nix, keep them there instead. Each package in one place.
environment.systemPackages = with pkgs; [ neovim git curl ripgrep fd fzf lazygit gcc gnumake unzip tree-sitter];Keep your other packages. This assumes the module already accepts pkgs. Neovim is the editor; LazyVim is a ready-made configuration that adds plugins, completion and useful defaults. The other packages provide search, downloads and build tools.
LazyVim currently requires Neovim 0.11.2 or newer (opens in a new tab). A Nerd Font is optional for its icons. Select one in your terminal if you already have it.
2. Put the LazyVim starter in your repo
The commands below create new folders. If you already have repo-managed Neovim files, stop and merge the pattern into those instead.
From your repo root, create a backup directory outside Git:
cd ~/git-projects/nixos-configbackup="$HOME/nvim-backup-$(date +%Y%m%d-%H%M%S)"mkdir -p "$backup"if [ -e ~/.config/nvim ]; then cp -RL ~/.config/nvim "$backup/nvim"fiecho "$backup"Keep that printed path, and use the same terminal for the remaining commands. Now get the official LazyVim starter (opens in a new tab):
mkdir -p app-configs/nvim-stategit clone https://github.com/LazyVim/starter app-configs/nvimmv app-configs/nvim/.git "$backup/starter-git"Moving the starter’s Git metadata aside lets your own repo track the files. Keep plugin revisions and extras in the separate writable folder:
if [ -f app-configs/nvim/lazyvim.json ]; then mv app-configs/nvim/lazyvim.json app-configs/nvim-state/else printf '{}\n' > app-configs/nvim-state/lazyvim.jsonfiprintf '{}\n' > app-configs/nvim-state/lazy-lock.jsonThe empty lockfile is for this fresh installation. LazyVim will populate it on first startup. If you are migrating an existing LazyVim setup, copy your own Lua files and both existing JSON files instead so you retain your settings and plugin versions.
3. Link the configuration with Home Manager
In home.nix, add config to the existing module arguments if it is missing. Keep your other arguments:
{ config, pkgs, ... }:Add these assignments inside the existing module body. Replace any old declarations managing these same Neovim paths:
home.file.".config/nvim" = { source = ./app-configs/nvim; recursive = true;};
home.file.".config/nvim/lazyvim.json".source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/git-projects/nixos-config/app-configs/nvim-state/lazyvim.json";
home.file.".config/nvim/lazy-lock.json".source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/git-projects/nixos-config/app-configs/nvim-state/lazy-lock.json";recursive = true links the individual configuration files. mkOutOfStoreSymlink points to a writable file in your checkout instead of an immutable copy in the Nix store. Use the string path shown here for those two files, not a relative Nix path such as ./app-configs/nvim-state/lazyvim.json.
Keep the checkout at that location. Moving it will break the writable links until you update these paths and rebuild.
4. Build and start Neovim
Stage the new files so a Git-backed flake can see them, then build without activating. If your package list is in packages.nix, use that filename instead of configuration.nix in both git add commands:
git add configuration.nix home.nix app-configs/nvim app-configs/nvim-statesudo nixos-rebuild build --flake .#nixosIf the build fails, fix it before continuing. After it passes, move the old live directory aside so Home Manager can create its links:
if [ -e ~/.config/nvim ]; then mv ~/.config/nvim "$backup/nvim-before-links"fisudo nixos-rebuild switch --flake .#nixosThis avoids overwriting existing Neovim files. My own configuration also uses force = true on the two JSON declarations, but the backup-and-move step makes that unnecessary here.
Check the final destinations:
readlink -f ~/.config/nvim/lazyvim.jsonreadlink -f ~/.config/nvim/lazy-lock.jsonBoth should end in your checkout’s app-configs/nvim-state/, each with its matching filename. If they resolve into /nix/store, stop and check the declarations before changing extras.
If you used Neovim before with a different setup, move ~/.local/share/nvim, ~/.local/state/nvim and ~/.cache/nvim into your backup directory before this first launch. Those folders can contain old plugins and sessions.
Open nvim and let the first plugin installation finish. Run :LazyHealth to check the installation. On NixOS, install any missing language tools through your package list or a project development shell. Mason downloads are not guaranteed to run on NixOS.
5. Save a setting back into Git
Inside Neovim, run :LazyExtras, and press x on an extra you want to enable. Restart Neovim when prompted. Then, from your repo:
git diff HEAD -- app-configs/nvim-state/lazyvim.jsonYou should see the changed extras selection. Plugin updates can also change lazy-lock.json. lazy.nvim recommends keeping that lockfile in version control (opens in a new tab).
Review both files before saving the change in Git:
git diff HEAD -- app-configs/nvim-state/git add configuration.nix home.nix app-configs/nvim app-configs/nvim-stategit commit -m "feat(neovim): track config and writable LazyVim state"On my workstation, I verified the live links point into the repo. The earlier setup check also exercised LazyVim’s JSON save function and recorded a Git change. The interactive toggle above is your check that it works in your own editor.
Everyday changes
Edit Lua settings in app-configs/nvim/, then rebuild to apply them. Extras and plugin updates save straight into the two linked JSON files, so those changes need a Git review rather than another rebuild.
To undo an unwanted plugin update, close Neovim, restore the last committed lockfile, then reopen the editor and run :Lazy restore:
git restore --source=HEAD --staged --worktree -- app-configs/nvim-state/lazy-lock.jsonThat discards uncommitted lockfile changes. The restore command (opens in a new tab) installs the plugin revisions recorded there.
To cancel the whole setup, restore your previous package list and home.nix declarations and rebuild. Move the new ~/.config/nvim directory aside, then copy your saved nvim backup back into ~/.config/ if you had one. Restore any data folders you moved too.
Keep this writable-link trick limited to files you have checked. Some applications replace files in a way that breaks symlinks. For these two LazyVim files, it lets me keep the settings I change in the editor beside the configuration that rebuilds it.
Did this help? or maybe you have a better way to do it. Let me know.
Comments