Certbot + Let's Encrypt + Docker Nginx (Guía 2025)

Certbot + Let's Encrypt + Docker Nginx (Guía 2025)
Certbot + Let's Encrypt + Docker Nginx (Guía 2025)

Configura un certificado SSL gratuito con Certbot y Let's Encrypt en un entorno Docker Nginx. Esta guía actualizada te muestra cómo hacerlo de forma segura y automatizada.

¿Qué es HTTPS?

HTTPS es un protocolo de aplicación basado en el protocolo HTTP, destinado a la transferencia segura de datos de Hipertexto. En resumen, es la versión segura de HTTP.

¿Qué es CertBot?

Certbot es una herramienta de software gratuita y de código abierto para usar automáticamente los certificados de Let’s Encrypt en sitios web y habilitar HTTPS. Está mantenido por la Electronic Frontier Foundation (EFF).

¿Qué es LetsEncrypt?

Let’s Encrypt es una autoridad de certificación (CA) gratuita, automatizada y abierta, gestionada por la organización sin ánimo de lucro Internet Security Research Group (ISRG).


Prerequisitos

Antes de empezar, necesitas:

  1. Un servidor con Docker y Docker Compose instalados.
  2. Un dominio registrado (ej. mi-dominio.com) y activo.
  3. Acceso a la configuración DNS del dominio para apuntar un registro A a la IP pública de tu servidor.

Estructura de Archivos

Para este tutorial, usaremos una estructura de directorios simple. Crea una carpeta para tu proyecto y, dentro de ella, los siguientes archivos:

mi-proyecto/
├─ docker-compose.yml
└─ data/
——–└─ nginx/
———-└─ conf/
————└─ default.conf

  • docker-compose.yml: Nuestro archivo de orquestación.
  • data/nginx/conf/default.conf: Nuestra configuración del sitio para Nginx.

1. Configurar Docker Compose

Este es el corazón de nuestro sistema. Crearemos un archivo docker-compose.yml con dos servicios: nginx (nuestro servidor web) y certbot (para obtener y renovar los certificados).

docker-compose.yml:

version: "3.8"

services:
  nginx:
    container_name: nginx
    image: nginx:latest
    ports:
      - "80:80"
      - "443:443"
    volumes:
      # Carpeta web principal (pon tu sitio aquí)
      - ./data/www:/usr/share/nginx/html
      # Configuración de Nginx
      - ./data/nginx/conf/default.conf:/etc/nginx/conf.d/default.conf:ro
      # Volúmenes compartidos con Certbot
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"

  certbot:
    container_name: certbot
    image: certbot/certbot
    volumes:
      # Volúmenes compartidos con Nginx
      - ./data/certbot/conf:/etc/letsencrypt
      - ./data/certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

Explicación de los volúmenes compartidos:

  • /etc/letsencrypt: Aquí guarda Certbot los certificados. Nginx necesita leerlos.

  • /var/www/certbot: Certbot necesita escribir un archivo temporal aquí (el “desafío” o “challenge”) y Nginx necesita servirlo para que Let’s Encrypt pueda verificar que eres el dueño del dominio.

2. Configurar Nginx

Ahora creamos nuestro archivo de configuración de Nginx. Este archivo ya está preparado para gestionar tanto el desafío de Certbot (en el puerto 80) como el tráfico SSL (en el puerto 443).

  • data/nginx/conf/default.conf: (Recuerda reemplazar mi-dominio.com con tu dominio real)

server {
    listen 80;
    server_name mi-dominio.com;

    # Servir el desafío ACME para Certbot
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    # Redirigir todo el resto del tráfico a HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name mi-dominio.com;
    
    # --- Configuración SSL ---
    # ¡Estas rutas fallarán al principio! Lo arreglaremos en el siguiente paso.
    ssl_certificate /etc/letsencrypt/live/mi-dominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mi-dominio.com/privkey.pem;

    # Opcional: Mejorar la seguridad (Recomendado)
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # --- Configuración del Sitio ---
    location / {
        # Ejemplo: Servir un sitio estático
        root /usr/share/nginx/html;
        index index.html;
        
        # Ejemplo: Hacer proxy a otro servicio (ej. una app Node)
        # proxy_pass http://mi_app:3300;
    }
}

3. Obtener el Certificado SSL (El primer inicio)

Aquí está el clásico problema del “huevo y la gallina”:

  • Nginx no puede arrancar porque los archivos ssl_certificate (que pusimos en default.conf) aún no existen.

  • Certbot (en modo webroot) no puede obtener los certificados porque Nginx no está corriendo para servir el desafío.

La solución es generar unos certificados falsos (dummy) para que Nginx arranque la primera vez.

Paso 3.1: Generar certificados Dummy

Vamos a crear las carpetas y generar un certificado autofirmado temporal solo para que Nginx arranque.


# Reemplaza mi-dominio.com con tu dominio
DOMAIN="mi-dominio.com"

# 1. Crear las carpetas
mkdir -p ./data/certbot/conf/live/$DOMAIN
mkdir ./data/certbot/www

# 2. Generar el certificado dummy
openssl req -x509 -nodes -newkey rsa:2048 -days 1 \
    -keyout "./data/certbot/conf/live/$DOMAIN/privkey.pem" \
    -out "./data/certbot/conf/live/$DOMAIN/fullchain.pem" \
    -subj "/CN=localhost"

Paso 3.2: Iniciar los servicios

Ahora que Nginx tiene unos certificados (falsos) para leer, podemos iniciar todo.

docker compose up -d

Paso 3.3: Obtener los certificados Reales

Nginx está corriendo (con un certificado falso). Ahora, le pedimos a Certbot que obtenga los certificados reales. Certbot reemplazará los archivos falsos por los buenos.

# Reemplaza mi-dominio.com y [email protected]
docker compose run --rm certbot certonly \
    --webroot \
    --webroot-path /var/www/certbot \
    --email [email protected] \
    --domain mi-dominio.com \
    --rsa-key-size 4096 \
    --agree-tos \
    --non-interactive \
    --force-reissue

Paso 3.4: Recargar Nginx

Los certificados ya son reales. Forzamos a Nginx a recargarlos:

docker compose exec nginx nginx -s reload

¡Y ya está! Tu sitio ahora funciona con un certificado SSL válido.

4. Renovación Automática

No necesitas hacer nada más. La configuración de docker-compose.yml que hemos creado se encarga de todo:

  • El servicio certbot (entrypoint: …) se despierta cada 12 horas e intenta renovar (certbot renew). Si el certificado no está cerca de expirar, no hace nada.
  • El servicio nginx (command: …) recarga su configuración cada 6 horas (nginx -s reload). Esto asegura que, cuando Certbot sí renueve el certificado, Nginx lo cargará automáticamente en las siguientes horas.