Dónde desplegar Pitangus
Pitangus tiene tres piezas, y cada una puede vivir donde más te convenga:
| Pieza | Qué necesita | Imagen |
|---|---|---|
| API y panel | HTTP y la base de datos. Sin disco propio: todo el estado está en PostgreSQL. | ghcr.io/pitangus-dev/pitangus |
| Worker | Un proceso permanente que ejecuta los motores de análisis. | ghcr.io/pitangus-dev/pitangus-worker (motores dentro), o la imagen de la API con el socket de Docker |
| PostgreSQL | Versión 16 o superior, gestionada o en un contenedor. | — |
Cada etiqueta de versión publica las imágenes, firmadas y multiarquitectura. También puedes construirlas desde el repositorio (make build, o docker build --target worker-standalone -f docker/app/Dockerfile . para el worker).
Elige destino
Sección titulada «Elige destino»| Destino | API y panel | Worker | PostgreSQL | Guía | Probado |
|---|---|---|---|---|---|
| Tu servidor, con el repositorio | Compose | Compose, motores dentro (socket de Docker con SOCKET=1) |
Contenedor | despliegue-vps.md | Sí |
| Tu servidor, un solo archivo (Coolify, Dokploy, Portainer, gestor de Docker de Hostinger) | Compose | Compose, motores dentro | Contenedor | abajo | En local, de principio a fin |
| Render | Web service | Background worker | Render Postgres | abajo | Todavía no |
| Railway, Fly.io | Servicio | Servicio | Su PostgreSQL | abajo | Todavía no |
| Vercel | Función de Python | En otro sitio (cualquier fila de arriba) | Neon, Supabase… | abajo | La entrada ASGI, en local |
| Kubernetes | Deployment | Deployment | Tu operador | abajo | Todavía no |
Sea cual sea el destino, importan los mismos cuatro valores:
PITANGUS_DATABASE_URL=postgresql://usuario:contraseña@host:5432/pitangus # valen postgres:// y postgresql://PITANGUS_MASTER_KEY=<openssl rand -base64 32> # la misma en la API y en cada workerPITANGUS_PUBLIC_URL=https://pitangus.example.comPITANGUS_ALLOWED_ORIGINS=https://pitangus.example.com # cada dirección con la que se abreLa clave maestra descifra todos los secretos guardados: GitHub App, Jira, canales de avisos, semillas
TOTP y la clave de firma de sesiones. Guárdala en el gestor de secretos de la plataforma, con una copia fuera de las
copias de la base. pitangus check-config revisa toda la configuración y dice qué está mal.
Un solo archivo: cualquier servidor o panel de Docker
Sección titulada «Un solo archivo: cualquier servidor o panel de Docker»deploy/compose.yaml no necesita nada más: imágenes publicadas, volúmenes con nombre, sin
construir, sin el repositorio y sin el socket de Docker (el worker ejecuta los motores dentro de su propia imagen).
curl -fsSLO https://raw.githubusercontent.com/Pitangus-Dev/pitangus/main/deploy/compose.yamlprintf 'PITANGUS_DB_PASSWORD=%s\nPITANGUS_MASTER_KEY=%s\nPITANGUS_PUBLIC_URL=%s\n' \ "$(openssl rand -hex 24)" "$(openssl rand -base64 32)" "https://pitangus.example.com" > .envdocker compose --profile https up -d # --profile https: Caddy obtiene el certificado (sin él, detrás de tu proxy)docker compose exec api python -m pitangus setup-code- Coolify y Dokploy: crea un recurso Docker Compose, pega el archivo, define las tres variables en la pantalla de
entorno y apunta tu dominio al servicio
api, puerto 8766. Deja apagado el perfilhttps: el proxy de la plataforma se encarga del TLS. - Hostinger: en el panel del VPS, abre el gestor de Docker, crea un proyecto desde Compose y pega el archivo y las
variables. Si no hay otro proxy en el servidor, añade
COMPOSE_PROFILES=https. - Portainer: Stacks → Add stack, pega el archivo y añade las variables.
- Redes: PostgreSQL solo está en
db, una red interna sin salida que comparte con la api, el worker y las copias. Caddy solo está enedge, con la api. - Copias de seguridad:
--profile backupguarda copias conpg_dumpen./backupscada 24 h (cámbialo conPITANGUS_BACKUP_INTERVAL_HOURS). La clave maestra no va en ellas: guárdala aparte.
Dimensionado: con 2 vCPU y 4 GB de memoria corre un análisis a la vez; con 8 GB va holgado. La imagen del worker ocupa unos 1,8 GB.
render.yaml es un Blueprint con la API (web service), el worker (background worker) y
PostgreSQL. New → Blueprint, elige el repositorio y escribe PITANGUS_PUBLIC_URL cuando te lo pida
(https://<servicio>.onrender.com o tu dominio). Render genera la clave maestra (32 bytes aleatorios en base64, justo
su formato) y la comparte con el worker. Léela una vez del entorno de la API y guarda una copia.
Railway, Fly.io y otras plataformas de contenedores
Sección titulada «Railway, Fly.io y otras plataformas de contenedores»Dos servicios con las imágenes publicadas, más el PostgreSQL de la plataforma:
| Servicio | Imagen | Variables, además de las cuatro de arriba |
|---|---|---|
| api | ghcr.io/pitangus-dev/pitangus:<versión> |
PITANGUS_BIND=0.0.0.0, PITANGUS_EMBEDDED_WORKER=0, PITANGUS_FORWARDED_ALLOW_IPS=*, puerto 8766 |
| worker | ghcr.io/pitangus-dev/pitangus-worker:<versión> |
ninguna |
En Railway, referencia la base como PITANGUS_DATABASE_URL=${{Postgres.DATABASE_URL}}. Dale al worker al menos 2 GB de
memoria. Sus cachés (las bases de datos de los motores) pueden ir en disco efímero: se vuelven a descargar tras un
redespliegue.
Vercel ejecuta la API y el panel como una función de Python (vercel.json, entrada
api/index.py, que importa pitangus.app.asgi). El worker no cabe ahí: un análisis puede durar más de lo que dura una
función, y no hay proceso permanente. Ejecuta el worker en cualquier otra fila de la tabla, contra la misma base y con
la misma clave maestra.
- Crea una base PostgreSQL (Neon, Supabase u otra) a la que lleguen los dos.
- Importa el repositorio en Vercel y define las cuatro variables. En
PITANGUS_ALLOWED_ORIGINSpon cada dirección con la que lo abras: el dominio de producción, y las URL de vista previa si las usas. - Arranca el worker en otro sitio con los mismos
PITANGUS_DATABASE_URLyPITANGUS_MASTER_KEY. - El código de configuración aparece en los registros de la función con la primera petición;
pitangus setup-code, ejecutado en cualquier sitio con las mismas variables, también lo muestra.
Si el worker no puede estar siempre encendido (escala a cero), define PITANGUS_PERIODIC=external en los dos y deja que
un programador dispare las tareas periódicas: añade "crons": [{"path": "/api/cron", "schedule": "*/5 * * * *"}] a
vercel.json y un CRON_SECRET de al menos 32 caracteres (Vercel lo envía como token bearer). El plan Hobby de Vercel
solo permite una ejecución al día.
Kubernetes
Sección titulada «Kubernetes»Todavía no hay manifiestos. Las piezas encajan directamente: un Deployment para la API (puerto 8766, /api/health como
sonda), otro para la imagen del worker (no necesita el socket de Docker), las cuatro variables desde un Secret y el
PostgreSQL que prefieras. Con PITANGUS_PERIODIC=external, un CronJob que ejecute python -m pitangus periodic
sustituye al reloj del worker líder.
Cuántas réplicas del worker y qué mirar para escalarlas: escalar.md.
Tareas periódicas
Sección titulada «Tareas periódicas»Entregar los avisos, sondear los pull requests, sincronizar NVD y revisar a diario si hay avisos nuevos van con reloj:
PITANGUS_PERIODIC=leader(por defecto): las ejecuta un worker, elegido con un cerrojo de PostgreSQL. No hay nada que configurar.PITANGUS_PERIODIC=external: nada se ejecuta solo. Un programador llama apitangus periodic(cron, un CronJob) o aGET /api/cronconAuthorization: Bearer <PITANGUS_CRON_TOKEN o CRON_SECRET>. Cada llamada ejecuta lo que toca; la API encola para un worker la sincronización de NVD y la revisión de avisos, porque necesitan los motores o tardan.