Architecture note: GoLand remote debugging with Delve for containers and hosts
GoLand remote debugging with Delve for containers and remote hosts: minimal dev-only design, trust boundaries, operational checks, failure modes and when to change approach.
23 Dec 2025, 14:01 UTC

Debugging a Go service that runs in a container or on a remote host from GoLand requires a headless Delve server next to the process and a controlled tunnel back to the IDE. The supported path is GoLand’s Go Remote run/debug configuration attaching to that server. The design is deliberately minimal and dev-only because a reachable Delve endpoint is equivalent to full process control.
Requirements
The binary must be built for debugging. Compile with go build -gcflags='all=-N -l' so the compiler disables optimizations and inlining. Without -N -l breakpoints can be skipped and variables appear optimized out.
The Delve build used on the target must support the Go toolchain that produced the binary. Delve typically lags new Go releases, so a newer toolchain than Delve understands can cause attach failures or misread runtime structures.
Smallest suitable design
Three parts are enough: target, tunnel, IDE.
Target. Run the service under a headless Delve listening on a single private port, commonly 2345. Two common forms are dlv exec as PID 1, or dlv attach to a running PID. In a dev-only image:
dlv listen 127.0.0.1:2345 --headless=true --api-version=2 exec /app/myapp -- <args>Run this command inside the container or on the remote host as the user that can start the service. Required permissions include the ability to execute dlv and, for attach, SYS_PTRACE capability or equivalent ptrace allowance.
Tunnel. Do not publish the debug port to the network. Expose it only via an authenticated tunnel from the target to the developer workstation, for example SSH local port forwarding or kubectl port-forward. The tunnel maps the remote 127.0.0.1:2345 to 127.0.0.1:2345 on the workstation.
IDE. In GoLand create a Go Remote run/debug configuration pointing at host 127.0.0.1 port 2345. The configuration dialog shows the exact dlv command to launch, including the API version flag. Use that command as the source of truth for your environment.
Trust and data boundaries
A reachable Delve endpoint allows arbitrary expression evaluation, memory read and write, and goroutine control. Treat the session as a privileged actor.
- Bind Delve to loopback or a private interface only, e.g. 127.0.0.1:2345, not 0.0.0.0.
- Expose the port only through a controlled tunnel with authentication, never via a Kubernetes Service, LoadBalancer, NodePort or public Ingress.
- Assume the debug session can read secrets present in process memory and change program state. Limit access to the developer who owns the tunnel and the specific deployment.
Operational checks
Before attaching, verify the setup rather than assuming it works.
- Confirm the deployed binary was built with -N -l by checking the build definition or Dockerfile used for the dev image, not by inspecting the binary alone.
- Confirm the Delve port is reachable only from the intended workstation via the tunnel and not from other hosts in the cluster.
- Verify breakpoints bind after attach in GoLand. Unbound breakpoints indicate a build flag or symbol mismatch.
- Confirm Go and Delve versions are compatible by running go version and dlv version in the target environment and comparing with Delve release notes for supported Go versions.
- Gate CI/CD so images containing delve or an exposed debug port cannot be promoted to production.
Failure modes
Optimized binaries cause silently skipped breakpoints and optimized out variables. The IDE shows the breakpoint as set but it is never hit.
Toolchain mismatch causes attach errors or corrupted stack inspection when Delve is older than the Go runtime.
Containers using dlv attach may fail with permission denied if the runtime applies default seccomp profiles or drops SYS_PTRACE. dlv exec avoids ptrace but still requires the container to allow execution of dlv.
Port conflicts or stale delve processes produce confusing attach errors. A previous headless server still bound to 2345 will reject a new attach.
Debug builds inflate binary size and distort performance. Do not use -N -l builds for load tests, benchmarks or capacity planning.
Conditions that would change the design
Production debugging should be prohibited. Prefer logs, metrics, pprof profiles or core dump analysis instead of a live Delve endpoint.
In shared clusters with multiple developers, use per-developer namespaces or ephemeral debug sidecars rather than a shared debug port.
If remote attach latency makes stepping unusable, mirror the environment locally and use GoLand’s local debugger instead of remote.
Menu labels, bundled Delve versions and the exact command shown in the Go Remote dialog vary across GoLand releases. Confirm against your installed version.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.