Introduction
By default, when docker containers are deployed they run on normal HTTP but most times it's better to run web services using HTTPS which is a secure protocol over the internet. So we're going to see how to enable an SSL certificate on docker containers using LetsEncrypt and Certbot
Prerequisites
Certbot requires a live domain for it to be assigned an SSL certificate to it, you can obtain a domain at your chosen registrar, In this article, I'll be using my own domain mrshanas.com. I've created a subdomain for it.
Also, a virtual server is required with docker
and docker-compose
installed in it, I'm using Digitalocean droplets that have docker installed in it from the market place but you can use any of your choices but make sure the above-mentioned are installed
Getting Started
Create a simple project with a Dockerfile
and docker-compose.yaml
files and a config
directory that contains an empty nginx.conf
.
Dockerfile
FROM nginx:1.21.1-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY ./nginx.conf /etc/nginx/conf.d
The Dockerfile contains a script to pull the Nginx image, delete the default config file, and then copy the defined config file from our project
nginx.conf
server {
listen 80;
listen [::]:80;
server_name test.mrshanas.com;
server_tokens off;
location /.well-known/acme-challenge/ {
allow all;
root /tmp/acme-challenge;
}
location / {
return 301 https://$host$request_uri;
}
}
This will enable Nginx to run locally at HTTP, and listen on port 80, and all incoming requests will be redirected to https.
the line
listen [::]:80
means for IPv6 addresses.
Change the server name to the domain you want to enable the certificate. For me, it is test.mrshanas.com
Certbot makes a challenge to the server by making a request to the special URL ${your_domain}/.well-known/acme-challenge/
so we want to enable it to access the URL and verify the challenge for it to generate the certificate
docker-compose.yml
version: "3"
services:
nginx:
container_name: "nginx_server"
build:
context: ./config
ports:
- 80:80
- 443:443
volumes:
- ./config:/config
- /etc/letsencrypt:/etc/letsencrypt:ro
- /tmp/acme-challenge:/tmp/acme-challenge
networks:
- app
restart: always
letsencrypt:
container_name: "certbot"
image: certbot/certbot
command: sh -c "certbot certonly --webroot -w /tmp/acme-challenge/ -d test.mrshanas.com --text --agree-tos --email shanas@mrshanas.com --rsa-key-size 4096 --verbose --keep-until-expiring --preferred-challenges=http"
entrypoint: ""
volumes:
- "/etc/letsencrypt:/etc/letsencrypt"
- "/tmp/acme-challenge:/tmp/acme-challenge"
environment:
- TERM=xterm
networks:
app:
driver: bridge
The above file defines two docker containers nginx
and letsencrypt
that will make the task successful.
Running Containers on HTTP
The Nginx container is based on the Dockerfile
we created and exposes ports 80 and 443 and volumes that will contain the generated SSL certificates
The certificates will be stored in
/etc/letsencrypt
Now run docker-compose up --build nginx
and visit your domain name and If it's successful you will see like below
That means the container ran but it is not running on HTTPS, so let's see how to make it secure.
The letsencrypt container pulls the certbot image from docker hub and runs the command certbot certonly --webroot -w /tmp/acme-challenge/ -d test.mrshanas.com --text --agree-tos --email shanas@mrshanas.com --rsa-key-size 4096 --verbose --keep-until-expiring --preferred-challenges=http
Make sure to replace the domain name with the one you want to configure SSL and the email with your email
Generating SSL Certificates with certbot image
Open a new terminal while the Nginx container is running and run docker-compose up --build letsencrypt
, If it's successful you will see the output below
That means certbot was successful and generated certificates that are stored in the directory etc/letsencrypt/live/<your_domain>
.
Enabling HTTPS on the domain
Add the following lines below the server
block in the nginx.conf
server {
listen 443 ssl;
listen [::]:443 ssl http2;
server_name test.mrshanas.com;
ssl_certificate /etc/letsencrypt/live/test.mrshanas.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/test.mrshanas.com/privkey.pem;
}
Replace test.mrshanas.com
with your domain whose the certificate belongs to, and stop the Nginx container then rebuild it again with the command docker-compose up --build -d nginx
-d
flag means the container will run in detached mode
If the above command was successful then navigate to your domain and you should see as below
That means the process was successful and your domain is running on HTTPS.
Conclusion
With the configuration, you can deploy your projects using Docker and make them run on HTTPS with ease. You can find the complete sample code in my GitHub repo.
Top comments (4)
Hi, reading your article I wonder if it can also work for a reverse proxy? I've been trying and I can't
Yes it can please can you tell me what's not working
your article s missing one major thing. when you first run nginx there are no letsencrypt certificates and nginx server wont start without those but to get those certificates, you need nginx server running. how do you workaround this egg and chicken problem?
Great writeup and I found it really helpful, thanks for posting this! I originally thought that having LE as a separate container would be too much complexity.