How to configure Nginx reverse proxy with Docker?

How to set up Nginx as a reverse proxy? How to achieve it with Docker and docker-compose? Today I will show you a simple setup example.

What do we want to achieve?

One picture says more than a thousand words. Take a look at the diagram below.

Our example architecture
Our example architecture

Our sample infrastructure will consist of:

  • Nginx configured as a reverse proxy,
  • Two Nginx instances as services behind proxy.

Configuration

To begin with, we define the structure of the files and directories that will be discussed:

.

├── docker-compose.yml

# infrastructure definition

├── nginx_configs

# proxy configuration files

│   ├── default.conf │   ├── service_a.conf │   └── service_b.conf ├── proxy

# proxy HTML contents

│   └── index.html ├── service_a

# service_a HTML contents

│   └── index.html └── service_b

# service_b HTML contents

└── index.html

Infrastructure definition

To quickly set up our infrastructure, we will use the following docker-compose.yml file.

version

:

'

3.9'

services

:

proxy

:

image

:

nginx:1-alpine

volumes

:

-

./nginx_configs/:/etc/nginx/conf.d/:ro

-

./proxy/:/usr/share/nginx/html/:ro

ports

:

-

"

8080:80"

networks

:

-

internal-network

service_a

:

image

:

nginx:1-alpine

hostname

:

service_a

volumes

:

-

./service_a/:/usr/share/nginx/html/:ro

networks

:

-

internal-network

service_b

:

image

:

nginx:1-alpine

hostname

:

service_b

volumes

:

-

./service_b/:/usr/share/nginx/html/:ro

networks

:

-

internal-network

networks

:

internal-network

:

Service A and service B have to be in the same network as proxy. In our case it is a network called internal-network.

I also gave both services a hostname. Internal Docker DNS resolver will point at our services by their hostnames.

We would like to expose our proxy to the world. To achieve it I exposed proxy HTTP port.

Proxy configuration

I mounted the directory with our proxy configuration files. Let’s configure a proxy so that it can route traffic to our services.

nginx_configs/service_a.conf contents:

upstream

service_a

{

# Here we configure Nginx where to redirect the traffic

server

service_a:80

;

}

server

{

# The port on which our proxy is listening

listen

80

;

# Domain for which traffic is to be redirected

server_name

service_a.example.com

;

# Forward Host header

proxy_set_header

Host

$host

;

# We want all locations to point to service A

location

/

{

# service_a is our upstream

proxy_pass

http://service_a

;

}

}

nginx_configs/service_b.conf contents:

upstream

service_b

{

server

service_b:80

;

}

server

{

listen

80

;

server_name

service_b.example.com

;

proxy_set_header

Host

$host

;

location

/

{

proxy_pass

http://service_b

;

}

The following configuration allows the proxy to handle traffic going directly to it. If we do not define a default configuration, Nginx will direct traffic to the first service encountered in the configuration. We want to avoid this scenario.

nginx_configs/default.conf contents:

server

{

listen

80

;

location

/

{

root

/usr/share/nginx/html

;

index

index.html

index.htm

;

}

}

Services configuration

To distinguish the services from each other, we will replace their default index.html files. By default, each Nginx instance will respond with the same content, and we won’t be able to tell them apart when testing.

Create proxy/index.html file with following contents:

This is proxy service

Then service_a/index.html:

This is service A

And finally service_b/index.html:

This is service B

Testing

Now is the time to get our infrastructure up and running and test the proxy server. Run docker-compose and wait for all containers to start working.

$

docker-compose up

-d

Then make the following requests to our proxy.

$

curl

-s

http://localhost:8080 This is proxy service

$

curl

-s

-H

"Host: service_a.example.com"

http://localhost:8080 This is service A

$

curl

-s

-H

"Host: service_b.example.com"

http://localhost:8080 This is service B

Seems to be working!

Summary

As you can see, setting up a simple reverse proxy is easy. It is worth mentioning that the proxy configured in this way is not properly secured (no SSL support). However, security is not the topic of this post.

I put the whole example into the GitHub repository. You can find it here.

If you have any questions or comments, please feel free to contact me.