![](https://thana.in.th/wp-content/uploads/2020/05/Screen-Shot-2562-10-16-at-02.08.46.png)
Install Docker on Ubuntu
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
Update the Ubuntu package database
sudo apt-get update
install Docker
sudo apt-get install -y docker-ce
Install Docker Compose on Ubuntu
sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
A Better Solution: Run Let’s Encypt’s Certbot in a Docker Container
On your server, create a new Directory:
sudo mkdir -p /docker/letsencrypt-docker-nginx/src/letsencrypt/letsencrypt-site
Then, create a new docker-compose.yml file
sudo nano /docker/letsencrypt-docker-nginx/src/letsencrypt/docker-compose.yml
docker-compose.yml
version: '3.1'
services:
letsencrypt-nginx-container:
container_name: 'letsencrypt-nginx-container'
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./letsencrypt-site:/usr/share/nginx/html
networks:
- docker-network
networks:
docker-network:
driver: bridge
Then, create a configuration file for nginx
sudo nano /docker/letsencrypt-docker-nginx/src/letsencrypt/nginx.conf
nginx.conf
server {
listen 80;
listen [::]:80;
server_name xxx.com www.xxx.com;
location ~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
root /usr/share/nginx/html;
index index.html;
}
Next, create an index.html file
sudo nano /docker/letsencrypt-docker-nginx/src/letsencrypt/letsencrypt-site/index.html
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Let's Encrypt First Time Cert Issue Site</title>
</head>
<body>
<h1>Hello!</h1>
<p>
This is the temporary site that will only be used for the very first time SSL certificates are issued by Let's Encrypt's
certbot.
</p>
</body>
</html>
Before running the Certbot command, spin up a Nginx container in Docker to ensure the temporary Nginx site is up and running
cd /docker/letsencrypt-docker-nginx/src/letsencrypt
sudo docker-compose up -d
Then, open up a browser and visit the domain to ensure that the Docker container is up and running and accessible. As stated earlier, it’s not necessary to have a default index.html page for this container, but it makes testing the container a lot easier, so I always create one.
![](https://thana.in.th/wp-content/uploads/2019/10/the-site-running-in-the-nginx-docker-container-for-generating-the-first-lets-encrypt-certificate.png)
Run the staging command for issuing a new certificate:
sudo docker run -it --rm \
-v /docker-volumes/etc/letsencrypt:/etc/letsencrypt \
-v /docker-volumes/var/lib/letsencrypt:/var/lib/letsencrypt \
-v /docker/letsencrypt-docker-nginx/src/letsencrypt/letsencrypt-site:/data/letsencrypt \
-v "/docker-volumes/var/log/letsencrypt:/var/log/letsencrypt" \
certbot/certbot \
certonly --webroot \
--register-unsafely-without-email --agree-tos \
--webroot-path=/data/letsencrypt \
--staging \
-d xxx.com -d www.xxx.com
After executing the above command, you should get the following output which should indicate everything ran successfully.
![](https://thana.in.th/wp-content/uploads/2019/10/issue-a-new-lets-encrypt-certificate-with-certbot-and-docker-in-staging-mode.png)
You can also get some additional information about certificates for your domain by running the Certbot certificates command:
sudo docker run --rm -it --name certbot \
-v /docker-volumes/etc/letsencrypt:/etc/letsencrypt \
-v /docker-volumes/var/lib/letsencrypt:/var/lib/letsencrypt \
-v /docker/letsencrypt-docker-nginx/src/letsencrypt/letsencrypt-site:/data/letsencrypt \
certbot/certbot \
--staging \
certificates
![](https://thana.in.th/wp-content/uploads/2019/10/get-additional-information-with-the-certbot-certificates-command.png)
If the staging command executed successfully, execute the command to return a live certificate
First, clean up staging artifacts:
sudo rm -rf /docker-volumes/
And then request a production certificate: (note that it’s a good idea to supply your email address so that Let’s Encrypt can send expiry notifications)
sudo docker run -it --rm \
-v /docker-volumes/etc/letsencrypt:/etc/letsencrypt \
-v /docker-volumes/var/lib/letsencrypt:/var/lib/letsencrypt \
-v /docker/letsencrypt-docker-nginx/src/letsencrypt/letsencrypt-site:/data/letsencrypt \
-v "/docker-volumes/var/log/letsencrypt:/var/log/letsencrypt" \
certbot/certbot \
certonly --webroot \
--email youremail@domain.com --agree-tos --no-eff-email \
--webroot-path=/data/letsencrypt \
-d xxx.com -d www.xxx.com
If everything ran successfully, run a docker-compose down command to stop the temporary Nginx site
cd /docker/letsencrypt-docker-nginx/src/letsencrypt
sudo docker-compose down
Set up Your Production Site to Run in a Nginx Docker Container
sudo mkdir -p /docker/letsencrypt-docker-nginx/src/production/production-site
sudo mkdir -p /docker/letsencrypt-docker-nginx/src/production/dh-param
docker-compose.yml
version: '3.1'
services:
production-nginx-container:
container_name: 'production-nginx-container'
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./production.conf:/etc/nginx/conf.d/default.conf
- ./production-site:/usr/share/nginx/html
- ./dh-param/dhparam-2048.pem:/etc/ssl/certs/dhparam-2048.pem
- /docker-volumes/etc/letsencrypt/live/xxx.com/fullchain.pem:/etc/letsencrypt/live/xxx.com/fullchain.pem
- /docker-volumes/etc/letsencrypt/live/xxx.com/privkey.pem:/etc/letsencrypt/live/xxx.com/privkey.pem
networks:
- docker-network
networks:
docker-network:
driver: bridge
Next, create the Nginx configuration file for the production site
sudo nano /docker/letsencrypt-docker-nginx/src/production/production.conf
production.conf
server {
listen 80;
listen [::]:80;
server_name xxx.com www.xxx.com;
location / {
rewrite ^ https://$host$request_uri? permanent;
}
#for certbot challenges (renewal process)
location ~ /.well-known/acme-challenge {
allow all;
root /data/letsencrypt;
}
}
#https://xxx.com
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ohhaithere.com;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/xxx.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xxx.com/privkey.pem;
ssl_buffer_size 8k;
ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_ecdh_curve secp384r1;
ssl_session_tickets off;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8;
return 301 https://www.xxx.com$request_uri;
}
#https://www.xxx.com
server {
server_name www.xxx.com;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_tokens off;
ssl on;
ssl_buffer_size 8k;
ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;
ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
ssl_ecdh_curve secp384r1;
ssl_session_tickets off;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4;
ssl_certificate /etc/letsencrypt/live/xxx.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xxx.com/privkey.pem;
root /usr/share/nginx/html;
index index.html;
}
Generate a 2048 bit DH Param file
sudo openssl dhparam -out /docker/letsencrypt-docker-nginx/src/production/dh-param/dhparam-2048.pem 2048
Copy your site content into the mapped directory:
/docker/letsencrypt-docker-nginx/src/production/production-site/
Spin up the production site in a Docker container:
cd /docker/letsencrypt-docker-nginx/src/production
sudo docker-compose up -d
![](https://thana.in.th/wp-content/uploads/2019/10/Screen-Shot-2562-10-16-at-02.00.46.png)
How to Renew Let’s Encrypt SSL Certificates with Certbot and Docker
Earlier, we placed the following section in the production Nginx configuration file:
location ~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
The production site’s docker-compose file then maps a volume into the Nginx container that can be used for challenge requests:
production-nginx-container:
container_name: 'production-nginx-container'
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
#other mapped volumes...
#for certbot challenges
- /docker-volumes/data/letsencrypt:/data/letsencrypt
networks:
- docker-network
To add a crontab, run the following commands:
sudo crontab -e
Place the following at the end of the file, then close and save it.
0 23 * * * docker run --rm -it --name certbot -v "/docker-volumes/etc/letsencrypt:/etc/letsencrypt" -v "/docker-volumes/var/lib/letsencrypt:/var/lib/letsencrypt" -v "/docker-volumes/data/letsencrypt:/data/letsencrypt" -v "/docker-volumes/var/log/letsencrypt:/var/log/letsencrypt" certbot/certbot renew --webroot -w /data/letsencrypt --quiet && docker kill --signal=HUP production-nginx-container