Neovim via Nixos (but not too much)

Aug 1, 2025

Thinner wrappers, impure symlinks




  • neovim-with-plugins.nix is a neovim-wrapper heavily inspired by @viperML's blog post
  • symlink.nix is a very basic symlinking mechanism that allows for nix-based (copied to /nix/store, immutable) and regular filesystem (symlinked from wherever, impure) sources, heavily inspired by (and borrowing code from) @jade's blog post

I've been using nixpkgs' programs.neovim module happily for a while now, with an init.lua packaged as a plugin, for nice lua editing experience. However, the edit / build / debug iteration cycle of this setup is quite slow.

Overall, this kind of wrapper feels quite "thick"; which can be a benefit, as mentioned by @fzakaria 's post', since you're pushing a whole executable and configuration as a program.

I wanted something halfway in between, using nixpkgs' vimPlugin infrastructure, but supplying my own ~/.config/nvim for config.

@viperML had a great post on writing your own neovim wrapper, from which I borrowed heavily in writing neovim-with-plugins. It's basically a thinner wrapper, you give it a list of vimPlugins you want included in the wrapped packpath; not-yet-packaged vimPlugins can be defined witih vimUtils.buildVimPlugin:

# nvim-config.nix
{ pkgs, ... }:
{

  environment.variables = { EDITOR = "nvim"; };

  programs.neovim-with-plugins = {
    enable = true;
    startPlugins = with pkgs.vimPlugins; let
      vim-godot = pkgs.vimUtils.buildVimPlugin {
        name = "vim-godot";
        src = pkgs.fetchFromGitHub {
          owner = "habamax";
          repo = "vim-godot";
          rev = "1c8385789f7f4558bdbbf6e75033b34b4727944b";
          sha256 = "sha256-uqe954fyDK5d5D+Tm/iDLXHfxFYO7BiLdQYyS4UGFMs=";
          # sha256-uqe954fyDK5d5D+Tm/iDLXHfxFYO7BiLdQYyS4UGFMs=
        };
      };
    in [
      nvim-treesitter.withAllGrammars
      telescope-nvim
      vim-godot
    ];
  };
}

For config, I took inspiration (and the bash script) from @jade 's post on supplying configs through non-nix means. symlink.nix is a very basic symlinking mechanism that allows for nix-based (copied to /nix/store, immutable) and regular filesystem (symlinked from wherever, impure) sources.

I use it this way to symlink directly from my nixos config repo checkout to ~/.config/nvim; changes take effect immediately, and are checked in / pulled along with the nixos configuration repo:

{
  services.symlink = {
    enable = true;
    entries = {
      "/home/alexou/.config/nvim" = {
        sourcePathImpure = "/home/alexou/dev/amp/nix_hosts/self/modules/config/nvim";
      };
    };
  };
}

For my servers where I don't have a full checkout of my nixos repo, I would do something similar but with sourcePath, which would copy nvim/ into /nix/store and link it to ~/.config/nvim, as home-manager.file would do:

{
  services.symlink = {
    enable = true;
    entries = {
      "/home/deploy_user/.config/nvim" = {
        sourcePath = ./nvim;
      };
    };
  };
}

Some caveats:

For neovim python, I copied what nixpkgs' programs.neovim does for python -- wrap a pythonEnv that has pynvim. But as I wasn't generating the config in nix, I have to set it manually in init.lua:

vim.g.python3_host_prog = '/run/current-system/sw/bin/nvim-python3'

Could not for the life of me figure out how to properly escape the single quotes to set it in the wrapper.

I would also not recommend using my flake and importing these modules -- they're small enough that you can just read and write your own versions. Or better yet, go read the blog posts linked above, they're better and more original.

https://blog.femtodata.com/posts/feed.xml