Cambiar tema

Guía completa para desplegar Nginx con Docker: montaje de configuración, HTTPS y proxy inverso

Easton editorial illustration: fault-isolation scanner

Por séptima vez pulsas docker restart nginx, refrescas el navegador y sigue apareciendo el error 502. Cambiaste nginx.conf, pero la configuración del contenedor no se aplica.

Los problemas de configuración al desplegar Nginx con Docker los ha pisado casi todo el mundo que trabaja con contenedores. Montaje de archivos de configuración, certificados HTTPS, comunicación entre contenedores: operaciones que parecen simples, pero esconden trampas en los detalles. Este artículo cubre desde la forma correcta de montar la configuración hasta la renovación automática de certificados HTTPS y la configuración de red del proxy inverso, con un esquema de despliegue completo y usable.

Aprenderás:

  • Por qué la configuración no se aplica tras editarla y 5 formas correctas de montarla
  • La ruta completa de configuración HTTPS, desde certificados autofirmados hasta Let’s Encrypt
  • Trucos de configuración de red al hacer proxy inverso a otros contenedores Docker
  • Endurecimiento de seguridad y ajuste de rendimiento en producción

Configuración básica de Docker Nginx y montaje de archivos

¿Por qué montar la configuración?

Quizá te preguntes: ¿no basta empaquetar la configuración de Nginx en la imagen? Sí, pero…

Cada cambio de configuración implica reconstruir la imagen, subirla al registro, volver a descargarla y reiniciar el contenedor. El proceso completo puede llevar más de diez minutos. Y ni hablemos de gestión de versiones de configuración o cambio entre entornos. Si a las dos de la madrugada hay un incidente en producción y quieres revertir la configuración rápido, ¿esperar a que termine el build? Mejor no pensarlo.

Montar la configuración resuelve ese dolor: modificas el archivo en el host y el contenedor lo refleja al instante. Pruebas, rollback y trabajo en equipo resultan mucho más sencillos.

Estructura de directorios clave del contenedor Nginx

Primero, ubica dónde vive todo dentro del contenedor:

/etc/nginx/
├── nginx.conf              # Configuración principal
├── conf.d/                 # Directorio de subconfiguraciones, un sitio por archivo
│   └── default.conf        # Configuración del sitio por defecto
/usr/share/nginx/html       # Raíz de archivos estáticos
/var/log/nginx/             # Directorio de logs
    ├── access.log
    └── error.log

Punto clave: en nginx.conf hay una línea include /etc/nginx/conf.d/*.conf;. Eso significa que la configuración principal carga automáticamente todos los .conf de conf.d. Si solo montas nginx.conf y olvidas conf.d, acabas de encontrar la primera trampa.

Montaje correcto: paso a paso

No arranques el contenedor todavía. Un poco de preparación te ahorrará muchos dolores de cabeza.

Paso 1: copiar la configuración por defecto del contenedor como plantilla

¿Por qué? La configuración oficial por defecto está probada; partir de ella es más fiable que escribir desde cero.

# Arranca un contenedor temporal
docker run --name nginx-temp -d nginx

# Copia la configuración por defecto
docker cp nginx-temp:/etc/nginx/nginx.conf ./nginx/nginx.conf
docker cp nginx-temp:/etc/nginx/conf.d ./nginx/conf.d

# Elimínalo cuando termines
docker stop nginx-temp && docker rm nginx-temp

Paso 2: crear la estructura de directorios estándar en el host

Mi costumbre es guardar todo lo relacionado con Docker bajo /opt; puedes ajustarlo a tu gusto:

mkdir -p /opt/nginx/{conf,conf.d,html,logs,ssl}

Esta estructura cubre configuración, archivos estáticos, logs y certificados SSL de un solo golpe.

Paso 3: arrancar el contenedor y montar todos los directorios

Aquí está lo importante. Fíjate en cada mapeo de rutas tras el parámetro -v:

docker run -d --name my-nginx \
  -p 80:80 -p 443:443 \
  -v /opt/nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v /opt/nginx/conf.d:/etc/nginx/conf.d \
  -v /opt/nginx/html:/usr/share/nginx/html \
  -v /opt/nginx/logs:/var/log/nginx \
  -v /opt/nginx/ssl:/etc/nginx/ssl \
  nginx

Puntos clave:

  • -p 80:80 -p 443:443: expone HTTP y HTTPS (lo necesitarás para HTTPS más adelante)
  • Tras nginx.conf va :ro (montaje de solo lectura), para evitar que procesos dentro del contenedor modifiquen la configuración
  • conf.d se monta como directorio completo, no como archivo suelto (esto es crucial; verás por qué abajo)

Cuatro trampas que quizá te esperan

Trampa 1: editar con vim y que el contenedor no se sincronice

Fue la primera que me pilló. Editaba nginx.conf en el host, reiniciaba el contenedor y no pasaba nada.

Causa: vim puede cambiar el inode del archivo al editar. Docker monta por inode; si el inode cambia, el contenedor sigue viendo el archivo antiguo.

Dos soluciones:

  • Opción A: usar nano (no cambia el inode)
  • Opción B: montar el directorio completo en lugar de un solo archivo

Yo uso siempre la opción B. Con montaje de directorio, da igual cómo cambie el inode; además puedes añadir o quitar archivos de configuración con flexibilidad.

Trampa 2: ruta incorrecta en include

Entra al contenedor y revisa tu nginx.conf:

docker exec -it my-nginx bash
cat /etc/nginx/nginx.conf | grep include

Asegúrate de que exista esta línea:

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

Si la cambias a /opt/nginx/conf.d/*.conf (ruta del host), está mal. Dentro del contenedor solo valen rutas del contenedor.

Trampa 3: olvidar montar conf.d

Montar solo nginx.conf no basta. Si añades un sitio nuevo en conf.d del host, el contenedor no lo verá.

Compruébalo así:

docker exec my-nginx ls /etc/nginx/conf.d

Deberías ver todos los archivos de /opt/nginx/conf.d en el host.

Trampa 4: permisos que impiden a Nginx leer la configuración

En Linux pasa a menudo. Permisos demasiado restrictivos y el proceso Nginx (usuario nginx) no puede leer.

Solución:

chmod 644 /opt/nginx/conf/nginx.conf
chmod 644 /opt/nginx/conf.d/*.conf

Tras cambiar la configuración, acostúmbrate a probar la sintaxis:

docker exec my-nginx nginx -t

Si ves syntax is ok, puedes respirar tranquilo.

Montaje de archivo único vs directorio: ¿cuál elegir?

Es una pregunta frecuente. Mi recomendación:

nginx.conf principal: puede ir montado como archivo único + solo lectura (:ro), porque casi no cambia

Directorio conf.d: hay que montarlo como directorio, para añadir sitios con flexibilidad

html, logs: montaje de directorio, sin más

Regla general: si gestionas varios archivos con flexibilidad, monta el directorio; si quieres control de versiones sobre un archivo central, montaje de archivo único con protección de solo lectura.

Configuración HTTPS y renovación automática con Let’s Encrypt

Certificado autofirmado: validación rápida en desarrollo

En producción hace falta un certificado válido, pero en desarrollo y pruebas un autofirmado basta. Unos comandos y listo:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /opt/nginx/ssl/nginx.key \
  -out /opt/nginx/ssl/nginx.crt \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=Dev/CN=localhost"

Genera dos archivos:

  • nginx.key: clave privada, no la compartas
  • nginx.crt: certificado

Luego crea ssl.conf en /opt/nginx/conf.d/:

server {
    listen 443 ssl;
    server_name localhost;

    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}

Puntos clave:

  • Las rutas de ssl_certificate son del contenedor (/etc/nginx/ssl), no del host (/opt/nginx/ssl)
  • ssl_protocols solo TLS 1.2 y 1.3; protocolos antiguos tienen vulnerabilidades
  • Al arrancar el contenedor no olvides -p 443:443, o HTTPS no estará expuesto

Recarga en caliente:

docker exec my-nginx nginx -s reload

Visita https://localhost; el navegador avisará de que el certificado no es de confianza. Es normal en autofirmados: continúa y ya.

Let’s Encrypt: certificados gratuitos en producción

Let’s Encrypt es una maravilla: gratis, automatizable y confiable globalmente. El único “pero” es que caduca a los 90 días, pero con renovación automática no es problema.

Recomiendo docker-compose + contenedor certbot; es más fiable que hacerlo a mano.

Primero el docker-compose.yml completo:

version: '3'

services:
  nginx:
    image: nginx:latest
    container_name: my-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/html:/usr/share/nginx/html
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
    networks:
      - web

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
    networks:
      - web

networks:
  web:
    driver: bridge

Puntos clave:

1. Montaje compartido de certificados y archivos de validación

  • ./certbot/conf montado en ambos contenedores: certbot genera, nginx lee
  • ./certbot/www para la validación HTTP de Let’s Encrypt (método webroot)

2. Nginx hace reload cada 6 horas

La línea parece compleja, pero solo lanza un bucle en segundo plano que recarga la configuración cada 6 horas para que los certificados renovados surtan efecto al instante.

3. Certbot comprueba la renovación cada 12 horas

Let’s Encrypt recomienda comprobar una vez al día; aquí 12 horas da más margen.

Pasos para solicitar el certificado por primera vez

En /opt/nginx/conf.d/ crea primero una configuración temporal temp.conf para la validación HTTP:

server {
    listen 80;
    server_name your-domain.com;  # Cambia por tu dominio

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

    location / {
        return 301 https://$host$request_uri;
    }
}

Arranca los servicios:

docker-compose up -d

Solicita el certificado (sustituye your-domain.com y [email protected]):

docker-compose run --rm certbot certonly --webroot \
  -w /var/www/certbot \
  -d your-domain.com \
  --email [email protected] \
  --agree-tos \
  --no-eff-email

Si todo va bien verás “Congratulations!”. Los certificados estarán en ./certbot/conf/live/your-domain.com/.

Ya puedes crear la configuración HTTPS definitiva. Modifica temp.conf a:

server {
    listen 80;
    server_name your-domain.com;

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

    location / {
        return 301 https://$host$request_uri;
    }
}

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

    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    # Optimización SSL
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers on;

    # HSTS (forzar HTTPS)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}

Recarga la configuración:

docker-compose exec nginx nginx -s reload

Verificar la renovación automática

Los certificados de Let’s Encrypt duran 90 días, pero certbot renueva automáticamente cuando quedan 30. Puedes probarlo manualmente:

docker-compose run --rm certbot renew --dry-run

Si ves “The dry run was successful”, está bien.

También puedes revisar los logs de certbot:

docker-compose logs certbot

Extras de seguridad en HTTPS

Si quieres endurecer más la configuración SSL, añade esto:

# OCSP Stapling (comprobación en línea del estado del certificado)
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/your-domain.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# Prohibir iframe (anti clickjacking)
add_header X-Frame-Options DENY;

# Anti XSS
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

Después prueba tu SSL en SSL Labs. Conseguir A+ no es difícil.

Proxy inverso y comunicación entre contenedores Docker

Lo que hay que saber de la red de contenedores

Al usar Nginx como proxy de otros contenedores Docker, lo que más duele suele ser la red. Quizá probaste la IP del contenedor directamente y, tras un reinicio, cambió y toca reconfigurar.

Docker tiene varios modos de red; los más usados son:

  • bridge: modo por defecto; los contenedores se hablan por un puente virtual
  • host: el contenedor usa la pila de red del host; mejor rendimiento, peor aislamiento

Para proxy inverso con Nginx, recomiendo encarecidamente una red bridge personalizada. ¿Por qué?

  1. Los contenedores se hablan por nombre de servicio, sin depender de IPs que cambian
  2. Aislamiento: los contenedores de un proyecto forman un entorno de red propio
  3. Resolución DNS automática con el service discovery integrado de Docker

Crear una red personalizada y ejecutar contenedores

Primero crea la red:

docker network create my-app-network

Arranca tu backend (supongamos una API Node.js):

docker run -d \
  --name backend-api \
  --network my-app-network \
  -e NODE_ENV=production \
  my-backend:latest

Arranca Nginx en la misma red:

docker run -d \
  --name my-nginx \
  --network my-app-network \
  -p 80:80 -p 443:443 \
  -v /opt/nginx/conf.d:/etc/nginx/conf.d \
  nginx

Clave: en la misma red, en la configuración de Nginx puedes usar directamente el nombre backend-api para llegar al backend.

Configuración práctica de proxy inverso en Nginx

Crea api-proxy.conf en /opt/nginx/conf.d/:

upstream backend {
    server backend-api:3000;  # nombre-contenedor:puerto
    keepalive 32;
}

server {
    listen 80;
    server_name api.example.com;

    # Proxy de la API
    location /api/ {
        proxy_pass http://backend/;

        # Pasar información real del cliente
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Soporte WebSocket (si hace falta)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # Archivos estáticos servidos directamente por Nginx
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
}

Detalles fáciles de pasar por alto:

1. upstream y el manejo de rutas en proxy_pass

Fíjate en la barra final de proxy_pass http://backend/;. Con ella, /api/users se reenvía como http://backend/users (sin el prefijo /api).

Si escribes proxy_pass http://backend; (sin barra), se reenvía como http://backend/api/users (ruta completa).

2. Importancia del header X-Forwarded-For

Si el backend necesita la IP real del cliente, depende de este header. Sin él, solo verá la IP del contenedor Nginx.

3. Pool keepalive

keepalive 32 en upstream reutiliza conexiones TCP y reduce el coste del handshake. Muy útil con alta concurrencia.

Simplificar varios contenedores con docker-compose

Crear la red y arrancar contenedor a contenedor cansa. Con docker-compose, de un golpe:

version: '3.8'

services:
  backend:
    image: my-backend:latest
    container_name: backend-api
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://db:5432/mydb
    networks:
      - app-network
    depends_on:
      - db

  nginx:
    image: nginx:latest
    container_name: my-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/html:/usr/share/nginx/html
      - ./nginx/logs:/var/log/nginx
    networks:
      - app-network
    depends_on:
      - backend

  db:
    image: postgres:14
    container_name: postgres-db
    environment:
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=mydb
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  db-data:

Un comando levanta todo el stack:

docker-compose up -d

Docker automáticamente:

  • Crea la red app-network
  • Arranca contenedores según depends_on
  • Configura resolución DNS entre contenedores

En la configuración de Nginx puedes usar backend y db como hostnames. Muy cómodo.

Rutina de diagnóstico de problemas de proxy

Problema 1: 502 Bad Gateway

El error más común. Causas habituales:

  • El backend no está arrancado o se cayó
  • Nginx y el backend no están en la misma red
  • El backend escucha en un puerto distinto

Pasos de diagnóstico:

# Comprobar si el backend corre
docker ps | grep backend

# Revisar la red
docker network inspect my-app-network

# Entrar al contenedor Nginx y probar conectividad
docker exec -it my-nginx sh
ping backend-api
curl http://backend-api:3000/health

Si ping falla, hay un problema de red. Si curl hace timeout, el backend no escucha bien.

Problema 2: timeout de peticiones

El timeout por defecto de proxy es 60 segundos. Si tu API tarda más (subida grande, cálculo pesado), sube los timeouts:

location /api/long-running/ {
    proxy_pass http://backend/;
    proxy_connect_timeout 300s;
    proxy_send_timeout 300s;
    proxy_read_timeout 300s;
}

Problema 3: cuerpo de POST perdido

A veces un POST llega al backend como GET o sin cuerpo. Revisa:

location /api/ {
    proxy_pass http://backend/;
    proxy_request_buffering off;  # Desactivar buffering de petición
    client_max_body_size 100M;     # Permitir subidas grandes
}

Balanceo de carga con varios backends

Si tienes varias instancias del backend, Nginx puede repartir la carga:

upstream backend_cluster {
    least_conn;  # Algoritmo de menor número de conexiones

    server backend-1:3000 weight=3;  # Peso 3
    server backend-2:3000 weight=1;  # Peso 1
    server backend-3:3000 backup;     # Servidor de respaldo

    keepalive 32;
}

server {
    listen 80;

    location /api/ {
        proxy_pass http://backend_cluster/;
        # ... resto de ajustes proxy
    }
}

Algoritmos de balanceo:

  • round-robin (por defecto): reparto circular
  • least_conn: prioriza el nodo con menos conexiones
  • ip_hash: la misma IP de cliente va siempre al mismo servidor

Si un backend cae, Nginx desvía el tráfico a los nodos sanos.

Mejores prácticas y optimización de rendimiento en producción

Gestión de configuración: no subas a producción a lo bruto

Dejar la configuración suelta en el servidor es cosa de desarrollo. En producción conviene más orden.

Gestionar la configuración con Git

Yo uso un repositorio aparte para toda la configuración de Nginx, con carpetas por entorno. Ventajas claras:

  • Historial de versiones visible; rollback con git checkout
  • Colaboración en equipo con PR review de cambios
  • Integración CI/CD para despliegue automatizado

Plantillas de configuración

La configuración de distintos entornos se parece mucho. Plantillas + sustitución de variables ahorran trabajo. En el despliegue, envsubst basta.

Gestión de logs: no esperes a que el disco se llene

Nginx escribe logs rápido; en sitios con mucho tráfico varios GB al día no es raro. Sin control, un disco lleno tumba el contenedor.

Rotación de logs

Configura logrotate en el host: rotación diaria, retención 14 días, compresión de logs antiguos. Tras rotar hay que pedir a Nginx que reabra los archivos (nginx -s reopen), o seguirá escribiendo en el archivo viejo.

Logs centralizados

Con varios servidores, envía logs a una plataforma central (ELK, Loki, servicios cloud). Drivers de logs de Docker o Promtail lo simplifican.

Recarga elegante: que el usuario no lo note

Tras cambiar la configuración, no hagas docker restart a la ligera. Reiniciar corta todas las conexiones y pierdes peticiones en curso.

La forma correcta:

# Probar sintaxis primero
docker exec my-nginx nginx -t

# Si está bien, reload
docker exec my-nginx nginx -s reload

reload es recarga elegante: nuevos workers cargan la configuración nueva, los viejos terminan las peticiones actuales y salen. Transición sin cortes visibles para el usuario.

Lista de endurecimiento de seguridad

Antes de producción, revisa estos puntos:

1. Ocultar la versión de Nginx

Por defecto las páginas de error muestran la versión. Un atacante puede explotar vulnerabilidades conocidas. En el bloque http añade server_tokens off;.

2. Limitación de tasa anti DDoS

Protección simple y efectiva:

http {
    # Limitar frecuencia de peticiones por IP
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;

    # Limitar conexiones concurrentes
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}

server {
    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        limit_conn conn_limit 10;
        proxy_pass http://backend/;
    }
}

3. Montaje de configuración en solo lectura

Añade :ro a la configuración principal para evitar cambios accidentales dentro del contenedor.

4. Principio de mínimo privilegio

Asegúrate de que nginx.conf tenga user nginx;. No lo cambies a root; el riesgo es enorme.

Ajuste de rendimiento: exprimir Nginx

Optimización de procesos worker

worker_processes auto;  # Ajuste automático al número de núcleos CPU
worker_cpu_affinity auto;  # Afinidad de CPU

auto es cómodo: Nginx detecta los núcleos solo.

Optimización de conexiones

events {
    worker_connections 2048;  # Máximo de conexiones por worker
    use epoll;                # En Linux epoll rinde mejor
}

Concurrencia teórica máxima: worker_processes * worker_connections. En la práctica también cuenta el límite de descriptores (ulimit -n).

Compresión gzip

Comprimir texto puede reducir el tráfico un 60-80 %:

http {
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;  # Nivel 1-9; 6 equilibra rendimiento y ratio
    gzip_types
        text/plain
        text/css
        text/xml
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;
    gzip_min_length 1000;  # Archivos menores de 1 KB no compensa comprimir
}

Caché de recursos estáticos

location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
    expires 30d;  # Caché del navegador 30 días
    add_header Cache-Control "public, immutable";
}

Soporte HTTP/2

HTTP/2 mejora mucho el rendimiento; activarlo es sencillo:

server {
    listen 443 ssl http2;  # Añade http2
    # ... resto de configuración
}

Requisito: HTTPS; HTTP/2 va sobre TLS.

Monitorización y health checks

No olvides la monitorización. Prometheus + Grafana con nginx-prometheus-exporter exportando métricas funciona bien. Activa stub_status en Nginx, limita el acceso a la red interna de Docker, deja que Prometheus scrapee y Grafana muestre el panel.

Conclusión

En resumen, cuatro pilares:

Montaje de configuración: directorios mejor que archivos sueltos, configuración principal en solo lectura, no olvides conf.d y ssl. Evita la trampa del inode con vim.

HTTPS: autofirmado en desarrollo para validar rápido; Let’s Encrypt + Certbot automatizado en producción. docker-compose simplifica la renovación.

Proxy inverso: red bridge personalizada y comunicación por nombre de contenedor. upstream con keepalive mejora rendimiento y fiabilidad.

Práctica en producción: versiona la configuración con Git, rota logs, reload elegante en lugar de restart, y no te saltes las medidas de seguridad.

Docker Nginx parece simple, pero los detalles importan. Si lo dominas, montarás una arquitectura web estable de desarrollo a producción.

Si este artículo te evitó alguna trampa o resolvió un problema antiguo, ya valió la pena. Pruébalo; si te atascas, comenta y lo vemos.

Un último consejo: guarda las plantillas de configuración de este artículo y reutilízalas. No reinventes la rueda; el tiempo que ahorres lo puedes invertir en otras cosas.

Flujo completo para desplegar Nginx con Docker

Montaje de configuración, HTTPS y proxy inverso en la práctica, solucionando problemas habituales como configuración que no se aplica y comunicación entre contenedores

⏱️ Estimated time: 1 hr

  1. 1

    Step 1: Montaje de configuración: 5 formas correctas

    5 formas correctas de montar la configuración:
    • Montar el archivo de configuración: -v ./nginx.conf:/etc/nginx/nginx.conf
    • Montar el directorio de configuración: -v ./conf.d:/etc/nginx/conf.d
    • Usar volúmenes (Volume)
    • Configurar con docker-compose
    • Usar ConfigMap (entorno K8s)

    Problemas habituales:
    • La configuración no se aplica tras editarla: cambias nginx.conf y el contenedor sigue con la configuración antigua
    • Hay que montar correctamente los archivos con el parámetro -v
    • Asegúrate de que el formato del archivo sea correcto

    Mejores prácticas:
    • Montar directorios es mejor que montar un solo archivo
    • Proteger la configuración principal en solo lectura
    • No olvides los directorios conf.d y ssl
    • Evita la trampa del inode con vim
  2. 2

    Step 2: Configuración HTTPS y renovación automática con Let's Encrypt

    Configuración HTTPS:
    • Usar certificados gratuitos de Let's Encrypt
    • Configurar renovación automática: certbot renew
    • Montar el directorio de certificados: -v ./certs:/etc/nginx/certs
    • Configurar las rutas de los certificados SSL
    • Soportar HTTP/2 y TLS 1.3

    Configuración de Let's Encrypt:
    • Obtener certificados con certbot: certbot certonly --standalone
    • Probar renovación automática: certbot renew --dry-run
    • Montar certificados en el contenedor: -v ./letsencrypt:/etc/letsencrypt
    • Configurar nginx.conf para usar los certificados
  3. 3

    Step 3: Proxy inverso y mejores prácticas para producción

    Configuración de proxy inverso:
    • Configurar upstream apuntando al contenedor backend
    • Usar nombre de contenedor o servicio: proxy_pass http://backend:8080
    • Configurar health checks
    • Gestionar problemas de CORS
    • Configurar balanceo de carga

    Mejores prácticas para producción:
    • Gestionar múltiples contenedores con docker-compose
    • Configurar health checks
    • Establecer límites de recursos
    • Configurar rotación de logs
    • Gestionar la configuración con variables de entorno
    • Actualizar imágenes con regularidad

    Docker Nginx parece sencillo, pero tiene muchos detalles. Si dominas todo esto, podrás montar una arquitectura web estable y fiable de desarrollo a producción.

FAQ

¿Por qué la configuración de Docker Nginx no se aplica tras editarla?
Problema habitual: la configuración no se aplica tras editarla. Cambias nginx.conf y el contenedor sigue con la configuración antigua. Hay que montar correctamente los archivos con el parámetro -v y asegurarse de que el formato sea correcto.

5 formas correctas de montar la configuración:
1) Montar el archivo: -v ./nginx.conf:/etc/nginx/nginx.conf
2) Montar el directorio: -v ./conf.d:/etc/nginx/conf.d
3) Usar volúmenes (Volume)
4) Configurar con docker-compose
5) Usar ConfigMap (entorno K8s)

Mejores prácticas:
• Montar directorios es mejor que montar un solo archivo
• Proteger la configuración principal en solo lectura
• No olvides los directorios conf.d y ssl
• Evita la trampa del inode con vim
¿Cómo configurar HTTPS en Docker Nginx?
Configuración HTTPS:
• Usar certificados gratuitos de Let's Encrypt
• Configurar renovación automática: certbot renew
• Montar el directorio de certificados: -v ./certs:/etc/nginx/certs
• Configurar las rutas de los certificados SSL
• Soportar HTTP/2 y TLS 1.3

Configuración de Let's Encrypt:
• Obtener certificados con certbot: certbot certonly --standalone
• Probar renovación automática: certbot renew --dry-run
• Montar certificados en el contenedor: -v ./letsencrypt:/etc/letsencrypt
• Configurar nginx.conf para usar los certificados
¿Cómo configurar el proxy inverso en Docker Nginx?
Configuración de proxy inverso:
• Configurar upstream apuntando al contenedor backend
• Usar nombre de contenedor o servicio: proxy_pass http://backend:8080
• Configurar health checks
• Gestionar problemas de CORS
• Configurar balanceo de carga

Comunicación entre contenedores:
• Crear una red personalizada con docker-compose
• Asegurarse de que los contenedores estén en la misma red
• Acceder con el nombre del servicio o del contenedor
• Configurar upstream apuntando al servicio backend
¿Cuáles son las mejores prácticas de Docker Nginx en producción?
Mejores prácticas para producción:
• Gestionar múltiples contenedores con docker-compose
• Configurar health checks
• Establecer límites de recursos
• Configurar rotación de logs
• Gestionar la configuración con variables de entorno
• Actualizar imágenes con regularidad

Docker Nginx parece sencillo, pero tiene muchos detalles. Si dominas todo esto, podrás montar una arquitectura web estable y fiable de desarrollo a producción.

Consejo: guarda las plantillas de configuración de este artículo y reutilízalas la próxima vez, sin reinventar la rueda.

15 min de lectura · Publicado el: 18 dic 2025 · Actualizado el: 21 ago 2026

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog