Using GoLand Run/Debug Configurations with Delve for Local Service Debugging
Learn how to set up a minimal GoLand run/debug configuration that leverages the Delve debugger, verify binary‑source match, and recognize when the design must change.
28 Dec 2025, 00:55 UTC

Problem
When developing a Go service locally, repeatedly launching the binary from a terminal, setting environment variables, and attaching a debugger manually creates a fragile feedback loop. Small changes in build flags or working directory can cause breakpoints to miss the intended code, wasting time.
Takeaway
A single GoLand run/debug configuration captures the exact build context, delegates to the Delve debugger, and lets you start, stop, and inspect the service with one click, while keeping the trust boundary local to your workstation.
Requirements
- GoLand version that bundles a Delve release compatible with the project’s Go SDK (e.g., Go 1.22 with Delve 1.22+).
- Access to the project’s source tree; no external agents are required.
- Ability to execute the Go build toolchain locally (the
gocommand on$PATH).
Smallest Suitable Design
The run/debug configuration is the minimal artifact that reproduces a debug session:
- Package path – the main package or test binary to launch.
- Build flags – any
-ldflags,-tags, or optimization overrides. - Environment variables – key/value pairs the service expects.
- Working directory – where relative file reads/writes resolve.
All of these are stored in the IDE’s .idea/runConfigurations XML, version‑controlled alongside the project. No shell script or manual dlv exec command is needed.
Example Configuration
For a simple HTTP server in cmd/server/main.go:
# GoLand UI: Run → Edit Configurations → + → Go Build
Name: server-debug
File path: cmd/server/main.go
Working directory: $ProjectFileDir$
Go tool arguments: -gcflags='all=-N -l' # disable optimizations
Environment variables:
PORT=8080
LOG_LEVEL=debug
When you click the Debug icon, GoLand invokes go build with the supplied flags, launches the resulting binary under Delve, and binds the debugger to the IDE’s UI.
Trust/Data Boundaries
The design assumes the debug session runs on the same host as the IDE:
- The Delve control API is exposed only on a loopback port (default
127.0.0.1:0assigned dynamically). - No authentication is required because the channel is not reachable outside the host.
- Source files are read directly from the workspace; no remote file system is involved.
If you need to debug a container or remote host, you must switch to an “Attach to process” or “Remote” configuration, which moves the trust boundary to the network and requires explicit port‑binding restrictions.
Operational Checks
To confirm the configuration is trustworthy:
- Binary‑source match – After starting the debugger, open the
Debugtool window, select theFramespane, and verify that the file path shown for the current frame corresponds to the edited source (e.g.,cmd/server/main.go:23). - Delve version compatibility – In GoLand’s
Help → Aboutdialog, note the bundled Delve version; rungo versionin a terminal and ensure the Delve release supports that Go toolchain (release notes usually state compatibility). - Optimization impact** – With the
-gcflags='all=-N -l'flag set, inspect a variable that would be optimized away (e.g., a loop counter). If the variable appears as “unavailable”, re‑run without the flag to see the difference. - Port binding** – For remote debugging, run
ss -ltnp | grep <port>on the target host and confirm the address is127.0.0.1or a firewall‑restricted interface.
Failure Modes and Design Triggers
Certain situations invalidate the assumption that a single run/debug configuration is sufficient:
- Production‑safe observability needed – If setting breakpoints or disabling optimizations could affect latency or concurrency characteristics, prefer logs, metrics, or distributed tracing instead of a debugger.
- Multi‑service debugging – When a request flows across two or more services, a compound run configuration (or orchestration tool like Docker Compose with attached debuggers) provides a unified start/stop experience.
- CI reproduction – Headless CI pipelines cannot rely on the IDE UI; a script that invokes
dlv debug --headless --listen=:2345 --log -- $PWDreproduces the same binary‑source match without GoLand. - Stale binaries – If you rebuild externally (e.g., via
make) and forget to refresh the configuration, breakpoints may bind to an old artifact. The operational check of verifying the frame’s source path catches this. - Optimized builds – Release‑level builds with inlining or register allocation can hide variables; the design must be changed to include
-gcflags='all=-N -l'or accept limited inspection.
Practical Verification
After applying a configuration, you can validate the workflow:
- Set a breakpoint on the first line of
http.HandleFunc. - Start the debugger.
- Send a request (e.g.,
curl http://localhost:8080/health). - Confirm the breakpoint hits, the
Framespane shows the expected file and line, and you can inspect therequestvariable. - Remove the breakpoint, disable the
-gcflagsflag, and repeat the request to observe that the service runs with normal optimizations (no breakpoint hits).
If any step fails, revisit the operational checks above to isolate whether the issue is binary‑source mismatch, Delve version incompatibility, or unintended optimization.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.