Features Tutorials

Debugging a Go application inside a Docker container

Updated and validated on November 17, 2022. You can find more tutorials on how to use containers here. You may also refer to the Docker and Kubernetes sections of our Help documentation.

  1. Go development with Docker Containers
  2. Debugging a Go application inside a Docker container (this post)
  3. Running Go applications using Docker Compose in GoLand
  4. Using Kubernetes from GoLand

In the previous article of this series, we discussed how to get started using Docker to run our Go applications.

Today, we’ll take a look at how to debug a Go application that runs inside a container.

Creating the Dockerfile for debugging

Using the same repository that we used in the last article, we can switch our branch to the debug branch.

We made a few changes to our Dockerfile.

# Compile stage
FROM golang:1.17 AS build-env

# Build Delve
RUN go install github.com/go-delve/delve/cmd/dlv@latest

ADD . /dockerdev
WORKDIR /dockerdev

RUN go build -gcflags="all=-N -l" -o /server

# Final stage
FROM debian:buster

EXPOSE 8000 40000

WORKDIR /
COPY --from=build-env /go/bin/dlv /
COPY --from=build-env /server /

CMD ["/dlv", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/server"]

These changes add Delve, the Go debugger, to the container image and instruct the container to start the application via Delve.

We also need to compile the application with special flags to enable a better debugging experience that results from turning off optimizations from the compiler. And finally, we’ll add both Delve and our binary to the image.

Changing the Run Configuration to allow the debugger to run

Before we start the debugging session, we also need to change how the container runs. In the Bind ports field we’ll add ports 40000:40000. In the Command line options section of the Docker container Run Configuration, we need to add the following value:

--security-opt="apparmor=unconfined" --cap-add=SYS_PTRACE

This allows us to run a debugger inside the container at the cost of some security features, so it should only be used on debugging containers, but not production ones.

Pro tip: If you don’t want to alter your original Docker container or the Run Configuration used for it, then you can create a duplicate Dockerfile and a duplicate Run Configuration. Use the Copy refactoring on the Dockerfile to copy it to a new file named Dockerfile.debug.

If you’ve never used the .debug extension before, the IDE will ask you how to associate the file. In the file pattern, use Dockerfile.debug and then select the Dockerfile type in the list that appears.

Starting the debugger

To start the debugger, place breakpoints as you normally would, then create a new Go Remote Run Configuration, and the debugger will start as usual.

As you’ve probably noticed, there’s no need to add file path mapping manually to the IDE, as it will automatically know how to map the source code from the container to the host.

Note: This works as long as the binary is not stripped of debugging information.

That’s it for today. We’ve discussed creating and running Docker configurations, which allow us to debug Go applications inside containers.

In our future articles, we’ll look at how to start and debug our application using Docker Compose and Kubernetes.

Please share your feedback with us, either in the comments section below, on our issue tracker, or by tweeting to us at our Twitter account.

image description