For the purpose of this article, we shall consider the following Gin webserver written in Go that responds with { "message": "pong" } for a GET /ping request.

// main.go
package main

import "github.com/gin-gonic/gin"

func setupRouter() *gin.Engine {
  r := gin.Default()

  r.GET("/ping", func(c *gin.Context) {
    c.JSON(200, gin.H{
      "message": "pong",
    })
  })

  return r
}

func main() {
  r := setupRouter()
  r.Run()
}

Setting up Docker

We shall continue by creating a Docker image and a docker-compose file to build and start the Gin webserver.

# Dockerfile
FROM golang:latest

RUN mkdir /app
WORKDIR /app
ADD . /app

RUN go get github.com/gin-gonic/gin
RUN go build main.go
CMD ["./main"]
# docker-compose
---
version: "3.7"
services:
  app:
    build: .
    image: hot-reloading-app
    ports:
      - "8080:8080"
    volumes:
      - ./:/app
    environment:
      PORT: "8080"

The webserver can be started as follows:

$ docker-compose up
Starting app_1 ... done
Attaching to app_1
app_1  | 2020/02/15 08:20:13 Running build command!
app_1  | 2020/02/15 08:20:13 Build ok.
...
app_1  | 2020/02/15 08:20:13 stdout: [GIN-debug] Listening and serving HTTP on :8080

And by sending a GET /ping request to the webserver we are expecting to receive a "pong".

$ curl http://localhost:8080/ping
{"message":"pong"}

However, after changing the response string to the GET /ping request you will observe that the actual response did not change. This is expected and, because the Docker image itself did not change, it is still running the most up-to-date binary that does not include the change made to the response. This effectively means that the image has to be stopped and rebuilt so that the changes made to the webserver are applied.

Setting up Docker with hot reloading

Having to manually rebuild the Docker image every time a change is made to the source code is simply not an option. CompileDaemon, as the name implies, is a daemon for Go that watches all .go files in a given directory and invokes go build when a modification is detected.

We shall proceed by changing the Dockerfile to install the CompileDaemon package and use it to build and start the webserver.

# Dockerfile
FROM golang:latest

RUN mkdir /app
WORKDIR /app
ADD . /app

RUN go get github.com/githubnemo/CompileDaemon
RUN go get github.com/gin-gonic/gin

ENTRYPOINT CompileDaemon --build="go build main.go" --command=./main

The next step is to rebuild the Docker image and start the server using docker-compose up. Go ahead and verify that the webserver is running as described in the first paragraph. Now, modify the response to some other string and observe the Docker image logs:

...
api_1  | 2020/02/15 07:24:46 Build ok.
api_1  | 2020/02/15 07:24:46 Hard stopping the current process..
api_1  | 2020/02/15 07:24:46 Restarting the given command.
...
api_1  | 2020/02/15 07:24:46 stdout: [GIN-debug] Listening and serving HTTP on :8080
api_1  | 2020/02/15 07:24:50 stdout: [GIN] 2020/02/15 - 07:24:50 | 200 |       1.015ms |      172.21.0.1 | GET      /ping

Finally, by sending a GET /ping request to the webserver we are expecting not to receive a "pong" but rather the updated string.

$ curl http://localhost:8080/ping
{"message":"pong pong pong!"}

We have now successfully set up Gin webserver in Docker that automatically reloads changes when files are changed. 🙌

You can grab the final source code from here: https://gist.github.com/rpereira/c52190d18bc41f2ec016c3f15b059e5f