Guía Paso a Paso: Servidor Multi-Dominio con DuckDNS y Apache

DuckDNS Apache2 Docker SSL/Let's Encrypt

Objetivo de esta guía: Explicar desde cero cómo registrar dominios gratuitos, automatizar la actualización de la IP pública sin depender del router, y enrutar correctamente el tráfico web hacia contenedores Docker independientes utilizando Apache como Proxy Inverso.

1. Registro de Dominios en DuckDNS

DuckDNS es un servicio de DNS Dinámico (DDNS) gratuito y sin caducidad mensual. Para empezar, necesitamos vincular nuestra IP pública a nombres de dominio legibles.

  1. Acceso: Entra en duckdns.org.
  2. Autenticación Segura: Inicia sesión utilizando un proveedor OAuth. Recomendamos encarecidamente usar GitHub para mantener tu infraestructura vinculada a tu perfil de desarrollador y garantizar un acceso seguro con autenticación en dos pasos (2FA).
  3. Límites: Una cuenta gratuita te permite registrar hasta 5 dominios (subdominios de .duckdns.org).
  4. Creación: En la casilla "sub domain", escribe el nombre deseado y pulsa "add domain". Para esta arquitectura crearemos tres:
    • pi5appserver.duckdns.org (Para nuestro Dashboard web HTML).
    • pi5odoo.duckdns.org (Para nuestro contenedor de Odoo).
    • levelup42.duckdns.org (Para nuestra futura API de Spring Boot).
  5. El Token de Seguridad: En la parte superior de la página principal, verás un campo llamado Token (una cadena larga de números y letras, ej. a1b2c3d4...). Cópialo; es la clave maestra que usará nuestro servidor para identificarse.

2. Entendiendo el Proxy Inverso y los Puertos (Localhost)

¿Por qué usamos direcciones como localhost:8069 en nuestra configuración? La respuesta está en la contenedorización (Docker).

Concepto Clave: Cuando desplegamos una aplicación en Docker, esta se ejecuta de forma aislada. Odoo, por defecto, escucha en el puerto 8069 dentro de su contenedor. Spring Boot escucha en el 8080.

A través de Docker, "mapeamos" esos puertos internos hacia nuestra Raspberry Pi host (localhost). Como no es seguro ni estético obligar al usuario final a escribir números de puerto en su navegador, colocamos un Proxy Inverso (Apache) en la puerta principal (puertos 80/443). Apache recibe el tráfico de los dominios limpios y lo redirige internamente al puerto de localhost correspondiente.

graph LR User((Cliente Web)) -- HTTPS/443 --> Apache[Apache Reverse Proxy] subgraph "Raspberry Pi 5 Server" Apache -- pi5appserver.duckdns.org --> HTML[Directorio Web /var/www/html] Apache -- pi5odoo.duckdns.org --> Odoo[Docker Odoo :8069] Apache -- levelup42.duckdns.org --> Java[Docker Spring Boot :8080] end style Apache fill:#ffecb3,stroke:#fbc02d,stroke-width:2px style HTML fill:#c8e6c9,stroke:#388e3c,stroke-width:2px style Odoo fill:#e1bee7,stroke:#714b67,stroke-width:2px style Java fill:#ffcc80,stroke:#e65100,stroke-width:2px

3. Automatización de la IP (Script Cron)

Dado que muchos routers bloquean o no incluyen DuckDNS, configuraremos la propia Raspberry Pi para que actualice la IP pública automáticamente cada 5 minutos.

# 1. Crear directorio y script de actualización
mkdir ~/duckdns && cd ~/duckdns
nano duck.sh
# Contenido del script (Sustituye TU_TOKEN_SECRETO por el Token de la web): # echo url="https://www.duckdns.org/update?domains=pi5appserver,pi5odoo,levelup42&token=TU_TOKEN_SECRETO&ip=" | curl -k -o ~/duckdns/duck.log -K - # 2. Dar permisos de ejecución al script
chmod 700 duck.sh
# 3. Añadir a Cron para que se ejecute silenciosamente cada 5 minutos
crontab -e
# Añade esta línea al final del archivo: # */5 * * * * ~/duckdns/duck.sh >/dev/null 2>&1

3.1. Verificación de Conexión (Logs)

Una vez creado el script y programada la tarea, es fundamental realizar una prueba manual para comprobar que nuestra Raspberry Pi se está comunicando correctamente con los servidores de DuckDNS.

# 1. Ejecutamos el script manualmente por primera vez
~/duckdns/duck.sh
# 2. Leemos la respuesta guardada en el archivo log
cat ~/duckdns/duck.log
✅ Si el log responde "OK": ¡Éxito absoluto! Tu IP pública se ha sincronizado correctamente. La Raspberry Pi ya es autónoma y mantendrá tus dominios actualizados en segundo plano sin que tengas que volver a preocuparte por tu router.
❌ Si el log responde "KO": La petición ha sido rechazada por DuckDNS. Pasos para solucionarlo:

4. Configuración de VirtualHosts en Apache

Debemos crear un archivo de configuración para cada servicio en /etc/apache2/sites-available/.

4.1. Dashboard Principal (Página Estática HTML)

📄 /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80> ServerName pi5appserver.duckdns.org DocumentRoot /var/www/html ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined </VirtualHost>

4.2. Contenedor de Odoo

📄 /etc/apache2/sites-available/odoo-docker.conf
<VirtualHost *:80> ServerName pi5odoo.duckdns.org ProxyPreserveHost On ProxyRequests Off # Redirección al puerto local expuesto por Docker ProxyPass / http://localhost:8069/ ProxyPassReverse / http://localhost:8069/ RequestHeader set "X-Forwarded-Proto" "https" </VirtualHost>

4.3. Contenedor de Spring Boot (API Java)

📄 /etc/apache2/sites-available/api-spring.conf
<VirtualHost *:80> ServerName levelup42.duckdns.org ProxyPreserveHost On ProxyRequests Off # Redirección al puerto local expuesto por Docker ProxyPass / http://localhost:8080/ ProxyPassReverse / http://localhost:8080/ RequestHeader set "X-Forwarded-Proto" "https" </VirtualHost>

5. Activación y Seguridad SSL (HTTPS)

Finalmente, activamos los sitios y utilizamos Let's Encrypt mediante Certbot para generar certificados de seguridad gratuitos, forzando la redirección de todo el tráfico a HTTPS.

# 1. Habilitar sitios y recargar Apache
sudo a2ensite odoo-docker.conf api-spring.conf
sudo systemctl reload apache2
# 2. Generar certificados SSL (Elige "Redireccionar" cuando pregunte)
sudo certbot --apache -d pi5appserver.duckdns.org
sudo certbot --apache -d pi5odoo.duckdns.org
sudo certbot --apache -d levelup42.duckdns.org

6. Preparación del Entorno (VS Code Remote SSH)

Para poder editar el código de nuestro Dashboard directamente desde Visual Studio Code sin problemas de permisos (evitando usar sudo), ajustamos la propiedad de la carpeta web.

# Otorgar propiedad del directorio al usuario activo, manteniendo al grupo de Apache (www-data)
sudo chown -R $USER:www-data /var/www/html
# Establecer permisos: Lectura/Escritura para el dueño y grupo; solo lectura para otros
sudo chmod -R 775 /var/www/html