- Overview
- Initial setup
- Prerequisites
- Step 0 - Create DNS records
- Step 1 - Edit domain names and emails in the configuration
- Step 2 - Configure Nginx virtual hosts
- Step 3 - Create named Docker volumes for dummy and Let's Encrypt TLS certificates
- Step 4 - Build images and start containers using staging Let's Encrypt server
- Step 5 - verify HTTPS works with the staging certificates
- Step 6 - Switch to production Let's Encrypt server
- Step 7 - verify HTTPS works with the production certificates
- Reloading Nginx configuration without downtime
This example automatically obtains and renews Let's Encrypt TLS certificates and sets up HTTPS in Nginx for multiple domain names using Docker Compose.
You can set up HTTPS in Nginx with Let's Encrypt TLS certificates for your domain names and get an A+ rating in SSL Labs SSL Server Test by changing a few configuration parameters of this example.
Let's Encrypt is a certificate authority that provides free X.509 certificates for TLS encryption. The certificates are valid for 90 days and can be renewed. Both initial creation and renewal can be automated using Certbot.
When using Kubernetes Let's Encrypt TLS certificates can be easily obtained and installed using Cert Manager. For simple websites and applications, Kubernetes is too much overhead and Docker Compose is more suitable. But for Docker Compose there is no such popular and robust tool for TLS certificate management.
The example supports separate TLS certificates for multiple domain names, e.g. example.com, anotherdomain.net etc.
For simplicity this example deals with the following domain names:
cms.onlinesale.techsite.onlinesale.tech
The idea is simple. There are 3 containers:
- Nginx
- Certbot - for obtaining and renewing certificates
- Cron - for triggering certificates renewal once a day
The sequence of actions:
- Nginx generates self-signed "dummy" certificates to pass ACME challenge for obtaining Let's Encrypt certificates
- Certbot waits for Nginx to become ready and obtains certificates
- Cron triggers Certbot to try to renew certificates and Nginx to reload configuration daily
- Docker and Docker Compose are installed
- You have a domain name
- You have a server with a publicly routable IP address
- You have cloned this repository (or created and cloned a fork):
git clone https://github.com/peterliapin/onlinesales-nginx.git
For all domain names create DNS A records to point to a server where Docker containers will be running.
DNS records
| Type | Hostname | Value |
|---|---|---|
| A | cms.onlinesale.tech |
directs to IP address X.X.X.X |
| A | site.onlinesale.tech |
directs to IP address X.X.X.X |
Copy the contents of config.env.sample to config.env and specify your domain names, contact emails and targets for these domains with space as delimiter in the config.env:
DOMAINS="cms.onlinesale.tech site.onlinesale.tech"
TARGETS="http://cms_onlinesale_tech:80 /var/www/html/site.onlinesale.tech"
CERTBOT_EMAILS="support@onlinesale.tech support@onlinesale.tech"For two and more domains separated by space use double quotes (") around the DOMAINS and CERTBOT_EMAILS variables.
For a single domain double quotes can be omitted:
DOMAINS=cms.onlinesale.tech
TARGETS=http://cms_onlinesale_tech:80
CERTBOT_EMAILS=support@onlinesale.techFor each domain you need to configure a target value to redirect incoming traffic to a service which runs on a local port inside a host PC, remote host or as a docker compose service inside the same docker network:
http://cms_onlinesale_tech:80- means all traffic will be redirected to the cms_onlinesale_tech docker compose service (port 80) which is deployed in the same docker compose networkhttp://localhost:80- means all traffic will be redirected to a local service running on port 80 on the a host PChttp://localhost:80- means all traffic will be redirected to a local service running on port 80 on the a host PC/var/www/html/site.onlinesale.tech- means that nginx will serve static content from /var/www/html/site.onlinesale.tech folder which should be mounted to the nginx service using an external volume
When you specify local path as a target, make sure html/my-domain directory (relative to the repository root) exists and countains the desired content and html directory is mounted as /var/www/html in docker-compose.yml:
services:
nginx:
#...
volumes:
#...
- ./html:/var/www/htmlWhen you specify a docker compose service or local or remote service like http://my-backend:8080/ as a target, the nginx will automatically configure itselves using the following configuration template:
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://my-backend:8080/;
}
my-backend is the service name of your backend application in docker-compose.yml:
services:
my-backend:
image: example.com/my-backend:1.0.0
#...
ports:
- "8080"Step 3 - Create named Docker volumes for dummy and Let's Encrypt TLS certificates, cert bot acme challanges, logs and static sites:
docker volume create --name=nginx_conf
docker volume create --name=letsencrypt_certs
docker volume create --name=certbot_acme_challenge
docker volume create --name=letsencrypt_logs
docker volume create --name=static_sitesdocker compose up -d --build
docker compose logs -fYou can alternatively use the docker-compose binary.
For each domain wait for the following log messages:
Switching Nginx to use Let's Encrypt certificate
Reloading Nginx configuration
For each domain open in browser https://${domain} and verify that staging Let's Encrypt certificates are working:
Certificates issued by (STAGING) Let's Encrypt are considered not secure by browsers.
Stop the containers:
docker compose downConfigure to use production Let's Encrypt server in config.env:
CERTBOT_TEST_CERT=0Re-create the volume for Let's Encrypt certificates:
docker volume rm letsencrypt_certs
docker volume create --name=letsencrypt_certsStart the containers:
docker compose up -d
docker compose logs -fFor each domain open in browser https://${domain} and https://www.${domain} and verify that production Let's Encrypt certificates are working.
Certificates issued by Let's Encrypt are considered secure by browsers.
Optionally check your domains with SSL Labs SSL Server Test and review the SSL Reports.
Do a hot reload of the Nginx configuration:
docker compose exec --no-TTY nginx nginx -s reload