Saltar al contenido principal

Despliegue en servidor Debian con Docker

Guía para desplegar Xenpia Chatbot en un servidor Debian usando Docker y Docker Compose.

Requisitos​

  • Servidor Debian 11 o 12 (o derivados como Ubuntu 22.04+)
  • Acceso SSH con usuario con permisos
  • Dominio o IP pública (recomendado: dominio con HTTPS)

1. Instalar Docker y Docker Compose en Debian​

# Actualizar e instalar dependencias
apt update && apt install -y ca-certificates curl gnupg

# Añadir clave y repo oficial de Docker
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

# Instalar Docker Engine y Compose plugin
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Comprobar
docker --version
docker compose version

# (Opcional) Evitar usar para docker
usermod -aG docker $USER
# Cerrar sesión y volver a entrar para que aplique

2. Clonar el proyecto y configurar variables​

# En el directorio donde quieras el proyecto (ej: /opt o ~)
git clone <url-del-repositorio> xenpia-chatbot
cd xenpia-chatbot

# Crear .env desde la plantilla
cp .env.example .env

# Editar .env con tus valores (URLs públicas, Supabase, Meta, OpenAI, etc.)
nano .env

Script de despliegue (opcional)​

Puedes usar el script deploy.sh para arrancar o actualizar:

./deploy.sh # Levanta; construye solo si hace falta
./deploy.sh --build # Fuerza reconstrucción de imágenes (tras cambiar .env o código)
./deploy.sh --logs # Levanta y muestra logs en tiempo real
./deploy.sh --stop # Para los contenedores

La primera vez que no exista .env, el script lo creará desde .env.example y te pedirá editarlo antes de continuar.

Importante: En .env debes definir las URLs públicas que usará el navegador:

  • NEXT_PUBLIC_SERVER_URL: URL base de la app (ej. https://app.tudominio.com)
  • NEXT_PUBLIC_BACKEND_URL y NEXT_PUBLIC_ORCHESTRATOR_URL: URL del API (ej. https://api.tudominio.com o http://IP_DEL_SERVIDOR:3000 si no usas proxy)

Si accedes por IP sin dominio, puedes usar por ejemplo:

  • NEXT_PUBLIC_SERVER_URL=http://TU_IP:3001
  • NEXT_PUBLIC_BACKEND_URL=http://TU_IP:3000
  • NEXT_PUBLIC_ORCHESTRATOR_URL=http://TU_IP:3000

3. Construir y levantar los contenedores​

# Construir imágenes y arrancar en segundo plano
docker compose -f docker-compose.yml up -d --build

# Ver estado
docker compose -f docker-compose.yml ps
docker compose -f docker-compose.yml logs -f

Servicios y puertos:

ServicioPuertoDescripción
backend3000API y orquestador
frontend3001Aplicación Next.js
redis6379Solo localhost (red interna)

4. Probar el despliegue​

  • Frontend: http://TU_IP:3001 o https://app.tudominio.com
  • Backend (health): http://TU_IP:3000 (o la ruta que expongas)

4.1 WhatsApp Embedded Signup (Meta)​

"Conectar WhatsApp" abre la ventana de Meta con el SDK de JavaScript (FB.login), sin redirect URI: al terminar, Meta devuelve un code a la propia página y el backend lo canjea en menos de 30 s. Por eso ya no existe la ruta /oauth/whatsapp-embedded ni la variable NEXT_PUBLIC_META_OAUTH_REDIRECT_URI.

En Meta for Developers → la app de Xenpia:

  1. Settings → Basic → App Domains: el dominio del panel (ej. app.tudominio.com).
  2. Facebook Login for Business → Settings: activar Login with the JavaScript SDK y añadir el dominio HTTPS del panel en Allowed Domains for the JavaScript SDK. El SDK no funciona por http:// (tampoco en local: ver docs/interno/empezar/entorno-local.md).
  3. Facebook Login for Business → Configurations: una configuración de tipo WhatsApp Embedded Signup en la versión v4 (la v2 deja de funcionar el 15-oct-2026), con los permisos whatsapp_business_management y whatsapp_business_messaging. Su ID va en NEXT_PUBLIC_META_EMBEDDED_SIGNUP_CONFIG_ID. Instagram y Messenger tienen las suyas (NEXT_PUBLIC_META_INSTAGRAM_SIGNUP_CONFIG_ID, NEXT_PUBLIC_META_MESSENGER_SIGNUP_CONFIG_ID).
  4. WhatsApp → Configuration → Webhook: URL https://api.tudominio.com/api/webhook/whatsapp con META_VERIFY_TOKEN, suscrita a los campos messages, history, smb_app_state_sync, smb_message_echoes y account_update. Los cuatro últimos son los de la coexistencia (el cliente sigue usando la app WhatsApp Business): sin ellos la conexión funciona, pero no se importa nada ni se ve lo que contestan desde el teléfono.
  5. En la configuración v4, dejar activada la opción de onboarding de usuarios de la app WhatsApp Business. Se comprueba abriendo el asistente: la pantalla de elegir WABA debe ofrecer "conectar tu cuenta existente de WhatsApp Business".

Las variables NEXT_PUBLIC_* se incrustan al compilar: tras cambiarlas, reconstruye el frontend con docker compose -f docker-compose.yml up -d --build.

El backend necesita META_APP_ID y META_APP_SECRET de esta misma app: con el secreto canjea el code y verifica la firma de cada webhook (META_WEBHOOK_SIGNATURE, ver docs/interno/canales/whatsapp.md §7).

5. (Recomendado) Nginx como proxy inverso con HTTPS​

Para servir por dominio y HTTPS con Let's Encrypt:

apt install -y nginx certbot python3-certbot-nginx
certbot --nginx -d app.tudominio.com -d api.tudominio.com

Ejemplo de configuración Nginx (/etc/nginx/sites-available/xenpia):

# Frontend
server {
listen 80;
server_name app.tudominio.com;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
}
}

# Backend
server {
listen 80;
server_name api.tudominio.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
}
}

Habilitar y recargar:

ln -s /etc/nginx/sites-available/xenpia /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Asegúrate de que en .env las URLs usen https://app.tudominio.com y https://api.tudominio.com.

6. Comandos útiles​

# Ver logs
docker compose -f docker-compose.yml logs -f backend
docker compose -f docker-compose.yml logs -f frontend

# Reiniciar un servicio
docker compose -f docker-compose.yml restart backend

# Reconstruir tras cambios
docker compose -f docker-compose.yml up -d --build

# Parar todo
docker compose -f docker-compose.yml down

# Parar y eliminar volúmenes (¡borra datos de Redis!)
docker compose -f docker-compose.yml down -v

7. Actualizar la aplicación​

cd xenpia-chatbot
git pull
docker compose -f docker-compose.yml up -d --build

8. Pipeline Bitbucket (despliegue automático en cada push a dev)​

El proyecto incluye bitbucket-pipelines.yml en la raíz. En cada push a la rama dev, el pipeline se conecta por SSH al servidor y ejecuta git pull + docker compose up -d --build.

8.1 Variables del repositorio en Bitbucket​

En Bitbucket → tu repositorio → Repository settings → Pipelines → Repository variables, crea:

VariableValorSecured
SSH_PRIVATE_KEYContenido completo de la clave privada SSHSí
SERVER_HOSTIP o dominio del servidor (ej. 192.168.1.10 o deploy.tudominio.com)No
SERVER_USERUsuario SSH en el servidor (ej. deploy)No
DEPLOY_PATHRuta del proyecto en el servidor (ej. /opt/xenpia-chatbot)No

Para SSH_PRIVATE_KEY: pega el contenido del archivo de la clave privada (incluidas las líneas -----BEGIN ... KEY----- y -----END ... KEY-----). Marca la variable como Secured para que no aparezca en los logs.

8.2 Clave SSH para que el pipeline se conecte al servidor​

Genera un par de claves solo para el pipeline (en tu máquina o en el servidor):

ssh-keygen -t ed25519 -C "bitbucket-pipeline" -f bitbucket_deploy -N ""
  • Clave pública (bitbucket_deploy.pub): añádela en el servidor Debian en ~/.ssh/authorized_keys del usuario con el que te conectas (ej. deploy):

    # En el servidor, como usuario SERVER_USER (ej. deploy)
    mkdir -p ~/.ssh
    echo "contenido de bitbucket_deploy.pub" >> ~/.ssh/authorized_keys
    chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
  • Clave privada (bitbucket_deploy): copia todo su contenido y pégalo en la variable Secured SSH_PRIVATE_KEY en Bitbucket.

8.3 Deploy key en el servidor para git pull (repositorio privado)​

En el servidor, git pull debe poder acceder al repositorio privado de Bitbucket. Dos opciones:

Opción A – Deploy key del repositorio (recomendada)

  1. En el servidor, genera otra clave SSH solo para Git (si no la tienes):

    ssh-keygen -t ed25519 -C "server-deploy" -f ~/.ssh/bitbucket_deploy_key -N ""
    cat ~/.ssh/bitbucket_deploy_key.pub
  2. En Bitbucket → tu repositorio → Repository settings → Access keys → Add key: pega la clave pública y dale Read (solo lectura).

  3. En el servidor, configura Git para usar esa clave con Bitbucket. Crea o edita ~/.ssh/config (como el usuario que ejecuta el pipeline, ej. deploy):

    Host bitbucket.org
    IdentityFile ~/.ssh/bitbucket_deploy_key
    IdentitiesOnly yes
  4. Comprueba desde el servidor: ssh -T [email protected] (debe identificar el repo sin pedir contraseña).

Opción B – App password (HTTPS)

Si clonaste el repo por HTTPS, en el servidor puedes usar un App password de Bitbucket y configurar la URL con usuario y contraseña (o un credential helper). Es más frágil que la deploy key.

8.4 Activar Pipelines​

En Bitbucket → Repository settings → Pipelines → Settings: activa Enable Pipelines. Asegúrate de que el archivo bitbucket-pipelines.yml está en la raíz del repositorio.

Tras un push a dev, el pipeline se ejecutará y desplegará en el servidor.

8.5 Usar runner auto-hospedado (opcional)​

Si ya tienes un runner instalado en el propio servidor (como en frontend/bitbucket-pipelines.yml), puedes hacer que el pipeline se ejecute ahí y no usar SSH:

  1. En la raíz del repo usa solo el bitbucket-pipelines.yml de la raíz (Bitbucket ignora el de frontend/).
  2. En ese archivo, comenta el step que hace SSH y descomenta el bloque que usa runs-on: self.hosted.
  3. En el runner, el código se hace checkout automáticamente; solo hace falta que exista un .env en la raíz del workspace (puedes crearlo una vez a mano o con un script previo).

Así no necesitas variables SSH_PRIVATE_KEY, SERVER_HOST, etc., ni deploy key en el servidor para Git (el checkout lo hace Bitbucket en el runner).

Solución de problemas​

  • Pipeline falla al conectar por SSH: Comprueba que SERVER_HOST, SERVER_USER y SSH_PRIVATE_KEY son correctos y que la clave pública está en authorized_keys del usuario en el servidor. Prueba desde tu PC: ssh -i bitbucket_deploy SERVER_USER@SERVER_HOST.
  • Pipeline falla en git pull (Permission denied / not found): El servidor necesita acceso de lectura al repo. Configura una deploy key en Bitbucket (Access keys) y en el servidor ~/.ssh/config para usar esa clave con bitbucket.org (ver sección 8.3).
  • Frontend no conecta al backend: Revisa que NEXT_PUBLIC_BACKEND_URL y NEXT_PUBLIC_ORCHESTRATOR_URL en .env sean las URLs que el navegador puede alcanzar (dominio o IP:puerto). Tras cambiar variables que empiezan por NEXT_PUBLIC_, hay que volver a construir: docker compose -f docker-compose.yml up -d --build.
  • Backend no conecta a Redis: En producción, REDIS_HOST=redis y REDIS_PORT=6379 vienen del environment del compose; no hace falta Redis con contraseña salvo que lo configures.
  • Frontend no responde en 3001 (connection reset / 502): El .env define PORT=3000 para el backend y el mismo archivo se usa como env_file del frontend. Sin anular PORT en el servicio frontend, Next.js queda escuchando en 3000 dentro del contenedor y el mapeo 3001:3001 no apunta a ningún proceso. En docker-compose.yml el frontend debe incluir PORT=3001 en environment (ya está así en el repo).
  • Puertos en uso: Cambia en docker-compose.yml el mapeo (ej. "3002:3001" para el frontend) y actualiza las URLs en .env y en Nginx si lo usas.
  • Build falla con getaddrinfo EAI_AGAIN registry.npmjs.org: Error de DNS dentro del contenedor. El build ya usa network: host y reintentos en yarn install. Si sigue fallando: vuelve a lanzar el build (a veces es transitorio); en Docker Desktop revisa DNS (Settings → Docker Engine → añade "dns": ["8.8.8.8"] si hace falta). test change Sun Aug 30 00:31:54 SAPST 2026