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.
Mục Lục
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 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.