In this tutorial, we will be setting up several containerized applications (websites) to run on a single server using an automated Nginx reverse proxy. Additionally, we will use a LetsEncrypt proxy companion to automatically provision the websites with Let’s Encrypt certificates.

Reverse Nginx Proxy

A Reverse Proxy is a service that is put in front of other web services to enable load balancing, request routing, and caching. Often it is used to allow multiple web applications to share the same host server’s ports (typically 80 and 443). This can also be done with Apache Virtual Hosts, but using an Nginx Reverse Proxy allows much better flexibility. For example, it solves a problem of running multiple websites as Docker containers on a single host.

Read more on the background of this idea here: Nginx Reverse Proxy for Docker.

Scenario Setup

We will create three projects: proxy, site1, site2. The first will implement nginx-proxy and docker-letsencrypt-nginx-proxy-companion. The latter two will be WordPress blogs.

The proxy will enable the hosting of the two WordPress blogs and will provision them with Let’s Encrypt certificates.

For simplicity, we will pack all the projects in the same repository, but in a real world scenario they can be completely independent repositories.

The repository folder structure will be this:


For this project, we will need a Linux-based server with Docker installed. You will also need at least 2 valid host names that resolve to the host server’s IP. In our example, we will be using site1.yourdomain.tld and site2.yourdomain.tld.


We will need a network that the containerized web servers can use to communicate to the reverse proxy. On the host service, create a Docker network called nginx-proxy:

Preparing the Proxy

We will follow the instructions available in the nginx-proxy project repositories nginx-proxy and docker-letsencrypt-nginx-proxy-companion.

In the proxy folder we create a docker-compose.yml file and define two services: nginx-proxy and letsencrypt-proxy.

The configuration of the first service is this:

Here, we will create a service based on the jwilder/nginx-proxy image and exposes ports 80 and 443.

Important here is the volume configuration. The /var/run/docker.sock volume allows the proxy to react to Docker events on the host server and automatically modify the Nginx reverse proxy configuration. This way, we can add containerized websites to the host without manually reconfiguring the reverse proxy.

The other three binds are required by the letsencrypt companion proxy and will be shared between the two proxy containers.

The configuration of the LetsEncrypt companion proxy is this:

Here, we use the jrcs/letsencrypt-nginx-proxy-companion to create a service that will automatically request Let’s Encrypt certificates every time we add a new containerized website to the host.

For the automation to work, the container will have to listen to Docker events on the host, which is why it uses /var/run/docker.sock:/var/run/docker.sock:ro volume.

It also shares volumes with the nginx-proxy service. This service’s container need to be identified by name via the environment variable NGINX_PROXY_CONTAINER.

The DEFAULT_EMAIL will be used by Let’s Encrypt to notify you about the certificate expiration.

The complete docker-compose.yml file for the proxy project will have this content:

As you can see, we use the nginx-proxy Docker network that we created earlier.

Preparing the Websites

Each of the two WordPress websites is implemented as a two-service project: a service for the MySQL server and service for a WordPress web application.

The content of the site1‘s docker-compose.yml file is this:

To make use of the Nginx reverse proxy and the Letsencrypt proxy companion, we only need to provide two environment variables:

  • VIRTUAL_HOST – will be used by the Nginx reverse proxy to autoconfigure the virtual host
  • LETSENCRYPT_HOST– will be used by the Letsencrypt proxy companion to request SSL certificates

Additionally, the network must be set to use the nginx-proxy Docker network.

The other site’s configuration is the same. The only difference is the names of the containers and the hostnames:

File site2/docker-compose.yml:

Test it Out

Pull the project code to the host server, change into the proxy directory and run:

docker-compose up -d

Make sure that both services are running by executing:

docker-compose ps

Start Websites

Switch to the first website directory site1 and run docker-compose up -d

Issuing the certificates will take a bit, so when running for the first time, allow about a minute before checking if the URL https://site1.yourdomain.tld works.

Repeat the same for the other website.

Further Information

If you want to customize this setup, the projects nginx-proxy and docker-letsencrypt-nginx-proxy-companion provide quite detailed documentation that will answer most questions.