Editor Setup for Kubernetes
Kubernetes manifests are YAML. Helm templates are YAML with Go template directives injected. A well-configured editor validates YAML against schemas, completes Kubernetes resource fields, and highlights Go template syntax. This guide sets up Neovim to handle all three.
Prerequisites
Section titled “Prerequisites”Install the language servers before configuring Neovim:
# yaml-language-server: LSP for YAML with schema validationnpm install -g yaml-language-server
# helm-ls: LSP for Helm chart templatesgo install github.com/mrjosh/helm-ls@latestConfirm both are on your $PATH:
which yaml-language-serverwhich helm-lsHow the two servers divide the work
Section titled “How the two servers divide the work”yamlls handles plain YAML files — values.yaml, Chart.yaml, raw Kubernetes manifests. It validates against Kubernetes schemas and completes resource fields.
helm_ls handles Helm template files (anything inside templates/). It parses Go template syntax ({{ .Values.foo }}), renders templates internally, and delegates YAML validation to yamlls as an embedded language server.
Neovim must route each file to the right server. Plain .yaml files get the yaml filetype and attach yamlls. Files inside templates/ get the helm filetype and attach helm_ls. Filetype detection handles the routing.
Step 1: Register the language servers
Section titled “Step 1: Register the language servers”Add both servers to your LSP server list:
-- lua/config/lsp_server_list.luaM.servers = { -- ... your other servers ... 'yamlls', 'helm_ls',}If you use Mason, both names are recognized by mason-lspconfig and install automatically.
Step 2: Add schemastore.nvim
Section titled “Step 2: Add schemastore.nvim”schemastore.nvim provides a catalog of JSON/YAML schemas: every Kubernetes resource type, Helm’s Chart.yaml, GitHub Actions workflows, docker-compose files, and hundreds more. Without it, yamlls cannot validate manifests against the Kubernetes spec.
Add it as a dependency of your LSP plugin:
-- lua/plugins/lsp.lualocal main_lsp = { "neovim/nvim-lspconfig", dependencies = { "hrsh7th/cmp-nvim-lsp", "b0o/schemastore.nvim" }, config = function() require("config/lsp").run_setup() end,}Step 3: Configure yamlls
Section titled “Step 3: Configure yamlls”yamlls ships with its own schemastore integration, but it is limited. Disable the built-in store and feed it schemas from schemastore.nvim instead. The filetypes field excludes helm so yamlls does not compete with helm_ls on template files.
-- lua/config/lsp.luavim.lsp.config('yamlls', { filetypes = { 'yaml', 'yaml.docker-compose', 'yaml.gitlab' }, settings = { yaml = { schemaStore = { enable = false, url = '', }, schemas = require('schemastore').yaml.schemas(), }, },})With this in place, opening values.yaml or any Kubernetes manifest gives you field completion, type validation, and enum checking driven by the resource’s apiVersion and kind.
Step 4: Configure helm_ls
Section titled “Step 4: Configure helm_ls”helm_ls needs the path to yaml-language-server so it can delegate YAML validation for the non-template portions of Helm files:
-- lua/config/lsp.luavim.lsp.config('helm_ls', { settings = { ['helm-ls'] = { yamlls = { path = 'yaml-language-server', }, }, },})helm_ls provides go-to-definition for .Values.x references (jumping to values.yaml), completion for built-in objects like .Release and .Chart, diagnostics from helm template rendering, and hover docs for template functions like include, toYaml, and default.
Step 5: Tree-sitter parsers
Section titled “Step 5: Tree-sitter parsers”Install the yaml and helm tree-sitter parsers. The helm parser highlights Go template directives ({{ }}) inside YAML — something the standard YAML parser cannot do.
-- lua/config/treesitter_list.luaM.servers = { 'markdown', 'sql', 'python', 'rust', 'typst', 'typescript', 'yaml', 'helm',}After adding them, restart Neovim. Parsers install on first load, or run :TSInstall yaml helm manually.
Step 6: Helm filetype detection
Section titled “Step 6: Helm filetype detection”Neovim does not detect helm as a filetype out of the box. An autocommand checks whether a YAML file lives inside a Helm chart’s templates/ directory by walking up the path until it finds a directory named templates, then confirming the parent contains a Chart.yaml.
-- lua/autocommands.lualocal helm_group = vim.api.nvim_create_augroup("Helm", { clear = true })vim.api.nvim_create_autocmd({ "BufNewFile", "BufRead" }, { pattern = "*/templates/*.yaml,*/templates/*.yml,*/templates/*.tpl", group = helm_group, callback = function(args) local path = args.file local templates_dir = vim.fn.fnamemodify(path, ':h') while vim.fn.fnamemodify(templates_dir, ':t') ~= 'templates' do local parent = vim.fn.fnamemodify(templates_dir, ':h') if parent == templates_dir then return end templates_dir = parent end local chart_dir = vim.fn.fnamemodify(templates_dir, ':h') if vim.fn.filereadable(chart_dir .. '/Chart.yaml') == 1 then vim.bo[args.buf].filetype = 'helm' end end, desc = "Detect Helm chart templates and set filetype to helm",})Plain YAML files outside Helm charts keep the yaml filetype, so yamlls handles them directly.
Verifying the setup
Section titled “Verifying the setup”Open files from a Helm chart and confirm the expected filetype and LSP client attach:
| File | Expected filetype | Expected LSP | What to check |
|---|---|---|---|
templates/deployment.yaml | helm | helm_ls | Go template highlighting, .Values completion |
values.yaml | yaml | yamlls | Field completion, schema validation |
Chart.yaml | yaml | yamlls | Schema validation for chart metadata |
Check with:
" Check filetype:set filetype?
" Check attached LSP clients:checkhealth lspTroubleshooting
Section titled “Troubleshooting”helm_ls does not attach to template files — Run :set filetype?. If it shows yaml instead of helm, the autocommand did not fire. Confirm the file path matches */templates/*.yaml and that a Chart.yaml exists in the parent of templates/.
yamlls attaches to Helm template files — The yamlls filetypes config must exclude helm. If yamlls reports errors on {{ }} syntax, it is parsing Go templates as YAML. Verify the filetypes list.
No schema validation on Kubernetes manifests — Check that schemastore.nvim is installed. Run :lua print(vim.inspect(require('schemastore').yaml.schemas())) to confirm it returns a schema table.
Tree-sitter highlighting missing for Go templates — Run :TSInstall helm and restart. The helm parser must be installed separately from yaml.