← Back to DevBytes

Let's Encrypt: Setup, Configuration, and Best Practices

Introduction to Let's Encrypt

Let's Encrypt is a free, automated, and open certificate authority (CA) that provides domain-validated (DV) SSL/TLS certificates. Backed by the Internet Security Research Group (ISRG), its mission is to make encrypted connections ubiquitous across the web by removing cost, complexity, and manual intervention from the certificate lifecycle. Instead of relying on traditional paid certificates that must be renewed manually every one or two years, Let's Encrypt issues short-lived certificates (90 days) and fully automates issuance and renewal via the ACME (Automated Certificate Management Environment) protocol.

The service is trusted by all major browsers and operating systems, meaning certificates issued by Let's Encrypt are recognized as valid without any additional configuration on the client side. Since its launch, it has become the default choice for developers, small businesses, and large-scale deployments alike, securing hundreds of millions of domains worldwide.

Why Let's Encrypt Matters

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Before Let's Encrypt, HTTPS adoption was hindered by cost and operational overhead. Manual certificate renewal led to expired certificates, broken websites, and security lapses. Let's Encrypt addresses these issues directly:

For a developer, using Let's Encrypt means you can deploy production-grade TLS on any server in minutes, integrate it into CI/CD pipelines, and never worry about expiration again if renewal automation is properly configured.

How Let's Encrypt Works

Let's Encrypt uses the ACME protocol (RFC 8555) to handle certificate requests and domain validation. A client (such as Certbot, acme.sh, lego, or a custom implementation) communicates with Let's Encrypt servers to prove ownership of a domain name. The process typically follows these steps:

  1. The client generates a key pair and a certificate signing request (CSR) for the domains to be secured.
  2. The client submits the CSR to Let's Encrypt and requests a challenge type for domain validation.
  3. Let's Encrypt issues a challenge (e.g., HTTP-01, DNS-01) that the client must complete to prove control over the domain.
  4. The client completes the challenge and notifies the CA.
  5. Upon successful validation, Let's Encrypt issues the certificate, which the client downloads and installs.
  6. The client typically saves the certificate and private key, configures the web server, and sets up automated renewal.

Validation Methods

There are several challenge types, each suited to different environments:

Choosing the right challenge depends on your infrastructure. HTTP-01 is simplest for typical web servers; DNS-01 is required for wildcard certificates and for setups where the server isn't publicly reachable on port 80.

Setting Up Let's Encrypt

The most widely used ACME client is Certbot, maintained by the Electronic Frontier Foundation (EFF). It integrates directly with many web servers (Apache, Nginx) to handle both challenge completion and configuration. You can also use other clients like acme.sh, lego, or dehydrated for more customized scenarios. Below, we focus on Certbot, but the core concepts apply to all ACME clients.

Installing Certbot

The recommended installation method varies by OS and web server. On modern distributions, using the snap package ensures you always get the latest version with automatic updates. For older systems, native packages or pip may be used.

Example: Installing Certbot via snap on Ubuntu 20.04+:

# Install snapd if not already installed
sudo apt update
sudo apt install snapd

# Install the core snap, then Certbot
sudo snap install core
sudo snap install --classic certbot

# Link the certbot command to the snap binary
sudo ln -s /snap/bin/certbot /usr/bin/certbot

For Debian-based systems with the Certbot package in the repository:

sudo apt update
sudo apt install certbot python3-certbot-nginx python3-certbot-apache

Replace python3-certbot-nginx or python3-certbot-apache with the appropriate plugin for your web server.

Obtaining a Certificate (HTTP-01 Challenge)

If you are running a web server that Certbot supports, you can obtain and install a certificate in one command. The server must already be serving the domain on port 80 (HTTP) and have the necessary server block or virtual host configured.

Nginx

sudo certbot --nginx -d example.com -d www.example.com

Certbot will scan the Nginx configuration, find the relevant server blocks, perform the HTTP-01 challenge automatically, and modify the configuration to enable HTTPS by adding SSL directives and redirecting HTTP to HTTPS. You’ll be prompted for an email address for renewal notifications and to agree to the terms of service.

Apache

sudo certbot --apache -d example.com -d www.example.com

Similarly, Certbot modifies Apache virtual hosts, enabling the SSL module and rewriting configurations to use the new certificate.

Manual / Standalone Mode (No Running Server)

If you don't have a web server running or prefer not to let Certbot modify configurations, you can use the certonly subcommand with the standalone plugin. This spins up a temporary web server on port 80 to complete the challenge.

sudo certbot certonly --standalone -d example.com

After issuance, the certificate and key are stored in /etc/letsencrypt/live/example.com/ (by default). You must then manually configure your web server to use them.

Obtaining a Certificate with DNS-01 Challenge

DNS validation is essential for wildcard certificates and for domains where HTTP access is blocked or impractical. You need to automate DNS record creation via a plugin or perform manual steps.

Using DNS Plugin (Cloudflare Example)

Certbot supports many DNS providers through plugins. For Cloudflare, install the plugin and provide API credentials:

# Install the Cloudflare DNS plugin (snap)
sudo snap install certbot-dns-cloudflare

# Create a file containing Cloudflare API token
# e.g., /home/user/.secrets/cloudflare.ini
# dns_cloudflare_api_token = "your-token"

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /home/user/.secrets/cloudflare.ini \
  -d example.com \
  -d *.example.com

This automatically creates and removes the required TXT record, then issues the certificate. Wildcard certificates cover any subdomain at the same level (e.g., *.example.com).

Manual DNS Challenge

If no plugin exists for your DNS provider, use the manual mode:

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

Certbot will output a specific TXT record and wait for you to create it. After DNS propagation (you can verify with dig or nslookup), press Enter to continue. This method is not automatable for renewal, so use it only for initial testing or if you plan to script the renewal with a custom hook.

Certificate File Locations and Renewal Basics

All certificates, keys, and related files are stored in the /etc/letsencrypt directory structure (default for Certbot on Linux):

Certbot sets up a systemd timer (or cron job) that runs twice daily and renews certificates that expire in less than 30 days. You can test renewal manually:

sudo certbot renew --dry-run

This simulates the renewal process without actually issuing new certificates. Always test after configuration changes.

Configuration and Integration

Configuring Web Servers to Use the Certificate

When Certbot does not modify server configurations automatically (e.g., certonly or manual mode), you must update your server's SSL settings yourself. Here are examples for common servers.

Nginx

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Strong TLS configuration (recommended)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...';
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    root /var/www/html;
    # ... other directives
}

# HTTP redirect to HTTPS
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

Apache

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    # Strong TLS settings
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...

    DocumentRoot /var/www/html
    # ...
</VirtualHost>

# Redirect HTTP to HTTPS
<VirtualHost *:80>
    ServerName example.com
    Redirect permanent / https://example.com/
</VirtualHost>

Automated Renewal with Hooks

Certbot allows you to define pre- and post-hooks that run before and after renewal. This is crucial for servers that need to reload configurations or for DNS plugins that require cleanup. Hooks can be specified in /etc/letsencrypt/renewal/example.com.conf or globally.

Example: Reload Nginx after successful renewal:

# In the renewal configuration file:
renew_hook = systemctl reload nginx

Or pass as command-line option during initial run:

sudo certbot --nginx -d example.com --post-hook "systemctl reload nginx"

If you use DNS plugins, the plugin itself handles cleanup; no extra hooks are typically needed. For custom scripts, ensure the hook exits with zero status and does not block indefinitely.

Renewal for Non-Web Servers (Mail, FTP, etc.)

Certificates can secure non-HTTP services. After obtaining a certificate (using standalone or DNS challenge), simply point the service's configuration to fullchain.pem and privkey.pem. Then ensure renewal includes a post-hook that restarts or reloads the service. For example, with Postfix or Dovecot, the hook could run systemctl restart postfix dovecot.

Multi-Domain and Wildcard Certificates

You can include up to 100 domains per certificate (subject to rate limits). Use the -d flag for each domain. Wildcard domains require DNS-01 validation:

sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials ~/cloudflare.ini \
  -d example.com -d *.example.com -d example.net

This creates a single certificate covering all listed domains. Renewal works automatically as long as the DNS plugin credentials remain valid.

Best Practices

1. Automate Renewal and Monitor It

Never rely on manual certificate renewal. Certbot’s timer is robust, but you must ensure the timer is active and that renewal succeeds without errors. Monitor expiration using external services (e.g., Uptime Robot, Nagios, or custom scripts) that query the certificate’s notAfter date or attempt TLS connections.

Verify the renewal timer:

sudo systemctl status certbot.timer   # systemd
# or
sudo crontab -l | grep certbot        # cron

Set up email alerts for renewal failures by configuring Certbot’s --email flag or using post-hook scripts that send notifications.

2. Protect Private Keys

Private keys in /etc/letsencrypt/live/ should be readable only by root and the services that need them. Use strict file permissions (e.g., 640) and never expose them via public web directories. For extra security, consider using hardware security modules (HSMs) or separate key management, but for most deployments, standard OS file permissions suffice.

# Ensure the directory is secure
sudo chmod 0750 /etc/letsencrypt/live
sudo chmod 0750 /etc/letsencrypt/archive

3. Use the Right Validation Method for Your Environment

Choose HTTP-01 for standard, internet-facing web servers with a running HTTP stack. Use DNS-01 when you need wildcard certificates, when port 80 is blocked by a firewall, or when you’re securing internal services that do not have public HTTP endpoints. Avoid manual DNS mode for production because it cannot be automated for renewal. Prefer DNS plugins tied to your DNS provider’s API.

4. Keep Client Software Updated

ACME clients evolve rapidly to support new features, fix security bugs, and adapt to CA changes. If using Certbot via snap, updates happen automatically. With pip or package managers, schedule regular updates (e.g., monthly) and watch for deprecation notices from Let's Encrypt (e.g., algorithm changes, new chain requirements).

5. Handle Rate Limits Gracefully

Let's Encrypt imposes rate limits to protect infrastructure. The main limits are:

During development, use the staging environment (--test-cert or the ACME staging URL) to avoid hitting limits. Staging certificates are not trusted by browsers but mimic the real CA behavior exactly.

# Test with staging
sudo certbot --dry-run --test-cert --nginx -d example.com

When rolling out a large number of domains, stagger issuance over multiple weeks or use multiple accounts (with careful planning) to stay within limits.

6. Always Use the Fullchain File

Most modern servers (Nginx, Apache, HAProxy) require the full chain – the server certificate concatenated with the intermediate CA certificate. Point to fullchain.pem rather than cert.pem alone, otherwise some clients (especially mobile browsers) may not trust the connection. The fullchain.pem file is updated on renewal, so using symlinks ensures your server always picks up the latest version.

7. Configure Strong TLS Settings

Obtaining a certificate is only half the story. Ensure your server’s TLS configuration follows modern security standards. Recommended base settings:

Example Nginx OCSP stapling and HSTS:

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

8. Plan for Disaster Recovery

Since Let's Encrypt certificates are short-lived, losing access to the private key or the ACME account can be problematic. Back up the following securely:

If a server is lost, you can re-register with the same ACME account (using the account key backup) and re-issue certificates immediately without rate limit concerns. Without the account key, you can create a new account, but you may encounter the duplicate certificate limit temporarily.

9. Avoid Hardcoding Certificate Paths in Containerized Environments

In Docker or Kubernetes, certificates are often short-lived and generated at runtime. Instead of using Certbot inside a container that persists certificates on a volume, consider using an ACME-aware ingress controller (like cert-manager for Kubernetes) that handles issuance and renewal dynamically. If you must use Certbot in a container, use a shared volume or a sidecar pattern, and ensure renewal hooks reload the web server inside the container.

10. Contribute and Stay Informed

Let's Encrypt is community-driven. Subscribe to their blog and community forum to stay updated on upcoming changes (e.g., new chain requirements, algorithm deprecation). Report issues, share feedback, and if possible, donate or contribute code to help keep the ecosystem healthy.

Conclusion

Let's Encrypt has transformed the landscape of web security by making TLS certificates free and effortless. For developers, it removes a major friction point, enabling secure deployments from day one. The key to success lies in understanding the validation mechanisms, choosing the right client for your stack, and meticulously setting up automated renewal with proper monitoring. By following the practices outlined here—securing keys, using fullchain files, testing in staging, and keeping software current—you can ensure that your services remain trusted and resilient. Embrace the automation, and let Let's Encrypt handle the certificate lifecycle while you focus on building great applications.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles