How to Configure SSL/TLS Certificates with Let's Encrypt for Self-Hosted Services

Let's Encrypt provides free, automated X.509 certificates that enable HTTPS for any domain you control using the ACME protocol, typically implemented via Certbot or containerized add-ons.

The Self-Hosting-Guide repository by mikeroyal references Let's Encrypt as the standard solution for securing self-hosted applications with HTTPS. According to the guide at README.md line 2583, you can obtain certificates through either native Certbot clients or Docker-based automation tools that handle the entire certificate lifecycle.

Two Approaches to Certificate Automation

Self-hosting environments typically use one of two architectures to obtain and manage Let's Encrypt certificates:

  • Certbot (stand-alone or web-server plugin) – A native client that communicates directly with the Let's Encrypt ACME API. It can spin up a temporary HTTP server for validation or integrate with existing web servers like Apache or Nginx. Use this approach when you control the host OS and can install packages directly.

  • Docker-based Let's Encrypt add-ons – Pre-built containers that bundle Certbot with reverse-proxy software. These manage certificates inside the container and expose them via bind-mounts or Docker networks. Ideal for container-centric setups where you prefer not to install software on the host.

Both methods rely on the ACME protocol, where Let's Encrypt issues an HTTP-01 challenge that the client must fulfill to prove domain ownership.

Method 1: Certbot Stand-Alone Installation

Installation and Initial Certificate Request

To install Certbot on Ubuntu or Debian systems and obtain your first certificate:

sudo apt update && sudo apt install -y certbot

sudo certbot certonly --standalone \
    -d example.com -d www.example.com \
    --email admin@example.com \
    --agree-tou --no-eff-email

The --standalone flag instructs Certbot to spin up a temporary web server on port 80 to complete the HTTP-01 challenge. This requires port 80 to be accessible from the internet without NAT hairpinning.

Web Server Configuration

After obtaining the certificate, configure your web server to use the generated files. For Nginx, create a configuration file that references the certificate paths:

cat <<'EOF' | sudo tee /etc/nginx/sites-available/example.com.conf
server {
    listen 443 ssl;
    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;

    # ... your location blocks ...

}
EOF

sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Automatic Renewal Setup

Certbot installations include a systemd timer that handles renewal automatically. Enable and start this timer to ensure certificates renew every 60 days (certificates are valid for 90 days):

sudo systemctl enable --now certbot.timer

This timer runs certbot renew daily and reloads your web server when certificates are refreshed.

Method 2: Docker-Based Let's Encrypt Add-ons

For container-centric deployments, use a Docker-based approach like the Home Assistant Let's Encrypt add-on referenced in the Self-Hosting-Guide. This method bundles the certificate management with your reverse proxy.

Docker Compose Configuration

Create a docker-compose.yml that runs Certbot alongside an Nginx reverse proxy:

version: "3.8"
services:
  nginx-proxy:
    image: nginxproxy/nginx-proxy:latest
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - ./letsencrypt:/etc/nginx/certs:ro
    restart: unless-stopped

  letsencrypt:
    image: linuxserver/letsencrypt
    container_name: letsencrypt
    environment:
      - URL=example.com
      - VALIDATION=http
      - EMAIL=admin@example.com
    volumes:
      - ./letsencrypt:/config
    network_mode: "service:nginx-proxy"
    restart: unless-stopped

The linuxserver/letsencrypt container runs Certbot internally and shares network space with the reverse proxy to answer challenges. Certificates are written to ./letsencrypt and mounted read-only into the proxy.

Volume Mounting and Proxy Integration

The container stores certificates in a persistent volume that your application containers can access via bind-mounts. When the certificate renews, the container automatically reloads the proxy to apply the new certificate without manual intervention.

Method 3: Caddy with Built-In ACME Support

Caddy includes a native ACME client that eliminates manual configuration. Create a Caddyfile:

example.com {
    reverse_proxy your_service:8080
    tls admin@example.com
}

Deploy with Docker:

docker run -d -p 80:80 -p 443:443 \
  -v $(pwd)/Caddyfile:/etc/caddy/Caddyfile \
  -v caddy_data:/data caddy:latest

Caddy automatically obtains and renews certificates, handling the HTTP-01 challenge internally and reloading the configuration when certificates update.

ACME Protocol and Validation Requirements

Regardless of the method chosen, several requirements must be met for successful certificate issuance:

  • Domain resolution must point to your public IP address before initiating the challenge.
  • Port 80/443 accessibility is required for the HTTP-01 challenge; ensure your firewall and router allow inbound connections.
  • Private key security is critical; restrict privkey.pem permissions to only the process that needs it (typically chmod 600).
  • Automatic renewal occurs every 60 days by default, ensuring continuous HTTPS availability without manual intervention.

Summary

  • Let's Encrypt provides free SSL/TLS certificates through the automated ACME protocol.
  • Certbot offers a native solution for Linux hosts with systemd-based renewal timers.
  • Docker add-ons like linuxserver/letsencrypt provide containerized certificate management for microservice architectures.
  • Caddy offers zero-configuration HTTPS with built-in ACME support.
  • Port 80 must be open to the internet for HTTP-01 validation to succeed.
  • Automatic renewal is standard across all methods, ensuring certificates never expire unexpectedly.

Frequently Asked Questions

What is the difference between Certbot stand-alone and web-server plugin modes?

Certbot stand-alone mode spins up a temporary web server on port 80 to complete the ACME challenge, while plugin mode uses your existing Apache or Nginx installation to answer challenges. Use stand-alone when no web server is running, and use plugin mode when you already have a web server installed that you want to configure automatically.

Can I use Let's Encrypt with a dynamic IP address?

Yes, but you must configure a dynamic DNS provider like DuckDNS to keep your domain pointing at your current public IP. The Self-Hosting-Guide indicates that Docker-based Let's Encrypt add-ons can be coupled with dynamic DNS providers to maintain certificate validity even when your IP changes.

How do I troubleshoot certificate renewal failures?

First verify that port 80 is accessible from the internet and that your domain DNS records resolve to your current public IP. Check Certbot logs with sudo journalctl -u certbot or examine the Docker container logs for ACME error messages. Ensure no firewall rules block inbound connections during the challenge window.

Where are the certificate files stored in a Docker-based setup?

Docker-based Let's Encrypt containers typically store certificates in a bind-mounted volume such as ./letsencrypt or /config within the container. These volumes map to host directories containing fullchain.pem and privkey.pem files that you can mount into other containers or reference directly in your reverse-proxy configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →