# zbirenbaum/copilot.lua: a Lua Copilot client for Neovim, and when copilot.vim is the better pick

> copilot.lua replaces copilot.vim with a Lua client that talks to the Copilot Language Server. It is a good fit for Neovim 0.11+ users who want completion, a suggestion panel and optional NES, and a bad fit for anyone still on an older Neovim or a musl-based Linux system.

**zbirenbaum/copilot.lua** — Fully featured & enhanced replacement for copilot.vim complete with API for interacting with Github Copilot

- Repository: https://github.com/zbirenbaum/copilot.lua
- Stars: 4,099 · Forks: 161
- Language: Lua
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/zbirenbaum-copilot-lua

## What copilot.lua replaces, and the complaint that started it

The README states the motivation plainly: while using copilot.vim, the author's laptop began to overheat, and the large chunks of ghost text moving around the code interfered with existing cmp ghost text. copilot.lua is the response to both. It is a pure Lua client for GitHub Copilot inside Neovim, and the README calls it "the pure lua replacement for github/copilot.vim".

The audience is narrow and specific. You need Neovim 0.11.0 or higher. You need NodeJS v22 or higher only if you choose server = { type = "nodejs" }. If you are on an older Neovim, this plugin is not for you, and neither is the configuration style it assumes.

What you get for that requirement is control. Suggestions, the panel, auto-trigger behaviour, debounce timing and every keymap are Lua tables in a single setup call. That matters if your Neovim config is Lua and you want Copilot to behave like the rest of it, rather than like a Vimscript plugin bolted on. The repository also carries an API surface for interacting with Copilot, which the description calls out separately from the completion features.

## How the Copilot Language Server gets onto your machine

copilot.lua does not embed a Copilot model. It talks to the Copilot Language Server, and the README makes "binary" the default server type. On supported platforms, first use asynchronously downloads about 75 to 110 MB, depending on the pinned release and platform, and caches it under stdpath("data")/copilot.lua/lsp. Downloads are version-pinned and SHA-256 verified.

That is a more careful install path than most Neovim plugins have, and the details are worth knowing because they explain most first-run failures. A first install or cache miss needs a transport (curl, wget or PowerShell), hashing, and extraction tooling. Unix cache hits and custom paths need none of that. Windows cache hits use PowerShell to validate the cached files' ACLs. Successful installs are extracted into private staging and atomically published to a cache path keyed by version, platform and SHA-256. Interrupted staging directories are ignored and cleaned later. Concurrent first runs may duplicate a download, but they reuse the first complete published install.

The practical consequence: the first launch is not instant, and it is not offline. If you are behind a proxy that blocks curl or wget, or on a machine without an extraction tool, the server will not come up. The README does not document a manual download path for the binary, so the transport is the transport.

## Installing copilot.lua and running a first completion

The README gives a packer.nvim example. copilot-lsp is optional and only needed for NES functionality.

```lua
use { "zbirenbaum/copilot.lua"
  requires = {
    "copilotlsp-nvim/copilot-lsp", -- (optional) for NES functionality
  },
}
```

You must call require("copilot").setup(options) for Copilot to start. The README notes that the Copilot server takes some time to start up and recommends lazy loading. Its own lazy-loading example uses cmd = "Copilot" and event = "InsertEnter".

```lua
use {
  "zbirenbaum/copilot.lua",
  requires = {
    "copilotlsp-nvim/copilot-lsp",
  },
  cmd = "Copilot",
  event = "InsertEnter",
  config = function()
    require("copilot").setup({})
  end,
}
```

With no options, the defaults apply. Authentication is next: run :Copilot auth to start the permanent sign-in process, which the README recommends. After that, the default suggestion keymaps are <M-l> to accept, <M-]> and <M-[> to move between suggestions, and <C-]> to dismiss. Note that auto_trigger defaults to false, so suggestions do not appear on their own until you enable it or map toggle_auto_trigger.

To check where you stand, :Copilot auth info prints your current authentication status, and :Copilot auth signout followed by :Copilot auth signin switches accounts. Credentials live in ~/.config/github-copilot/auth.db on Linux and macOS (or $XDG_CONFIG_HOME/github-copilot/auth.db), and in ~/AppData/Local/github-copilot/auth.db on Windows.

## The panel, the suggestion engine and the defaults you will want to change

Two subsystems ship enabled by default: panel and suggestion. The panel is a buffer that lists completions, with jump_prev mapped to [[, jump_next to ]], accept to <CR>, refresh to gr and open to <M-CR>. Its layout defaults to position = "bottom" with ratio = 0.4. auto_refresh defaults to false, so the panel does not chase your edits unless you ask it to.

The suggestion engine has auto_trigger = false, hide_during_completion = true, debounce = 15 and trigger_on_accept = true. accept_word, accept_line and toggle_auto_trigger are all set to false, meaning they are unbound until you bind them. If you are coming from copilot.vim expecting inline suggestions the moment you type, this default will feel inert. That is a deliberate choice, not a bug: the README's motivation section is explicit that uncontrolled ghost text was part of the problem.

NES is a third block, and it is disabled by default. nes.enabled = false carries the note that it requires copilot-lsp as a dependency, and its accept_and_goto, accept and dismiss keymaps all default to false. If you install copilot-lsp but never set nes.enabled, nothing happens. The dependency is necessary but not sufficient.

Beyond that, should_attach decides whether a buffer gets Copilot at all. The default implementation returns false for buffers that are not buflisted and for buffers whose buftype is not empty, which keeps Copilot out of terminals and scratch buffers. root_dir defaults to walking upward for .git. Both are functions, so both are yours to replace.

## Where copilot.lua is the wrong tool

The platform matrix is the hardest boundary. Native binaries are supported on Linux x64/arm64 with glibc, macOS x64/arm64, and Windows x64/arm64. Musl Linux and other unsupported systems must use server = { type = "nodejs" }, which brings back the NodeJS v22 or higher requirement. If you are on Alpine or a musl-based container image and you also do not want a Node runtime, copilot.lua has no path for you.

Neovim 0.11.0 is a hard floor. The plugin is Lua and the setup API assumes a modern Neovim; there is no documented compatibility shim for 0.10 or earlier.

Authentication has a sharp edge. The README says tokens given by gh auth token do not support Copilot. To use a token at all you must first sign in permanently, then run :Copilot auth info to grab a token, and set GITHUB_COPILOT_TOKEN or GH_COPILOT_TOKEN. The README warns that if the variable is set, even empty, the LSP will attempt to use it to log in. An empty exported variable in a shell profile is therefore a real way to break your own setup.

Finally, if your Copilot access does not come from the public GitHub instance, you need auth_provider_url set to your provider, for example "https://mycorp.ghe.com/". The README documents the key but not what happens when it is wrong, so a misconfigured URL is a silent-ish failure mode rather than a guided one.

## copilot.lua against copilot.vim and the LSP route

The obvious alternative is github/copilot.vim, the plugin copilot.lua exists to replace. The difference in approach is not cosmetic: copilot.vim is Vimscript and brings its own client, while copilot.lua is Lua and drives the Copilot Language Server. The README's stated reasons for the split are resource use and ghost-text interference with cmp. If neither of those bothers you, copilot.vim remains a working option and does not ask for Neovim 0.11.0.

The other comparison is against using the language server directly, without this plugin. copilot.lua is a client for that server; the README credits copilot-lsp for the NES code and lists it as an optional dependency. If you already have an LSP client setup and only want the server wired in, you are choosing between configuring the server yourself and letting copilot.lua manage the download, cache, authentication and keymaps. The latter is the reason to use this plugin at all.

For configuration management, the README's lazy-loading example maps cleanly onto lazy.nvim, which is why "copilot lua lazy" and "copilot lua lazyvim" are things people search for. The event = "InsertEnter" pattern is the part that matters; the plugin manager around it is interchangeable.

## Maintenance cost, the licence and the upgrade path

The repository is not archived, and the last push was on 2026-09-19. Recent releases are v3.1.4 on 2026-09-08, v3.1.3 on 2026-09-08 and v3.1.0 on 2026-09-06, and the repository carries a CHANGELOG.md, a release-please-config.json and a .release-please-manifest.json. Release automation is in place, so version bumps are generated rather than hand-written.

For local work there is a Makefile. make lint runs stylua --check lua/ --config-path=.stylua.toml and luacheck lua/ --globals vim, matching CI. make fmt auto-fixes formatting. make test clones mini.nvim into deps/ and runs nvim --headless --noplugin -u ./tests/scripts/minimal_init.lua -c "lua MiniTest.run()". make test_file runs a single file through MiniTest.run_file with the FILE environment variable. If you fork or patch the plugin, that is the loop.

Upgrade cost is mostly the binary cache. Because downloads are version-pinned and published to a cache path keyed by version, platform and SHA-256, a new release means a new cache entry rather than an in-place patch, and a fresh 75 to 110 MB download on a cache miss. Budget for that on slow connections.

The licence is MIT. That is permissive and places few obligations on how you redistribute or modify the plugin, but it says nothing about GitHub Copilot itself, which is a separate service with its own terms. The README does not address service terms, so read those separately; nothing here is legal advice.

## Conclusion

Adopt copilot.lua if you run Neovim 0.11.0 or newer, want Copilot completion wired into Lua configuration, and are willing to run :Copilot auth once. Do not adopt it if you are on musl Linux, on an older Neovim, or want a plugin that works without the Copilot Language Server binary being downloaded and cached. Verify three things before you commit: that your platform is one of Linux x64/arm64 with glibc, macOS x64/arm64 or Windows x64/arm64; that you can reach a transport (curl, wget or PowerShell) for the first download; and that the default keymaps (<M-l> to accept, <C-]> to dismiss) do not collide with maps you already have.

## FAQ

### How do I use copilot.lua in Neovim?

Install it with your plugin manager, call require("copilot").setup(options), and run :Copilot auth to sign in. The README recommends lazy loading because the Copilot server takes time to start, and gives cmd = "Copilot" with event = "InsertEnter" as an example.

### Why is copilot.lua disabled?

The README does not document a disabled state by that name, but several defaults can look like it. suggestion.auto_trigger and panel.auto_refresh both default to false, nes.enabled defaults to false, and should_attach returns false for buffers that are not buflisted or whose buftype is not empty.

### What is the difference between copilot.lua and copilot.lsp?

copilot.lua is the Neovim plugin that drives the Copilot Language Server and manages its download, cache and authentication. copilot-lsp is a separate project credited in the README for the NES code, and it is an optional dependency that NES requires.

## Sources

- [Issues](https://github.com/zbirenbaum/copilot.lua/issues)
- [License: MIT](https://github.com/zbirenbaum/copilot.lua/blob/master/LICENSE)
- [README](https://github.com/zbirenbaum/copilot.lua/blob/master/README.md)
- [Releases](https://github.com/zbirenbaum/copilot.lua/releases)
- [zbirenbaum/copilot.lua on GitHub](https://github.com/zbirenbaum/copilot.lua)

---

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