nvim-dev-container puts Neovim inside a devcontainer
Neovim dev container support - Mirror of https://codeberg.org/esensar/nvim-dev-container
At a glance
- What is it?
- nvim-dev-container is a Neovim plugin that builds and attaches to development containers from a .devcontainer.json file, and unlike the remote container tooling in VS Code it embeds Neovim itself in the container over the client/server protocol. It is written in Lua, MIT licensed and still marked work in progress.
- Who is it for?
- nvim-dev-container suits a Neovim user who wants the VS Code devcontainer workflow without leaving the editor, since it reads the same .devcontainer.json, installs Neovim into the image and attaches the running instance to the container's filesystem and toolchain.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 165 days ago.
- What is it written in?
- Mainly Lua, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the plugin is trying to do
The stated goal of this plugin is to provide functionality similar to VS Code's remote container development plugin and other functionality that enables development in a docker container, and the README says it is inspired by jamestthompson3/nvim-remote-containers, but aims to enable having neovim embedded in docker container.
That distinction is the whole design. There is an existing Neovim plugin for remote containers, and this one takes a different route: rather than mounting the project into a container and running your local Neovim against it, it installs Neovim inside the container and attaches the editor to that copy over Neovim's own client/server protocol.
The consequence is that the toolchain inside the container is the toolchain you actually use. Your plugins, your LSP servers, your formatters, your debugger all run inside the container's environment rather than on the host, which is the entire point of a devcontainer. With a mounted-files approach, the editor stays on the host and the language server either has to run on the host or has to be reached over the network.
The project is written in Lua, is MIT licensed, and the README carries a WORK IN PROGRESS marker with an asciinema recording linked next to it. The repository is described as a mirror of the Codeberg project, and the README points at the Codeberg CI status badge, so the authoritative repository is on Codeberg with GitHub as a copy.
The project also has an unusual and useful habit. The README notes that if you do not have an account registered and do not want to register one, you can use mailing lists to report bugs, discuss the project and send patches, with separate lists for discussions and for development at lists.sr.ht.
Installing it with lazy.nvim
The installation is a plugin manager entry, and the README gives the lazy.nvim form with the treesitter dependency declared:
{
'https://codeberg.org/esensar/nvim-dev-container',
dependencies = 'nvim-treesitter/nvim-treesitter'
}And if nvim-treesitter is already available, the shorter form is enough:
{ 'https://codeberg.org/esensar/nvim-dev-container' }The source URL is on Codeberg rather than GitHub, which follows from the mirror arrangement. That matters for plugin managers that key on repository URL, and it is also a statement about where the project is hosted.
The treesitter dependency is not incidental. The requirements section asks for nvim-treesitter with an included json parser, or a manually installed json parser, because the plugin has to read and write .devcontainer.json and that file is JSON. The parser is how the plugin edits the file rather than treating it as text, which is what lets it fill in the ${localWorkspaceFolder} placeholder and produce a template for a new file.
The version requirement is also specific. The plugin asks for NeoVim version 0.12.0+, and the README is unusually candid about what happens on older versions: previous versions may be supported, but are not tested, commands and autocommands will definitely fail, and attaching will resort to terminal buffer instead of Neovim client/server mode.
That last clause is the important one. The editor in the container is not a process on your host, so the only way to drive it is through the client/server protocol, and that is what the terminal fallback is a degradation of.
Calling setup, and the options that decide the behaviour
With the defaults, the plugin needs one call:
require("devcontainer").setup{}Everything passed into setup is optional, and the README then prints the full default block, which is the best documentation the project has. Several of those options are worth reading closely because they change what the container can see.
The three mount options are the ones with the most consequence. attach_mounts has neovim_config, neovim_data and neovim_state, each with an enabled flag that defaults to false, and each with an options table. The neovim_config entry mounts local config to /root/.config/nvim in the container and defaults its options to readonly, neovim_data mounts local data to /root/.local/share/nvim, and neovim_state mounts local state to /root/.local/state/nvim, with the comment that the state mount is only useful if using neovim 0.8.0+.
All three are off by default, and the README says enabling them is useful to mount local configuration and any other mounts when attaching. So by default the container runs with its own fresh Neovim installation, and it is your choice whether to share the host's plugins and configuration. Turning on the config mount with its readonly option is the case where your host plugin set and your container's plugin set can disagree, and that is a decision to make deliberately.
There is also always_mount, which takes a list of mount strings that should always be added to every run container and is passed directly as the --mount option to the docker command.
Then there is the container runtime. container_runtime takes a string, usually either podman or docker, and devcontainer-cli is also partially supported, and if it is nil the plugin will use whatever is available, trying podman first. A separate backup_runtime is used if the main runtime does not support an action, which the README says is useful for devcontainer-cli.
The behaviours you switch on, and the ones you override
The setup block separates the knobs that change behaviour from the functions that plug into it, and the split tells you what the author considered extension points.
The behavioural switches are mostly about automation. generate_commands defaults to true and can be set to false to prevent generating default commands. The autocommands table has three entries, all false by default: init, which can automatically start containers when a devcontainer.json is available, clean, which can automatically remove any started containers and any built images when exiting vim, and update, which can automatically restart containers when the devcontainer.json file is updated.
Two more options control search and caching. disable_recursive_config_search defaults to false, and the comment explains that by default the .devcontainer.json file is searched for recursively, and that in the non-recursive case only .devcontainer.json and .devcontainer/devcontainer.json files are checked relative to the directory provided by config_search_start. And cache_images defaults to true, which the README says makes attaching to containers faster after the first time, and can be set to false to disable image caching when adding neovim.
The function options are the real extension points, and each one names the thing it feeds. config_search_start is used to find a starting point for the .devcontainer.json file search and defaults to vim.loop.cwd(). workspace_folder_provider defaults to the first workspace folder for integrated LSP if available, with vim.loop.cwd() as a fallback, and it is used to replace ${localWorkspaceFolder} in devcontainer.json and for creating the default file. terminal_handler creates a terminal in a new tab using the :terminal command and removes the statusline when that tab is active to prevent a double statusline.
Then nvim_installation_commands_provider returns a table of commands to run when adding neovim to the container, and each command can be a string or a table of command parts. It takes the binaries available on PATH in the current container and the version string. And nvim_install_as_root, which defaults to false, exists because permission errors during the install are the common case it solves.
Where it falls short
Three limits are visible in the README, and they are worth stating before anyone builds a workflow on it.
The first is the work in progress marker. The README says so plainly, and the release history backs it up: 0.1.0 was the initial release on 2022-05-19, 0.1.1 fixed minor build status update issues the same day, and 0.2.0 added a separate attach command on 2022-05-20. Three releases in two days, all in 2022, with the version number still at 0.2.0. The last push to this mirror was on 2026-05-12, so the code is being touched, but there has been no tagged release since 2022.
The second is the version floor, which I mentioned above and which deserves repeating as a limitation rather than a footnote. On Neovim below 0.12.0 the commands and autocommands will definitely fail and the attach falls back to a terminal buffer. A terminal buffer is not the same product: you lose the client/server integration that makes the container's LSP and plugins behave normally.
The third is the dependency on nvim-treesitter and a JSON parser, which is a real constraint on a plugin that a lot of Neovim users will install. If you use a Neovim distribution that has already replaced nvim-treesitter with something else, or you deliberately keep your plugin set minimal, this plugin asks you to add a parser you did not want.
There is also the gap between the plugin and the devcontainer spec. The plugin reads .devcontainer.json and works with a container runtime directly, and the README mentions devcontainer-cli as partially supported, with a backup_runtime for actions the main runtime does not support. Partially supported is the operative phrase, and the devcontainer specification is large, so the surface where the plugin and the spec disagree is where you will look when something does not work.
How it compares to the alternatives
The comparison worth making is with the two approaches it sits between, because the plugin is a deliberate hybrid of them.
The first alternative is a plain mounted directory. You start a container with your project mounted, then run your host Neovim against the mounted files. The advantage is that nothing about your editor changes and the setup is one docker command. The disadvantage is that the editor's own tooling, the language servers, the formatters and the debuggers, still run on the host against files in a container, which is exactly the mismatch the devcontainer idea exists to remove.
The second alternative is the VS Code remote containers path, which is the model the plugin explicitly says it is providing functionality similar to. The difference is that in VS Code the editor UI stays on the host and the extension host and language servers run in the container, while here the whole editor, including its UI in the sense of the terminal client, runs in the container and the host connects to it over the Neovim client/server protocol. The plugin says the inspiration was nvim-remote-containers, and the difference from that project is that nvim-remote-containers keeps the editor on the host.
So the trade is real rather than a matter of taste. Embedding the editor means your configuration, plugins and keybindings come from the container, which is a good thing when the container is the point and a surprise when you expected your host setup. The mount options exist precisely so you can pick a middle ground, and they default to off, which tells you the author's expectation is that the container should be self-contained.
The pragmatic point for an adopter is that the plugin is MIT licensed and small, in Lua, with a setup function that takes function overrides for every part of the flow that touches your environment. It is the kind of plugin where reading the default options block tells you more than the documentation does.
Editorial conclusion
nvim-dev-container suits a Neovim user who wants the VS Code devcontainer workflow without leaving the editor, since it reads the same .devcontainer.json, installs Neovim into the image and attaches the running instance to the container's filesystem and toolchain. It is the wrong tool if you need the features of VS Code's remote containers that are not editor concepts, and the wrong tool if you are on Neovim below 0.12.0, where the README warns that commands and autocommands will fail and attaching falls back to a terminal buffer instead of client/server mode. Before adopting it, read the mount options in the setup block, because the local Neovim config, data and state mounts are disabled by default and enabling them changes what the container sees.
Frequently asked questions
How do I install nvim-dev-container?
Install it with your plugin manager. With lazy.nvim the entry is { 'https://codeberg.org/esensar/nvim-dev-container', dependencies = 'nvim-treesitter/nvim-treesitter' }, or { 'https://codeberg.org/esensar/nvim-dev-container' } if nvim-treesitter is already present. The plugin needs Neovim 0.12.0 or later and a JSON parser.
What is the difference between nvim-dev-container and nvim-remote-containers?
The README says this plugin is inspired by nvim-remote-containers but aims to enable having neovim embedded in the docker container. It also notes that devcontainer-cli is only partially supported, and provides a backup_runtime option for actions the main runtime does not support.
Does nvim-dev-container mount my local Neovim config into the container?
Not by default. The attach_mounts options neovim_config, neovim_data and neovim_state are all disabled by default, and the README says they can be enabled to mount local configuration. The neovim_config mount targets /root/.config/nvim and defaults to the readonly option.
Can nvim-dev-container use podman instead of docker?
Yes. The container_runtime option takes a string, usually either podman or docker, and if it is nil the plugin uses whatever is available, trying podman first. A separate backup_runtime option is used when the main runtime does not support an action.
Which container runtimes and compose commands does nvim-dev-container support?
The container_runtime option names the runtime and the plugin also has a compose command option, which takes a string, usually either podman-compose or docker-compose, for compose operations, with devcontainer-cli also partially supported. The runtime is the command that will be invoked for container operations.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/esensar-nvim-dev-container)Was this useful?