What Is gopls and How to Configure It

Cli
gopls is the official Go language server for editor intelligence, configured via editor settings and verified with the gopls -v version command.

The missing piece in your editor

You open a fresh Go project. You type fmt. and press Ctrl+Space. Nothing happens. You click a function name and your editor says "Go to Definition: Not Available". You run the code and it works fine, but your editor feels completely blind. The problem is not your editor. The problem is that you are missing gopls.

gopls is the official Go language server. It sits between your editor and the Go toolchain, translating your keystrokes into intelligent suggestions, accurate diagnostics, and instant navigation. Without it, your editor is just a text box. With it, your editor becomes a compiler-aware workspace.

How the language server actually works

Modern editors do not parse code themselves. They speak a standard protocol called LSP, the Language Server Protocol. The editor handles the UI. The language server handles the brain work. gopls is Google's implementation of that brain for Go.

Think of gopls as a highly trained librarian who never sleeps. When you open a file, gopls reads it, breaks it into an abstract syntax tree, runs the type checker, and builds a map of every function, variable, and import. It caches that map in memory. When you type a dot after a variable, gopls looks up the variable's type in its cache, finds all the exported methods, and sends them back to your editor as completion items. When you hover over a function, it pulls the doc comment and signature from the same cache. The editor never sees the source code. It only sees JSON messages flying back and forth over standard input and output.

This separation is why gopls works in VS Code, Neovim, Emacs, and Sublime Text. The editor only needs to speak LSP. gopls handles all the Go-specific logic.

Your first configuration

Most editors ship with basic LSP support out of the box. You still need to tell the editor where gopls lives and which features to enable. The default settings work, but they leave performance and accuracy on the table.

Here is a minimal VS Code configuration that turns on the features that actually matter:

{
  "gopls": {
    "ui.semanticTokens": true,
    "usePlaceholders": true,
    "hints": {
      "assignVariableTypes": true,
      "parameterNames": true,
      "compositeLiteralFields": true
    }
  }
}

The ui.semanticTokens flag tells gopls to send fine-grained syntax highlighting data. Your editor uses it to color function names, type names, and variables differently. The usePlaceholders flag changes how completions look. Instead of showing fmt.Println, it shows fmt.Println($1) so you can tab through the arguments without typing them manually. The hints block adds inline type annotations and parameter names directly in the editor gutter. Go does not require type declarations on every line, but seeing them while you code prevents silent type mismatches.

Save the file and reload your editor. The next time you open a Go file, gopls will start automatically.

Configuration is a dial, not a switch. Turn on what helps you read code. Leave the rest off.

What happens when you type

Open a terminal and run gopls -v version to verify the binary is on your path. The output shows the version string and the Go version it was compiled against. Keep these two numbers in sync. A version mismatch is the most common cause of silent failures.

When you create a new file named main.go, gopls does not just read that file. It looks for a go.mod file in the current directory or any parent directory. That module file defines the workspace root. gopls treats everything under that root as a single unit. It runs the Go type checker across the entire package, not just the open file. This is why you get accurate completions for types defined in other files within the same module.

The type checker builds a dependency graph. It resolves imports, downloads missing modules if GOFLAGS or your editor triggers it, and validates that every function signature matches its calls. If you change a function signature in utils.go, gopls re-checks the package and immediately highlights every broken call site in main.go. The editor shows red squiggles. gopls sent a textDocument/publishDiagnostics LSP message containing the error range and message.

Go's convention is to keep error handling visible. gopls respects that. It will not auto-fix if err != nil blocks. It will only suggest the exact error value you need to return or wrap. The tooling stays out of your logic.

The type checker runs fast because it caches results. If you edit a file, gopls only re-checks the affected package and its direct dependents. The rest of the workspace stays warm in memory.

Trust the cache. If diagnostics disappear after a save, the cache updated correctly.

Real-world tuning

Default settings get you to 80 percent. The remaining 20 percent requires understanding how gopls interacts with your build environment.

Large projects trigger slow startup times. gopls indexes every file in the module on first load. You can speed this up by excluding vendor directories or generated code from the workspace. Add this to your editor settings:

{
  "go.useLanguageServer": true,
  "go.languageServerFlags": [
    "-rpc.trace",
    "-logfile=/tmp/gopls.log"
  ]
}

The -rpc.trace flag logs every LSP request and response. The -logfile flag writes internal gopls logs to disk. When completions lag or diagnostics vanish, check the log. You will see exactly which package failed to parse and why.

Environment variables matter. gopls inherits your shell's GOPATH, GOROOT, and GOFLAGS. If you run go build in the terminal and it works, but gopls complains about missing packages, your editor is not inheriting the correct environment. Set the go.goroot and go.gopath fields explicitly in your editor config, or launch your editor from the same terminal session where you configured your Go environment.

Go developers rely on gofmt for consistent formatting. gopls runs gofmt automatically on save. You do not need a separate formatter plugin. If you see formatting conflicts, disable third-party formatters. Let gopls handle it. The community standard is mandatory. Argue logic, not indentation.

When things go wrong

gopls fails silently more often than it panics. The editor shows a warning icon in the status bar, but you have to click it to see the message. Common failure modes include workspace detection errors, module resolution failures, and version skew.

If gopls cannot find a go.mod file, it falls back to GOPATH mode. Modern Go projects use modules. Running in GOPATH mode breaks import paths and disables accurate type checking. The editor shows undefined: package errors even when the code compiles. Fix it by running go mod init in your project root.

Version skew causes subtle bugs. If your system Go is 1.22 but gopls was built with 1.20, you will see invalid language version warnings. gopls enforces that its internal type checker matches your toolchain. Update both at the same time. Run go install golang.org/x/tools/gopls@latest to get the newest server. Run go version to check your compiler. Keep them aligned.

Cache corruption happens when you switch branches or rename packages. gopls keeps stale type information in memory. The editor shows cannot find package or undefined: type errors that do not match your disk. Clear the cache by running gopls cache clear in the terminal, then restart your editor. The server will rebuild the index from scratch.

Missing environment variables break vendor resolution. If your project uses a private module proxy, gopls needs GOPRIVATE and GOFLAGS=-mod=vendor set in the editor's environment. Without them, the server tries to fetch modules from the public proxy and fails. The log file shows module not found or proxy: 401 Unauthorized. Pass the variables through your editor's launch configuration.

The worst tooling bug is the one that never logs. Check the log file before you reinstall anything.

Pick the right tool

Go has many code navigation tools. gopls is the standard, but it is not the only option. Choose based on your workflow.

Use gopls when you want a single, maintained tool that handles completions, diagnostics, formatting, and refactoring across any LSP-compatible editor. Use godef or go-outline when you need lightweight, single-purpose navigation in a minimal editor setup and do not care about real-time diagnostics. Use a full IDE like GoLand when you want integrated debugging, database tools, and GUI-based refactoring without configuring LSP settings. Use plain go build and go vet when you prefer terminal-only workflows and want to avoid editor dependencies entirely.

Tooling should disappear. When your editor feels like an extension of your terminal, you are done configuring.

Where to go next