As a Go developer, the inability to live reload your applications can be frustrating. Unlike many other languages and frameworks, we have to manually rebuild and rerun to see our changes. This undoubtedly has an impact on productivity and needless to say is a tedious process. However, fear not! In this blog post, I will clear the air on live reloading in Go and demonstrate how it can be achieved with CompileDaemon and Docker Compose.
Table Of Contents
What Is Live Reloading?
Live reloading is the process of automatically rebuilding and restarting the entire application after a change to the source code. Live reloading and hot reloading are often used interchangeably and although they share some similarities, they are not the same.
Hot reloading only updates the modified parts of the application. It injects the updated code directly into the running application, preserving the application state and avoiding the need for a complete rebuild or restart.
Go is a compiled language, meaning the code needs to be compiled into machine code before it can be executed. This compilation process enables Go to achieve impressive speed and efficiency. However, this also increases the complexity of supporting hot reloading. At the time of this post, Go does not support any prospect of hot reloading, but we can still achieve live reloading.
What Is CompileDaemon?
CompileDaemon is a small utility which monitors your Go source code and automatically rebuilds your application whenever a change is detected. If you would like to see all the features of CompileDaemon, check the documentation.
Docker allows us to encapsulate applications and their dependencies into a container. This ensures consistent behaviour across different machines. Luckily for us, CompileDaemon and Docker make a great pairing.
We can containerise the application in Docker Compose, mount the source code to a volume and CompileDaemon can monitor the volume for changes. This helps us achieve a highly efficient and convenient live reloading setup.
Example Implementation
In this example, we will construct a simple Gin API which will live reload within a Docker container using CompileDaemon and Docker Compose. Before we begin, ensure you have Docker installed and running.
Create a new directory called live-reloading-example
and initialise a new go module.
go mod init
BashAdd Gin as a dependency.
go get -u github.com/gin-gonic/gin
BashCreating An API With Gin
Create a main.go
file which will start a Gin server containing a /hello
endpoint which returns a hardcoded JSON response.
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/hello", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "hello"})
})
r.Run(":8000")
}
GoBuild and run the application.
go build && ./live-reloading-example
BashOpen your browser and navigate to the URL below.
Ensure you are getting the expected response.
{"message": "hello"}
JSONCreating The Dockerfile
Let’s put our app in a container by creating a new Dockerfile
.
# Go Version
FROM golang:1.21-rc-alpine3.17
# Environment variables which CompileDaemon requires to run
ENV PROJECT_DIR=/app \
GO111MODULE=on \
CGO_ENABLED=0
# Basic setup of the container
RUN mkdir /app
COPY .. /app
WORKDIR /app
# Get CompileDaemon
RUN go get github.com/githubnemo/CompileDaemon
RUN go install github.com/githubnemo/CompileDaemon
# The build flag sets how to build after a change has been detected in the source code
# The command flag sets how to run the app after it has been built
ENTRYPOINT CompileDaemon -build="go build -o api" -command="./api"
To check everything is working so far, we will build a Docker image from the Dockerfile.
docker build -t live-reloading-example .
BashRun the Docker image.
docker run -p 8000:8000 live-reloading-example
BashCheck in the browser that you get the same response as before.
You will notice that the application will not live reload yet. This is because a Dockerfile is not supposed to be dynamic. They are used to build images which are a static snapshot of an application and its dependencies. To achieve live reloading we need to leverage Docker Compose.
Finishing Up With Docker Compose
Create a compose.yaml file in the project root with the following code.
version: '3.8'
name: live-reload-example
services:
api:
container_name: api
build:
context: .
dockerfile: Dockerfile
ports:
- 8000:8000
volumes:
- ./:/app
YAMLYou may have noticed that we are mounting our app folder to a volume. CompileDaemon will monitor this volume for changes and trigger the build and run commands which were set in the Dockerfile.
Start Docker Compose.
docker-compose up
BashOpen the browser and make sure that you still get the message.
{"message": "hello"}
JSONIn the main.go
file, change the message from hello
to goodbye
and save the file. You will see the project rebuild in the logs. Refresh the browser and you will see the message has changed to goodbye
.
With this setup in place, you can now make changes to your source code and CompileDaemon will automatically rebuild and restart your application.
If you would like to download the example code, it is available on our GitHub.
Conclusion
In conclusion, Go may not provide native support for live reloading, but that doesn’t mean we don’t have an effective solution. By using tools like CompileDaemon and Docker Compose, we can achieve live reloading and eliminate those annoying manual rebuilds.
Although live reloading isn’t useful in every scenario, you should make it an option in your Makefile as it can increase productivity when developing new features.
If you have any questions or comments, please leave them below. Also, don’t forget to subscribe to our newsletter to receive more informative posts like this directly to your inbox.
If you found this article helpful, please share it with your friends and colleagues.
Thank you for reading!
Leave a Reply