SSL/TLS certificates are an integral part of the modern digital world. This not only establishes your customers’ trust but also secures your websites from cyber threats. However, managing SSL certificates manually can be time-consuming and frustrating. Furthermore, if you’re a small-to-midsize business, the cost of commercial certificates may also be unnecessarily high, while you can easily utilize the power of open-source tools. Actually, even if you work for a big corporation, there might be some cases where you could still leverage these open-source tools.

With that being said, in this blog, I want to introduce Let’s Encrypt, a free, automated, and open Certificate Authority (CA) that provides an automated way to issue SSL certificates. And to facilitate the process of issuing SSL certificates by Let’s Encrypt, we will use Certbot, also a free open-source software, to automate this process. To demonstrate the setup and process, I will walk you through a demo of setting up Let’s Encrypt SSL Certificates for your Nginx web server in a real-world way.

Prerequisites

  • Prepare a Linux-based server where Nginx is installed. I’m using Ubuntu 24.04 in this demo.
  • A registered domain name pointing to your server’s IP address.

Install Certbot

I have a web server called web-01.srv.local. You can do the same with your server. Log in to your server, and install Certbot.

sudo apt update
# python3-certbot-nginx is needed for working with nginx
sudo apt install certbot python3-certbot-nginx -y

Noter: from Ubuntu 20+, if you encounter the error “The requested nginx plugin does not appear to be installed”, it indicates that your server doesn’t have “python3-certbot-nginx” package.

Obtaining SSL Certificates

Certbot simply addresses two tasks:

  • Obtaining a certificate: includes authetication of your domain control, domain verification, and renewing the domain.
  • (Optionally) Installing the certificate on supported web servers (Nginx/Apache) by modifying their settings.

I don’t recommend installing certificate directly through certbot command as it might change your Nginx setting in the way you don’t expect. Better we do it ourselves (e.g. through automation of a configuration management tool, such as Ansible, Puppet). In here, we obtain certificate only.

Certbot uses one of the plugins to achieve two tasks:

These plugins use one of the ACME protocal challenges, basically, there are two popular ones are http-01 (which access your webserver port 80 to verify) and dns-01 (requiring configuration of a DNS server on port 53). I think these two cover most of the cases.

Option 1: Obtain certificates with http-01 challenge.

http-01 requires your Nginx server to have public IP in front of it, e.g. you web server is on AWS EC2 with public IP associated to it or other hostings similar. I’m using the Ubuntu server on a local network which doesn’t have a public IP (actually I can use Port Forwarding on my modem but my ISP block verification in this way, so I have to use dns-01 challenge instead) .

In here, if you use nginx plugin combined with http-01 challenge to verify your domain, run:

sudo certbot certonly --nginx -d your-example-domain.com

certonly option allows us to obtain certificates only without installing those Once it’s done, there are 2 files generated are: fullchain.pem and privkey.pem should be in /etc/letsencrypt/live/yourdomain.com/

Certificate Renewal: Let’s Encrypt certificates have a validity of 90 days. We have to automate the renewal process so that our websites are not interrupted. Simply add a cron task to run the renew process every day at 23pm for us.

cat <<EOF > /etc/cron.d/certbot-nginx-renew
0 23 * * * certbot renew --nginx --quiet
EOF

The renew action checks all installed certificates for impending expiry and attempt to renew them.

If you just have only one web server, this way might suite your case. However, if you manage a fleet of many web servers, this way seems to be cumbersome because it requires you to install Certbot on every web server. We might want to manage the certificates creation in a central way. That’s where the dns-01 comes into play. Please see the option 2.

Option 2: Obtain certificates with dns-01 challenge.

dns-01 doesn’t require Certbot to be installed on a server with Nginx/Apache installed. We just need that server is a Linux-based OS. dns-01 requires to use DNS plugins or manual plugin to work.

For domain providers with supported DNS plugins:

Requesting SSL Certificates through dns-01 challenge really depends on which domain provider you are using. If your domain provider is in a DNS supported list (e.g. Rout53, Cloudfare, Google, etc.), it might be straightforward as we just use supported DNS plugins, just follow the link associated to each DNS plugin in that DNS supported list.

For example, if you use Rout53, please follow this certbot-dns-route53 plugin to set up AWS credentials and IAM. You need to install these supported plugin separately as they don’t come with default. E.g.

# For AWS Route53 DNS
sudo apt install python3-certbot-dns-route53 -y

Once installed and set up credentials and IAM, just run:

certbot certonly --dns-route53 -d your-example-domain.com -d www.your-example-domain.com

To renew, simply add a cron task to run:

cat <<EOF > /etc/cron.d/certbot-nginx-renew
0 23 * * * certbot renew --dns-route53 -d your-example-domain.com -d www.your-example-domain.com
EOF

For domain providers without supported DNS plugins:

If your domain provider is not in the supported list. For example, I’m using a domain from WordPress which is not supported, we have to use manual plugin.

In here, on my web-01 server (should be your Nginx web server), run:

sudo certbot certonly --manual --preferred-challenges dns -d your-example-domain.com

If you run the first time, Certbot will ask you to register an ACME server with Let’s Encrypt.

Output:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
 (Enter 'c' to cancel): enter-your-email@example.com 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.5-February-24-2025.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y # Select Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N # Select Y/N is up to you
Account registered.

Once you hit Y/N, it continues requesting a certificate and outputs (the command excludes the previous steps if you have registered the ACME server before that):

Requesting a certificate for your-example-domain.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:

_acme-challenge.your-example-domain.com.

with the following value:

wtM9lnD****************************uswlxiXqiI

Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.your-example-domain.com.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

At this step, DO NOT hit Enter yet; you will need to go to your domain provider and add a DNS TXT record as guided above. I would suggest setting the Time-To-Live (TTL) to 5 minutes in order to let the DNS record propagate soon. Here is my case:

Once you configured DNS TXT, can go to https://toolbox.googleapps.com/apps/dig/#TXT/ and test your DNS TXT, you should receive similar result if you have done correctly.

Then, hit the Enter button to let the requesting process continues. Output:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/your-example-domain.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/your-example-domain.com/privkey.pem
This certificate expires on 2025-08-09.
These files will be updated when the certificate renews.

NEXT STEPS:
- This certificate will not be renewed automatically. Autorenewal of --manual certificates requires the use of an authentication hook script (--manual-auth-hook) but one was not provided. To renew this certificate, repeat this same certbot command before the certificate's expiry date.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

If you have done correctly, it should look like similar below:

Ok, congratulation, you’ve got your own certificates, now we continue configuring our site config as the Case 1 to make it use new SSL certificates.

Certificate Renewal: manual plugin do not support automatic renewal unless combined with an authentication hook script. In our case, we can’t do it via cron task because we have to go through the same process we just did above, e.g. manually update DNS TXT like the same process we just did above. So we need to re-run the certbot command manually every 3 months.

I know it sounds cumbersome, in order to automatically the renewal process, it depends on your domain provider, and you can look through the authentication hook script doc to implement the method. Basically, you need two scripts, one is for automating the configuration of DNS TXT step, and the other is to clean up the DNS TXT config after generating the SSL certificates. Ultimately, you could run the certbot command through cron task which looks like below:

sudo certbot certonly --manual --preferred-challenges=dns --manual-auth-hook /path/to/dns/authenticator.sh --manual-cleanup-hook /path/to/dns/cleanup.sh -d secure.example.com

Configure Nginx config file for your site

Once you have SSL certificates generated in /etc/letsencrypt/live/your-example-domain.com/ from either one of the 2 options above. Open your nginx config file where you configure your domain, e.g. in /etc/nginx/nginx.conf or /etc/nginx/sites-available/your-example-domain.com, and make the following changes:

server {
    listen 80;
    server_name your-example-domain.com;
    # Redirect access to your site from port 80 to 443
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name your-example-domain.com;

    # Configure your site to use Let's Encrypt SSL certificates
    ssl_certificate /etc/letsencrypt/live/your-example-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-example-domain.com/privkey.pem;

    # Other configuration options
    ...

    location / {
        # Your website configuration
        ...
    }
}

Save the changes, and reload Nginx service.

sudo systemctl reload nginx

Access my website through https. I’ve got the result below and you should have the same:

I hope this blog helps you facilitate the management of SSL certificates for your site.

If you like my blog post and want to find a way to thank me, feel free to Buy me a coffee to support me in providing more quality content, or simply share it with others who may have the same issue. Thanks for reading my blog.


Discover more from Turn DevOps Easier

Subscribe to get the latest posts sent to your email.

By Binh

Leave a Reply

Your email address will not be published. Required fields are marked *

Content on this page