Configuring Nginx as a Reverse Proxy for a Go App in Docker.

Gustavo Maciel
4 min readOct 7, 2024

--

Nginx as a Reverse Proxy

If you have ever worked with Nginx, you know that one of its main use cases is to place it at the front of your application and use it as a reverse proxy to enhance security, performance, and scalability — among other benefits. In this guide, we will configure a Docker architecture where a Go application communicates with the outside world through an Nginx server, with each component running in its own container.

I’m assuming you know how to use Docker and you are also familiar with Docker Compose. Otherwise, this guide might be a bit challenging to follow, so I recommend reviewing the basics of Docker and Docker Compose before proceeding.

Project Organization

Let’s start with the project organization. For this example, I have a simple REST API written in Go with only one endpoint. I’ve simplified it since this guide focuses more on the Docker setup rather than on building APIs in Go.

nginx-go/
├── compose.yml
├── go-app/
│ ├── cmd/
│ │ └── main.go
│ └── Dockerfile
└── nginx/
├── Dockerfile
└── nginx.conf

Go App Configuration

We’ll take a look at the go-app/cmd/main.go first

package main

import (
"fmt"
"log"
"net/http"
)

func indexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Nginx server as a reverse proxy!")
}

func main() {
http.HandleFunc("/", indexHandler)
log.Fatal(http.ListenAndServe(":3000", nil))
}

Basically, it contains the configuration for starting the app and handling incoming requests properly. As mentioned, it’s a simplified version, with all the code located in the main.go file, which we would typically avoid in more complex scenarios. However, since the focus here is on Docker, we won’t go deep into the Go code.

Taking a look at the go-app/Dockerfile

FROM golang:1.21.3 AS builder

WORKDIR /app

COPY /cmd/main.go ./cmd/

RUN go mod init go-app

RUN CGO_ENABLED=0 GOOS=linux go build -o /go-app ./cmd/main.go

FROM scratch

WORKDIR /

COPY --from=builder /go-app .

EXPOSE 3000

CMD [ "/go-app" ]

The purpose of this Dockerfile is to build the Go binary to run on a lightweight image. It’s as simple as that.

Note that port 3000 in the Go container is exposed within the network, but we won’t expose it to the outside.

Nginx configuration

Here’s where things start to get more interesting. To place Nginx in front of the Go application, we need to configure the Dockerfile for Nginx (as usual) and also configure the nginx.conf file to reroute the requests received by the Nginx server to the Go application running on port 3000. Let’s look at the Dockerfile for Nginx.

nginx/Dockerfile

FROM nginx:latest

EXPOSE 80

ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

It typically builds the image and starts the server exposing port 80.

nginx/nginx.conf

server {
listen 80;

server_name localhost;

location / {
proxy_pass http://go-app:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}

The nginx.conf file is the trickiest part, as it specifies where to send the requests received on port 80. Here, we set proxy_pass to route the requests to http://go-app:3000. Since Docker containers know the names of other containers (due to built-in DNS resolution), they can communicate with each other using just the name and port (http://go-app:3000).

Docker Compose Configuration

We’re using Docker Compose to automate the process of setting up and running the containers. In this compose.yml file, we are configuring the Go app and the Nginx server to run on the same go-network, which is also defined in the compose file. In the configuration for the Nginx container, we're mapping port 8080 from our system to port 80 of the Nginx container, so we can easily access it from localhost:8080.

We also create a volume to map the nginx/ folder in our working directory to the /etc/nginx/conf.d/ folder inside the container, so our nginx.conf file can be placed inside the Nginx container.

compose.yml

version: '3'

services:

app:
build:
context: go-app
container_name: go-app
networks:
- go-network

nginx:
build:
context: nginx
container_name: nginx
volumes:
- ./nginx:/etc/nginx/conf.d/
networks:
- go-network
ports:
- "8080:80"
depends_on:
- app

networks:
go-network:
driver: bridge

Running Everything

With all that done, we can now run these containers in detached mode and check if everything went well. We will open the terminal and run the following command:

docker compose up -d

No errors occurred, so this looks promising. Now, we can open the browser and try to access localhost:8080 to see the result.

Great! We can see the response from the index endpoint that we configured. Since the Go app is running on port 3000, we know that Nginx is correctly redirecting from its own port 80, which we bound to our 8080. This means everything is working as expected.

Conclusion

I hope this guide has given you insight into how you can easily configure your Docker environment to use Nginx as a reverse proxy for a Go app. I’ve chosen to use Go as an example, but the configuration structure is almost the same for any app written in any language. So, if you want, this setup can be adapted without much hassle.

You can check the source code for this guide on Github.

--

--

Gustavo Maciel

Focused on building reliable systems with strong performance and efficiency.