# Advanced configuration
In this section, we will look at setting up a reverse proxy that will serve traffic for multiple applications running on the same host, as well as providing automatic Let's Encrypt certificates for each application.
A reverse proxy is an application that receives traffic, in this case web traffic, from clients and forwards the requests to the corresponding applications based on the requested domain name.
# Nginx reverse proxy
One popular solution for reverse proxying is nginx. We will setup a nginx proxy that will listen for incoming
connections on a port of our choice on localhost. For this example, I will pick port 8080
.
Now let's assume that we have a couple of websites that we will host, for example:
blog.local
todo.local
Each project has its own docker-compose.yml
file with a nginx service listening on port 8080.
Using a reverse proxy, we will forward any traffic that goes to http://blog.local:8080
to the blog
app,
and any traffic going to http://todo.local:8080
to the todo
app.
# Setup
We will be using a popular open source nginx proxy image from https://github.com/jwilder/nginx-proxy.
Since the nginx proxy will be serving the traffic for multiple applications, we will store it in a separate folder
outside of our applications folders. In your ~/Sites/
folder create a new folder called nginx-proxy
,
so we end up with ~/Sites/nginx-proxy/
.
Next, create a new file ~/Sites/nginx-proxy/docker-compose.yml
with the following content:
version: '3'
services:
nginx:
image: nginx:1.19-alpine
container_name: reverse-proxy
restart: always
ports:
- "8080:80"
volumes:
- nginx-proxy:/etc/nginx/conf.d/
networks:
- reverse-proxy
dockergen:
image: jwilder/docker-gen:0.7.3
depends_on:
- nginx
command: -notify-sighup reverse-proxy -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- nginx-proxy:/etc/nginx/conf.d/
- ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
- ./certs:/etc/nginx/certs:ro
networks:
- reverse-proxy
volumes:
nginx-proxy:
networks:
reverse-proxy:
external: true
NOTE
We've created a new network called reverse-proxy
that will be used to communicate between apps.
# Start the proxy
First we'll need to manually create the new network (this only has to be done once):
docker network create reverse-proxy
Now, let's start it up
docker-compose up
# App configuration
Next step is to set our apps up on the new reverse-proxy
network we've just created. To do that
we will modify our app docker-compose.yml
file, in our case ~/Sites/todo/docker-compose.yml
.
Add this at the end of the docker-compose.yml
file
... Other configuration above ...
networks:
reverse-proxy:
external: true
Now we will need to tell our nginx
service to use this new network and to expose port 80
// Find your nginx service that looks something like this
nginx:
image: nginx:1.19-alpine
depends_on:
- php
- node
// Remove this ports property as we no longer
// listen on port 8080 on the host
ports:
- "8080:80"
// Add this new expose
expose:
- 80
// Add this new environment variable
// this is the url we want to access
// this server on http://todo.local:8080
environment:
VIRTUAL_HOST: todo.local
// Add this new network property
networks:
- reverse-proxy
- default
So let's summarise what's going on here.
- We are deleting the ports property as we no longer want this nginx to bind to my localhost on its own
- We are adding an
expose
property that allows the nginx proxy to connect to this nginx serive on port80
. - We are telling the proxy to route any request for
todo.local
to this local app using the environment variable. - We are putting this service on an external network called
reverse-proxy
so that the reverse proxy has access to this container.
# Test the app
Now go ahead and bring this application up
docker-compose stop
docker-compose up
Open your browser and go to http://todo.local:8080
. If everything is okay, your application homepage should load.
# Let's Encrypt
It is a good practice for any application in production to serve traffic over HTTPS. This can be a tedious process, especially when setting up a new application. In this section, I will show you how you can secure your applications with Let's Encrypt certificates that are generated when your containers start up, and are automatically renewed before they expire.
Let's update the docker-compose.yml
file of the nginx proxy above, with the following contents:
version: '3'
services:
nginx:
image: nginx:1.19-alpine
container_name: reverse-proxy
restart: always
ports:
- "80:80"
- "443:443"
labels:
com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: true
volumes:
- nginx-proxy:/etc/nginx/conf.d/
- ./vhost.d/:/etc/nginx/vhost.d/
- nginx-html:/usr/share/nginx/html
- /etc/letsencrypt/docker:/etc/nginx/certs:ro
networks:
- reverse-proxy
dockergen:
image: jwilder/docker-gen:0.7.3
restart: always
depends_on:
- nginx
command: -notify-sighup reverse-proxy -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
labels:
com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen: true
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- nginx-proxy:/etc/nginx/conf.d/
- ./vhost.d/:/etc/nginx/vhost.d/
- nginx-html:/usr/share/nginx/html
- ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl
- /etc/letsencrypt/docker:/etc/nginx/certs:ro
networks:
- reverse-proxy
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion:v1.12
restart: always
depends_on:
- nginx
volumes:
- nginx-proxy:/etc/nginx/conf.d/
- ./vhost.d/:/etc/nginx/vhost.d/
- nginx-html:/usr/share/nginx/html
- /etc/letsencrypt/docker:/etc/nginx/certs:rw
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- reverse-proxy
volumes:
nginx-proxy:
nginx-html:
networks:
reverse-proxy:
external: true
You will also need to add a vhost.d
folder and a generic template for nginx
configuration. A full example of the
nginx proxy can be found here.
Start the container up
docker-compose up
WARNING
The domain names used to generate SSL certificates for, need to be pointing to the IP Address of the server hosting the nginx proxy, otherwise the verification will fail. This is because Let's Encrypt will attempt to create and access a challenge file under the given domain name.
# Summary
We are running a single nginx
proxy that binds to port 8080 on your local machine. This then forwards all the requests,
behind the scenes, using docker's network to any number of apps that are exposed using a VIRTUAL_HOST
environment variable.