Features Tutorials

Debugging a Go application inside a Docker container

Updated and validated on January 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

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.

Comments below can no longer be edited.

4 Responses to Debugging a Go application inside a Docker container

  1. Avatar

    Spencer K says:

    May 9, 2020

    Have you been able to step into Go Modules that are dependencies of a project? I can only set breakpoints for my local project files and the standard library. When I hover over the breakpoint in a module outside the project, it’s disabled and pops up an error saying “executable doesn’t contain debug information” with the file path to my local computer’s $GOPATH. When I look at the delve logs, it’s requesting my local path as well (on a mac `/Users//go`. On the remote image, the $GOPATH is `/go`.

    If I soft link my local path to `/go` (ie. `ln -s /Users//go /go`) and set my $GOPATH to `/go` it all works without an issue. I would figure with modules this would’ve been a non-issue a while ago.

    Am I missing something?

    • Avatar

      Florin Pățan says:

      May 11, 2020

      Hi Spencer,

      Turns out that we have a problem in the automatic path matching for Go Modules based projects, which is why this doesn’t seem to work.
      Please watch/vote for the issue https://youtrack.jetbrains.com/issue/GO-9308 to receive an update once we’ll fix this.

      Sorry for the inconvenience.

  2. Avatar

    Daniel says:

    May 10, 2020

    Hi ! this is awesome! however I’ve been trying to follow the steps with an own project (I followed steps from previous post and worked fine)

    Console returns the error:

    API server listening at: [::]:40000
    could not launch process: not an executable file

  3. Avatar

    Daniel says:

    May 10, 2020

    Forget My previous comment! I had a bad typo in the executable name which caused the issue.

Discover more