Host multiple websites with HTTPS on a single server

Setup a reverse-proxy, and, for each website running inside a Docker container, create an automatic nginx configuration and a SSL certificate.

Hosting multiple websites on a single Virtual Private Server is simple and efficient. Nowadays, HTTPS is a required feature for any website.

  • Create and renew SSL certificates to enable HTTPS on each website.
  • The need to restart nginx after each config modification, causing a short downtime for every websites.
  • The need to expose a port of each container to the host, and therefore keep track of the used ports (two containers can not use the same port).

Project structure

Create a nginx-proxy directory next to the websites directories. In my setup this is in /srv/www/ on the host.

.
+-- nginx-proxy
| +-- docker-compose.yml
| +-- nginx.tmpl
| +-- conf.d
| +-- vhost.d
| +-- html
| +-- certs
+-- your-website-one.tld
+-- your-website-two.tld
+-- your-website-three.tld

docker-compose.yml

Inside /nginx-proxy/, create a docker-compose.yml file with this content:

version: '3'
services:
nginx:
image: nginx
labels:
com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true"
container_name: nginx
restart: unless-stopped
logging:
options:
max-size: "10m"
max-file: "3"
ports:
- "80:80"
- "443:443"
volumes:
- /srv/www/nginx-proxy/conf.d:/etc/nginx/conf.d
- /srv/www/nginx-proxy/vhost.d:/etc/nginx/vhost.d
- /srv/www/nginx-proxy/html:/usr/share/nginx/html
- /srv/www/nginx-proxy/certs:/etc/nginx/certs:ro

nginx-gen:
image: jwilder/docker-gen
command: -notify-sighup nginx -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
container_name: nginx-gen
restart: unless-stopped
volumes:
- /srv/www/nginx-proxy/conf.d:/etc/nginx/conf.d
- /srv/www/nginx-proxy/vhost.d:/etc/nginx/vhost.d
- /srv/www/nginx-proxy/html:/usr/share/nginx/html
- /srv/www/nginx-proxy/certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
- /srv/www/nginx-proxy/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro

nginx-letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: nginx-letsencrypt
restart: unless-stopped
volumes:
- /srv/www/nginx-proxy/conf.d:/etc/nginx/conf.d
- /srv/www/nginx-proxy/vhost.d:/etc/nginx/vhost.d
- /srv/www/nginx-proxy/html:/usr/share/nginx/html
- /srv/www/nginx-proxy/certs:/etc/nginx/certs:rw
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
NGINX_DOCKER_GEN_CONTAINER: "nginx-gen"
NGINX_PROXY_CONTAINER: "nginx"

networks:
default:
external:
name: nginx-proxy
  • nginx-gen: uses the jwilder/docker-gen image. Its command instruction will render a nginx configuration (based on nginx.tmpl) for each website / container added to the network.
  • nginx-letsencrypt: generates and renew the HTTPS certificates.

nginx.tmpl

Inside /nginx-proxy/, create a nginx.tmpl file and copy the content from this file. This is the used by the nginx-gen container to create the nginx configuration file for each website / container added to the network.

Boot up

First create the network:

$ docker network create nginx-proxy
$ cd /srv/www/nginx-proxy/
$ docker-compose up -d

Link a website to the reverse-proxy

To link a website to the running nginx-proxy, we need to update its own docker-compose.yml file (not the one from nginx-proxy above) with a few instructions:

services:
my-app:

environment:
VIRTUAL_HOST: your-website.tld
VIRTUAL_PORT: 3000
LETSENCRYPT_HOST: your-website.tld
LETSENCRYPT_EMAIL: your-email@domain.tld
  • VIRTUAL_PORT: (opt.) the port your website is listening to (default to 80).
  • LETSENCRYPT_HOST: your domain name, used in the Let’s Encrypt configuration.
  • LETSENCRYPT_EMAIL: your email, used in the Let’s Encrypt configuration.
services:
my-app:

expose:
- 3000
networks:
default:
external:
name: nginx-proxy
$ cd /srv/www/your-website.tld
$ docker-compose up -d

Next Step

How can we replicate this production environment, on a local dev computer? I wrote a follow up article about this: set a local web development environment with custom Urls and HTTPS.

Useful links

Freelance developer / designer → http://francoisromain.com