Generate SSL/TLS certificates for free with Nginx/Certbot

Posted by : on

Category : Infrastructure

Why you may want to read this article

Today it is a continuation of my short Infrastructure-related articles.

This article will be about creating and configuring SSL/TLS certificates (https) for your domain and IP.


I have my Ubuntu server, domain that points to this server, Nginx reverse proxy in the docker container, and React application in the container as well. Each request that goes to my application is not protected, because it uses http protocol (not https).

I would like to make my application more secure by adding https functionality for free, without any cost.

To do that, we will

  • Certbot tool inside the docker container that will generate certificates for domain
  • Configure Nginx to use those SSL/TLS certificates

Configuring Nginx

Nginx and certbot docker-compose


version: '3'

    container_name: nginx
    image: nginx:latest
    restart: always
      - ./conf.d:/etc/nginx/conf.d
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
      - ./nginx-logs:/var/log/nginx
      - 80:80
      - 443:443
      - nginx-network
      - default

    image: certbot/certbot:latest
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
      - nginx-network

    external: true

This docker-compose file contains nginx service specification. We created a volume for the nginx configuration to pass local configurations to the container and a volume for certificates ./certbot/….

We have added nginx-network, this is for encapsulation and accessibility. With this setup only containers that are inside nginx-network network can be accessed from nginx.

On top of that, there is certbot specification with the certificate volumes.

Nginx.conf file


user  nginx;

worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/;

events {
    worker_connections  1024;

http {
    client_max_body_size 10M;

    # **disables nginx version**
    server_tokens off;

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  65;

    include /etc/nginx/conf.d/*.conf;

It is pretty simple nginx.conf file, just regular configurations, and in the last line we are including all the subconfigurations for nginx.

Application nginx subconfiguration


#frontend dev env
server {
    listen 80;


    location /.well-known/acme-challenge/ {
        root /var/www/certbot;

    location / {
        proxy_set_header Host $host;
        return 301$request_uri;

server {
    listen 443 ssl;


    ssl_certificate /etc/letsencrypt/live/;
    ssl_certificate_key /etc/letsencrypt/live/;

    location / {
        #this needed to resolve host by docker dns, othervise 'set $upstream will' not work
        resolver valid=10s;

        #this variable needed to not fail nginx if any of the containers is down
        set $upstream http://symptom-tracker-dev:3000;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_pass $upstream;

This is subconfiguration for my react application for demo purpose.

It has 2 parts:

  • The first part is for unsecured http connection that listens to 80th port of our domain It just redirects the request to https connection on 443d port.
  • The second part is specifying secured https connection with SSL/TLS protocols, it directs request to our application docker container symptom-tracker-dev on 3000 internal docker port.

The SSL certificates are ensured by

ssl_certificate /etc/letsencrypt/live/;
ssl_certificate_key /etc/letsencrypt/live/;

But now there is no fullchain.pem and privkey.pem files, let’s generate them.

Generate certificates

Go to our docker-compose.yml location.

Note, that I’m generating the certificates also for 2 subdomains: dev and blog. If you would like to have only one domain - specify only one.


docker-compose run --rm certbot certonly --webroot --webroot-path /var/www/certbot/ -d -d -d

You will see this output:


As you can see our certificates were successfully generated by letsencrypt organization, and since we have our docker volumes they are already in our server and inside the nginx container.

Take a look into certificate expiration, by that time - you need to renew the certificate with the same command.


Now let’s go and try to access our application by domain name



As you can see it shows TLS expiration details.

Follow up

Please subscribe to my social media to not miss updates.: Instagram, Telegram

I’m talking about life as a Software Engineer at Microsoft.

Besides that, my projects:

Symptoms Diary:


About Andrii Bui

Hi, my name is Andrii. I'm Software Engineer at Microsoft with 5 years of experience.