# 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 port 80.
  • 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.