Versión para VPS donde Docker, Nginx y usuario pedrito ya existen. Elimina: instalación de Docker, Nginx, creación de usuario, configuración DNS. Agrega: tabla de estado inicial, nota sobre docker-compose v1 vs v2. De 20 secciones (60-90 min) a 17 secciones (30-45 min). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
21 KiB
Guía de despliegue — Pedrito Hechakuaa en VPS Ubuntu
Destinatario: equipo técnico de InQuality
Entorno objetivo: VPS Ubuntu 22.04 en Hostinger — producción
Tiempo estimado: 30–45 minutos
Acceso requerido: SSH root al VPS + datos de configuración provistos por Marcos
Subdominio objetivo:pedrito.inqualityhq.com
Estado del servidor al momento de esta guía
| Componente | Estado |
|---|---|
DNS pedrito.inqualityhq.com |
✅ ya apunta al VPS (93.127.136.210) |
| Docker v29.2.1 + compose v2 plugin | ✅ instalados |
| Nginx v1.24.0 | ✅ instalado |
Usuario pedrito (UID 1003) |
✅ existe |
pedrito en grupo docker |
❌ paso 3 |
| Repositorio clonado | ❌ paso 4 |
.env configurado |
❌ paso 5 |
| Credenciales PJ cifradas | ❌ paso 6 |
| Certificado SSL | ❌ paso 7 |
| Nginx configurado para el bot | ❌ paso 8 |
| Firewall | ❓ paso 9 |
| Bot corriendo | ❌ paso 10 |
Nota sobre docker-compose: el servidor tiene
docker-composev1.29.2 (standalone) instalado. Esta guía usadocker compose(con espacio, plugin v2) que también está disponible. No mezclar los dos.
Índice
PARTE A — Preparación
PARTE B — Código y configuración 4. Clonar el repositorio 5. Configurar el archivo .env 6. Cifrar las credenciales del PJ
PARTE C — HTTPS y producción 7. Obtener certificado SSL con Certbot 8. Configurar Nginx como reverse proxy 9. Configurar el firewall 10. Iniciar el bot con Docker 11. Activar el modo webhook
PARTE D — Operación 12. Verificación final 13. Monitoreo y logs 14. Health check HTTP 15. Cómo actualizar el bot 16. Troubleshooting 17. Referencia rápida de comandos
1. Datos que necesitás antes de empezar
Antes de conectarte al VPS, juntá estos datos. Sin alguno de ellos el deploy no puede completarse.
| # | Dato | Quién lo tiene | Dónde va |
|---|---|---|---|
| 1 | FERNET_KEY (clave de cifrado) |
Marcos la genera | .env |
| 2 | TELEGRAM_BOT_TOKEN |
Marcos (de @BotFather) | .env |
| 3 | TELEGRAM_CHAT_ID |
Marcos (de @userinfobot) | .env |
| 4 | TELEGRAM_EXTRA_IDS |
Marcos (chat IDs de otros abogados) | .env |
| 5 | INVITE_CODE |
Marcos lo decide | .env |
| 6 | Cédula del PJ | Marcos | Script de cifrado (paso 6) |
| 7 | Contraseña del PJ | Marcos | Script de cifrado (paso 6) |
Tip: Pedile a Marcos que te envíe los datos 1-5 por un canal seguro (Signal, Bitwarden Send, etc.). Nunca por email ni WhatsApp.
⚠️ Seguridad crítica: Nunca guardés la contraseña del PJ (dato 7) en ningún archivo. El script del paso 6 la pide en pantalla y guarda solo el resultado cifrado.
2. Conectarse al VPS
ssh root@93.127.136.210
Si tenés clave SSH configurada en Hostinger:
ssh -i ~/.ssh/id_rsa root@93.127.136.210
Si es la primera conexión, escribí
yescuando pregunte por el fingerprint.
3. Agregar pedrito al grupo docker
El usuario pedrito existe pero no está en el grupo docker. Sin esto no puede correr contenedores.
# Como root:
usermod -aG docker pedrito
# Verificar que se agregó:
groups pedrito
# Debe incluir "docker" en la lista
Importante: Este cambio toma efecto en la próxima sesión SSH de
pedrito. Si ya tenés una sesión abierta comopedrito, cerrala y volvé a abrir.
4. Clonar el repositorio
# Cambiar al usuario pedrito
su - pedrito
# Verificar que estamos en su home
pwd
# Debe mostrar: /home/pedrito
# Clonar el repositorio
git clone https://github.com/InqGit/hechakuaa_bot.git
# Entrar al directorio
cd hechakuaa_bot
# Verificar que los archivos están presentes
ls -la
Deberías ver: main.py, config.py, Dockerfile, docker-compose.yml, migrations/, etc.
Crear el directorio de datos
mkdir -p /home/pedrito/hechakuaa_bot/data
Salir a root para el siguiente paso:
exit
Ajustar permisos del directorio data para Docker
El contenedor corre con UID 1000. El directorio data/ necesita ser escribible por ese UID:
# Como root:
chown -R 1000:1000 /home/pedrito/hechakuaa_bot/data
# Verificar:
ls -la /home/pedrito/hechakuaa_bot/
# data debe mostrar: drwxr-xr-x 2 1000 1000
5. Configurar el archivo .env
# Como pedrito:
su - pedrito
cd /home/pedrito/hechakuaa_bot
# Crear .env a partir del template
cp .env.example .env
# Editar
nano .env
Guía de campos del .env
Completá los campos marcados con ← COMPLETAR:
# ── CIFRADO ──────────────────────────────────────────────────────────────────
FERNET_KEY= # ← COMPLETAR (Marcos te la da o la generás abajo)
# ── CREDENCIALES PJ (se completan en el paso 6) ───────────────────────────────
PJ_USUARIO_ENC= # ← se completa automáticamente con el script
PJ_PASSWORD_ENC= # ← se completa automáticamente con el script
# ── TELEGRAM ─────────────────────────────────────────────────────────────────
TELEGRAM_BOT_TOKEN= # ← COMPLETAR (de @BotFather)
TELEGRAM_CHAT_ID= # ← COMPLETAR (chat ID de Marcos)
TELEGRAM_EXTRA_IDS= # ← COMPLETAR si hay más abogados (ej: 111222333,444555666)
# ── INVITACIÓN ────────────────────────────────────────────────────────────────
INVITE_CODE= # ← COMPLETAR (código maestro ilimitado)
# ── WEBHOOK (completar después del paso 11) ───────────────────────────────────
WEBHOOK_URL= # ← dejar vacío por ahora
WEBHOOK_SECRET= # ← dejar vacío por ahora
# ── HEALTH CHECK ─────────────────────────────────────────────────────────────
HEALTH_PORT=8080 # dejar así
HEALTH_ALERT_HOURS=2
HEALTH_AUTH_TOKEN= # ← COMPLETAR: python3 -c "import secrets; print(secrets.token_hex(32))"
# ── POLLING ──────────────────────────────────────────────────────────────────
POLL_INTERVAL_MINUTES=60
POLL_HORA_INICIO=7
POLL_HORA_FIN=18
Guardar y salir: Ctrl+O → Enter → Ctrl+X.
Generar FERNET_KEY (si Marcos no te la dio)
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
⚠️ Guardá la FERNET_KEY en un gestor de contraseñas (Bitwarden, 1Password, etc.). Si se pierde, las credenciales cifradas de todos los abogados quedan irrecuperables.
Instalar uv (si no está disponible)
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env
uv --version
Restringir permisos del .env
chmod 600 /home/pedrito/hechakuaa_bot/.env
# Verificar:
ls -la /home/pedrito/hechakuaa_bot/.env
# Debe mostrar: -rw------- 1 pedrito pedrito
6. Cifrar las credenciales del PJ
# Como pedrito, en el directorio del proyecto:
cd /home/pedrito/hechakuaa_bot
uv run python scripts/cifrar_credenciales.py
El script pide:
- Cédula de identidad — el número con el que Marcos entra a
apps.csj.gov.py - Contraseña — no se muestra mientras escribís
Salida esperada:
✅ Credenciales cifradas y guardadas en .env
PJ_USUARIO_ENC y PJ_PASSWORD_ENC actualizadas.
Verificar:
grep "PJ_USUARIO_ENC" /home/pedrito/hechakuaa_bot/.env
# Debe mostrar un token cifrado largo, NUNCA la cédula en texto plano
7. Obtener certificado SSL con Certbot
HTTPS es obligatorio para el modo webhook de Telegram.
Prerequisito: el DNS ya está propagado (verificado:
pedrito.inqualityhq.com→ 93.127.136.210).
# Como root:
exit # salir de pedrito si estabas ahí
# Instalar Certbot con plugin de Nginx
apt install -y certbot python3-certbot-nginx
# Obtener el certificado
certbot --nginx -d pedrito.inqualityhq.com
Certbot te va a preguntar:
- Tu dirección de email (para avisos de vencimiento)
- Aceptar los términos de servicio →
Y - Compartir email con EFF →
N
Salida esperada al terminar:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/pedrito.inqualityhq.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/pedrito.inqualityhq.com/privkey.pem
Verificar renovación automática
systemctl status certbot.timer
# Debe estar "active (waiting)"
certbot renew --dry-run
# Debe decir: "Simulating renewal of an existing certificate..."
8. Configurar Nginx como reverse proxy
Nginx recibe las conexiones HTTPS y las redirige al bot en el puerto 8080.
# Como root:
nano /etc/nginx/sites-available/pedrito
Pegar exactamente este contenido:
# /etc/nginx/sites-available/pedrito
limit_req_zone $binary_remote_addr zone=pedrito_webhook:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=pedrito_health:10m rate=10r/m;
server {
listen 80;
server_name pedrito.inqualityhq.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name pedrito.inqualityhq.com;
ssl_certificate /etc/letsencrypt/live/pedrito.inqualityhq.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pedrito.inqualityhq.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
server_tokens off;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
client_max_body_size 20M;
location /health {
limit_req zone=pedrito_health burst=5 nodelay;
proxy_pass http://127.0.0.1:8080/health;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 10s;
}
location /health/history {
limit_req zone=pedrito_health burst=3 nodelay;
proxy_pass http://127.0.0.1:8080/health/history;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization $http_authorization;
proxy_read_timeout 10s;
}
location /webhook {
limit_req zone=pedrito_webhook burst=50 nodelay;
proxy_pass http://127.0.0.1:8080/webhook;
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;
proxy_set_header X-Telegram-Bot-Api-Secret-Token $http_x_telegram_bot_api_secret_token;
proxy_read_timeout 30s;
}
location / {
return 404;
}
}
Guardar: Ctrl+O → Enter → Ctrl+X.
# Activar el sitio
ln -s /etc/nginx/sites-available/pedrito /etc/nginx/sites-enabled/pedrito
# Verificar sintaxis
nginx -t
# Debe decir: syntax is ok / test is successful
# Recargar Nginx
systemctl reload nginx
Si
nginx -tfalla: revisá que las comillas y los;estén correctos en el archivo.
9. Configurar el firewall
# Como root — verificar estado actual
ufw status
Si dice Status: inactive o no tiene las reglas necesarias:
ufw allow OpenSSH
ufw allow 'Nginx Full' # puertos 80 y 443
ufw --force enable
ufw status
Salida esperada:
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
Nginx Full ALLOW Anywhere
Importante: El puerto 8080 del bot no se abre al exterior. Solo Nginx accede a él internamente desde el mismo servidor.
10. Iniciar el bot con Docker
Verificar que docker-compose.yml usa binding correcto
su - pedrito
cd /home/pedrito/hechakuaa_bot
grep "8080" docker-compose.yml
Debe mostrar 127.0.0.1:8080:8080. Si muestra solo 8080:8080:
nano docker-compose.yml
# Cambiar "8080:8080" por "127.0.0.1:8080:8080"
Construir la imagen
docker compose build
# Tarda 1-3 minutos la primera vez
Primera prueba: correr en primer plano
docker compose up
Deberías ver:
pedrito-1 | {"event": "migration_aplicada", "version": "001_initial_schema", ...}
pedrito-1 | {"event": "database_iniciada", ...}
pedrito-1 | {"event": "health_server_iniciado", "port": 8080, ...}
pedrito-1 | {"event": "bot_telegram_iniciado", ...}
pedrito-1 | {"event": "login_exitoso", ...}
Si aparece login_exitoso, el bot se conectó al PJ correctamente. Detenerlo con Ctrl+C.
Si no aparece
login_exitoso: ver Troubleshooting.
Correr en background (modo producción)
docker compose up -d
# Verificar que está corriendo:
docker compose ps
# STATUS debe decir "running"
11. Activar el modo webhook
Generar el WEBHOOK_SECRET
python3 -c "import secrets; print(secrets.token_hex(32))"
# Salida: 64 caracteres hexadecimales — copiarlos
Actualizar .env
nano /home/pedrito/hechakuaa_bot/.env
Completar estas dos líneas:
WEBHOOK_URL=https://pedrito.inqualityhq.com
WEBHOOK_SECRET=<el token que generaste arriba>
Reiniciar el bot
cd /home/pedrito/hechakuaa_bot
docker compose restart
docker compose logs --tail=20
# Buscar: {"event": "webhook_registrado", "url": "https://pedrito.inqualityhq.com/webhook", ...}
Verificar que el webhook quedó registrado en Telegram
curl -s "https://api.telegram.org/bot<TU_BOT_TOKEN>/getWebhookInfo" | python3 -m json.tool
El campo url debe mostrar https://pedrito.inqualityhq.com/webhook.
12. Verificación final
# 1. El contenedor está corriendo
docker compose ps
# STATUS: running
# 2. Logs sin errores
docker compose logs --tail=30
# Buscar "login_exitoso" — NO debe aparecer: ERROR, exception, Traceback
# 3. Health check responde
curl -s https://pedrito.inqualityhq.com/health | python3 -m json.tool
# {"status": "ok", ...}
# 4. Webhook registrado
curl -s "https://api.telegram.org/bot<TU_BOT_TOKEN>/getWebhookInfo" | python3 -m json.tool
# url: https://pedrito.inqualityhq.com/webhook
# 5. Permisos del .env
ls -la /home/pedrito/hechakuaa_bot/.env
# -rw------- 1 pedrito pedrito
# 6. Directorio data existe
ls -la /home/pedrito/hechakuaa_bot/data/
# pasante.db y snapshot_*.json
# 7. HTTP redirige a HTTPS
curl -I http://pedrito.inqualityhq.com/health
# HTTP/1.1 301 Moved Permanently
Si todos pasan, avisarle a Marcos para que pruebe con /start en Telegram.
13. Monitoreo y logs
su - pedrito
cd /home/pedrito/hechakuaa_bot
# Logs en tiempo real
docker compose logs -f
# Últimos 50 registros
docker compose logs --tail=50
Logs de Nginx
# Como root:
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log
Eventos clave en los logs del bot
| Evento | Qué significa |
|---|---|
login_exitoso |
Conectó al PJ correctamente |
polling_tenant_ok |
Revisión completada sin errores |
polling_tenant_error |
Error al revisar notificaciones |
bot_notificacion_enviada |
Notificación enviada a Telegram |
onboarding_completado |
Un abogado completó el registro |
health_alerta_enviada |
El poller está detenido — alerta al admin |
webhook_registrado |
Webhook activo con Telegram |
14. Health check HTTP
El bot expone https://pedrito.inqualityhq.com/health.
# Sin auth — respuesta mínima pública:
curl -s https://pedrito.inqualityhq.com/health | python3 -m json.tool
# {"status": "ok", "timestamp": "..."}
# Con auth — respuesta completa (reemplazá TOKEN):
curl -s -H "Authorization: Bearer <HEALTH_AUTH_TOKEN>" \
https://pedrito.inqualityhq.com/health/history | python3 -m json.tool
Si el poller no actualizó en más de HEALTH_ALERT_HOURS horas, retorna "status": "degraded"
y el bot manda una alerta automática al Telegram de Marcos.
Monitor externo (opcional)
- URL:
https://pedrito.inqualityhq.com/health - Método:
GET— código esperado:200— intervalo: 5 minutos
15. Cómo actualizar el bot
su - pedrito
cd /home/pedrito/hechakuaa_bot
git pull
docker compose build
docker compose up -d
docker compose ps
docker compose logs --tail=20
.envydata/no se tocan congit pull— están en.gitignore.
Si hubo cambios en
.env.example:diff .env.example .env
Migración de credenciales vault v3 (solo al actualizar desde versiones < v0.4.0)
Si había abogados registrados antes de v0.4.0, migrar sus credenciales antes del restart:
docker compose run --rm pedrito uv run python scripts/migrar_vault_v3.py --dry-run
# Verificar output, luego:
docker compose run --rm pedrito uv run python scripts/migrar_vault_v3.py
16. Troubleshooting
El contenedor no arranca
docker compose logs --tail=50
docker compose ps
FERNET_KEY inválida — la clave en .env tiene espacios o comillas. Verificar el valor.
telegram.error.InvalidToken — el TELEGRAM_BOT_TOKEN tiene un error. Recopiarlo de @BotFather.
ModuleNotFoundError — reconstruir la imagen:
docker compose build --no-cache && docker compose up -d
El bot arranca pero no responde en Telegram
docker compose ps— ¿está corriendo?curl -s "https://api.telegram.org/bot<TOKEN>/getWebhookInfo"— ¿el webhook está registrado?systemctl status nginx— ¿Nginx está activo?curl -s https://pedrito.inqualityhq.com/health— ¿el health responde?tail -20 /var/log/nginx/error.log— ¿hay errores en Nginx?
Error de credenciales del PJ
su - pedrito
cd /home/pedrito/hechakuaa_bot
uv run python scripts/cifrar_credenciales.py
docker compose restart
docker compose logs --tail=10
El certificado SSL venció
certbot renew
systemctl reload nginx
El VPS se reinició y el bot no volvió
# El contenedor tiene restart: unless-stopped — debería volver solo.
# Si no:
su - pedrito
cd /home/pedrito/hechakuaa_bot
docker compose up -d
Error de permisos en data/
# Como root:
chown -R 1000:1000 /home/pedrito/hechakuaa_bot/data
su - pedrito
cd /home/pedrito/hechakuaa_bot
docker compose restart
17. Referencia rápida de comandos
Gestión del bot (como pedrito)
cd /home/pedrito/hechakuaa_bot
docker compose ps # estado
docker compose up -d # iniciar
docker compose down # detener
docker compose restart # reiniciar
docker compose logs -f # logs en tiempo real
docker compose logs --tail=50 # últimos 50 registros
# Actualización rápida:
git pull && docker compose build && docker compose up -d
Nginx (como root)
nginx -t # verificar configuración
systemctl reload nginx # recargar sin cortar conexiones
systemctl restart nginx # reiniciar completamente
tail -f /var/log/nginx/error.log
SSL
certbot renew # renovar manualmente
certbot certificates # ver fecha de vencimiento
Rutas de archivos importantes
| Archivo | Ruta absoluta |
|---|---|
| Código del bot | /home/pedrito/hechakuaa_bot/ |
| Configuración | /home/pedrito/hechakuaa_bot/.env |
| Base de datos | /home/pedrito/hechakuaa_bot/data/pasante.db |
| Snapshots | /home/pedrito/hechakuaa_bot/data/snapshot_*.json |
| Docker Compose | /home/pedrito/hechakuaa_bot/docker-compose.yml |
| Config Nginx | /etc/nginx/sites-available/pedrito |
| Logs Nginx | /var/log/nginx/ |
| Certificado SSL | /etc/letsencrypt/live/pedrito.inqualityhq.com/ |
Ante dudas técnicas, contactar a Marcos Benítez.