DEV Community

Cover image for Creating a Dockerfile for your Go Backend
Sadeedpv🥇
Sadeedpv🥇

Posted on • Edited on

Creating a Dockerfile for your Go Backend

This is a step-by-step comprehensive guide on how to create a docker image for your Go backend for absolute beginners. In this article, you will learn to build Docker image from scratch, and deploy and run your Go application. Here is the full source code of the GitHub repo

PREQUISITES

  • Install Go version 1.21.0 or later. Visit the Download Page for Go to install the toolchain.
  • Basic understanding of Golang fundamentals.
  • Docker running locally, Install Docker from here.
  • An IDE/Text-editor and a command-line terminal application.

Creating a Go Backend

In this tutorial, I will be using the Echo framework to build the backend. You can learn more about Echo here.

Feel free to create your own backend with/without frameworks.

Here is an example main.go file:

package main

import (
    "net/http"
    "os"

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

func main() {
    e := echo.New()

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })

    e.GET("/health", func(c echo.Context) error {
        return c.String(http.StatusOK, "Health is OK!!")
    })

    httpPort := os.Getenv("PORT")
    if httpPort == "" {
        httpPort = "8080"
    }

    e.Logger.Fatal(e.Start(":" + httpPort))
}

Enter fullscreen mode Exit fullscreen mode

As you can see, we have two endpoints,

  • / which returns Hello, World!
  • /health which return Health is OK!!

Don't forget to add PORT in you .env file:

PORT=8000
Enter fullscreen mode Exit fullscreen mode

You should also initialize new module in current directory using the command:

go mod init <your-module-package>
Enter fullscreen mode Exit fullscreen mode

To add the dependencies mentioned in the main.go file, use the command

go mod tidy
Enter fullscreen mode Exit fullscreen mode

Testing your application

Let’s start our application and make sure it’s running properly.

go run main.go
Enter fullscreen mode Exit fullscreen mode

Let's try out our application:

$ curl http://localhost:8080/
$ curl http://localhost:8080/health
Enter fullscreen mode Exit fullscreen mode

You should get the following output:

Hello, World!
Health is OK!!
Enter fullscreen mode Exit fullscreen mode

Creating the Dockerfile

Here is the full Dockerfile with the explanation

# syntax=docker/dockerfile:1

FROM golang:1.21.0

# Set destination for COPY
WORKDIR /app

# Download Go modules
COPY go.mod go.sum ./
RUN go mod download

# Copy the source code. Note the slash at the end, as explained in
# https://docs.docker.com/engine/reference/builder/#copy
COPY *.go ./

# Build
RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping

# Optional:
# To bind to a TCP port, runtime parameters must be supplied to the docker command.
# But we can document in the Dockerfile what ports
# the application is going to listen on by default.
# https://docs.docker.com/engine/reference/builder/#expose
EXPOSE 8080

# Run
CMD ["/docker-gs-ping"]
Enter fullscreen mode Exit fullscreen mode

Building the Docker image

After you've created the Dockerfile, now it's time to build a docker image from it with the build command.

The build command optionally takes a --tag flag. This flag is used to label the image with a string value, which is easy for humans to read and recognize. If you do not pass a --tag, Docker will use latest as the default value.

Let's build our docker image!!
Run the following command from the root directory

docker build --tag echo .
Enter fullscreen mode Exit fullscreen mode

And Voila!! We built a docker image for our backend.
To list the images, use docker image ls command or docker images command.

Run the docker image

In order to run the docker image, we can use the docker run command:

docker run <image-name>
Enter fullscreen mode Exit fullscreen mode

In my case, the command will look something like this:

docker run echo
Enter fullscreen mode Exit fullscreen mode

And Voila!! We got our server up and running; but wait, if you try to access one of your endpoints now, it will give you the following error:

Failed to connect to localhost port 8080: Connection refused

So, we have to expose port 8080 to port 8080 on the host. To do that, all you have to do is:

docker run -p 8080:8080 echo
Enter fullscreen mode Exit fullscreen mode

Now let’s rerun the curl command

$ curl http://localhost:8080/
Hello, World!
Enter fullscreen mode Exit fullscreen mode

Success! We were able to connect to the application running inside of our container on port 8080. Press Ctrl + C to stop the container.

To list all the images, you can try the command docker image ls, and you will see that our image has enormous size.

As @sibprogrammer mentioned in the comments, one common way to reduce the size is to use multi-stage build.

Thanks to @sibprogrammer for the details:

The common way of doing that is to use multi-stage build. Go is a compiled language. You don't need Go toolchain to run your services. So the general concept is the following: you build the binary in one stage and use another minimal image to launch it. It bring the huge difference. The latest "golang" image is about 800 Mb (it's based on Debian). The final image of your service can be just 5-10 Mb.
Here is an example:

FROM golang:1.15 as builder
ARG CGO_ENABLED=0
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download
COPY . .

RUN go build

FROM scratch
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
sibprogrammer profile image
Alexey Yuzhakov

The common way of doing that is to use multi-stage build. Go is a compiled language. You don't need Go toolchain to run your services. So the general concept is the following: you build the binary in one stage and use another minimal image to launch it.
Here is an example:

FROM golang:1.15 as builder

ARG CGO_ENABLED=0
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download
COPY . .

RUN go build

FROM scratch
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]
Enter fullscreen mode Exit fullscreen mode

It bring the huge difference. The latest "golang" image is about 800 Mb (it's based on Debian). The final image of your service can be just 5-10 Mb.

Collapse
 
sadeedpv profile image
Sadeedpv🥇

I was about to explain that, But I accidentally published the post instead of saving to draft; I just saw it today

Collapse
 
dyfet profile image
David Sugar

It's also pretty easy to create a multi-stage dockerfile for building an oci image that can be put into a registry.