Despliegue
Cómo ejecutar tu bot en producción, siempre conectado.
Solo ESM, Node >= 22
@botmesh/core y los paquetes de transporte son ESM puro — no hay build de CommonJS. Asegúrate de que el package.json de producción tenga "type": "module" (o que tu entrypoint use .mjs), y que el runtime sea Node >= 22.
Opción 1: VPS (recomendado)
1. Preparar el servidor
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
npm install -g pnpm
git clone https://github.com/your-user/my-bot.git
cd my-bot
pnpm install2. Primera conexión (escanear el QR)
node bot.js
# Escanea el QR, luego Ctrl+CLa sesión se guarda en ./sessions/. No necesitas escanear de nuevo.
3. Ejecutar con PM2 (siempre activo)
npm install -g pm2
pm2 start bot.js --name my-bot
pm2 save
pm2 startup # sigue las instrucciones para auto-iniciar en el arranqueComandos de PM2
pm2 status # estado
pm2 logs my-bot # logs
pm2 restart my-bot # reiniciar
pm2 stop my-bot # detenerOpción 2: Docker
Dockerfile
FROM node:22-slim
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
COPY . .
RUN mkdir -p /app/sessions
CMD ["node", "bot.js"]docker-compose.yml
version: '3.8'
services:
bot:
build: .
restart: unless-stopped
volumes:
- ./sessions:/app/sessions
- ./store.json:/app/store.json
environment:
- NODE_ENV=productiondocker compose up -d
docker compose logs -f # escanea el QR la primera vezOpción 3: Screen / Tmux
screen -S my-bot
node bot.js
# Salir sin detener: Ctrl+A, luego D
# Volver a entrar: screen -r my-botConsideraciones
Persistencia de sesión (auth)
Las credenciales de inicio de sesión de WhatsApp se guardan bajo sessionsDir/session (por defecto ./sessions/<session>) por el provider de Baileys. No la borres o tendrás que escanear el QR de nuevo:
const bot = new Bot({
provider: new BaileysProvider({ session: 'my-bot', sessionsDir: './sessions' }),
})Persistencia del store (conversaciones e historial)
El MemoryStore por defecto vive solo en memoria. Para producción, usa SQLiteStore — una base de datos real que persiste mensajes, chats, contactos, estado de flujos, checkpoints y conversaciones AI:
import { Bot } from '@botmesh/core'
import { BaileysProvider } from '@botmesh/baileys'
import { SQLiteStore } from '@botmesh/sqlite-store'
const bot = new Bot({
provider: new BaileysProvider({ session: 'mi-bot' }),
store: new SQLiteStore({ file: './data/bot.db' }),
})Con SQLiteStore, los checkpoints de flow(...).persistent() sobreviven reinicios automáticamente. Ver la guía de Store Persistente para más detalles.
Respalda la carpeta y el archivo juntos — son lo que le permite a un bot redesplegado continuar sin volver a escanear el QR ni perder conversaciones en curso:
tar -czf backup.tar.gz sessions/ data/
tar -xzf backup.tar.gzApagado ordenado
bot.stop() desmonta el provider y aborta las conversaciones activas. Usa { drain: true } para esperar los envíos en vuelo antes de cerrar (evita mensajes perdidos durante deploys):
process.on('SIGINT', async () => {
console.log('Apagando ordenadamente...')
await bot.stop({ drain: true, timeout: '15s' }) // espera hasta 15s por envíos pendientes (también acepta ms)
process.exit(0)
})
process.on('SIGTERM', async () => {
await bot.stop({ drain: true, timeout: 15_000 })
process.exit(0)
})| Opción | Tipo | Default | Descripción |
|---|---|---|---|
drain | boolean | false | Esperar a que los envíos en vuelo terminen |
timeout | number | 10000 | Máx ms a esperar por el drain antes de forzar el cierre |
Las respuestas que siguen enviándose cuando empieza stop(), y las que el provider ya aceptó, se siguen registrando en el log de mensajes (hooks after('send') y escritura en el store, hasta 5 s) antes de cerrar el store, con o sin drain.
Tras el drain, stop() no deja nada corriendo: los mensajes que aún esperaban en la cola de un chat se descartan en vez de procesarse, no arranca ninguna conversación nueva hasta start(), las respuestas en pausa que seguían esperando se descartan ({ skipped: true }) y los ctx.stream() en curso se cancelan sin volver a tocar el transporte (sin envío final, edición ni borrado; resuelven con cancelled: true). Ningún temporizador del bot mantiene vivo el proceso después; pnpm bench:soak lo comprueba bajo carga (ver CONTRIBUTING.md).
Logging
const bot = new Bot({
provider: new BaileysProvider({ session: 'mi-bot', debug: false }), // true para logs detallados
debug: false, // logger del Bot: `warn` por defecto, `debug` con true
})Sin logger ni debug, el logger pino integrado del Bot trabaja en nivel warn, así que se ven los avisos de plugins y handlers (p. ej. una autorespuesta de IA fallida). Usa debug: true para los logs de depuración o inyecta tu propio logger.
Siguientes pasos
- Varias réplicas — varios procesos del mismo bot (relevo ante caídas, más capacidad de webhooks)
- Preguntas frecuentes — problemas comunes
- API Reference — documentación técnica