Instalar sol2flow con Docker Compose
Un sol2flow autoalojado es un contenedor (la app web, la API y los trabajos en segundo plano) más una base de datos PostgreSQL (16, 17 o 18; se recomienda 18). Los archivos subidos van a un volumen o a un almacenamiento compatible con S3. Los correos salen por SMTP.
Requisitos
- Un servidor Linux (x86-64 o ARM64) con Docker y el plugin Docker Compose.
- 1 CPU y 1–2 GB de memoria para un equipo pequeño; más para muchos usuarios o importaciones grandes.
- Un nombre de dominio que apunte al servidor y un proxy inverso para HTTPS (Proxy inverso y TLS).
- Una cuenta SMTP para enviar correo (Correo).
- Espacio en disco para la base de datos y los archivos subidos, más sitio para las copias de seguridad.
- PostgreSQL 16, 17 o 18 (se recomienda 18). El
compose.ymlde abajo trae la 18; ¿ya usas 16 o 17? Consulta Actualizar PostgreSQL.
La imagen es ghcr.io/sol2flow/sol2flow. Etiquetas: latest, una versión como 1.2.3 o una línea menor como 1.2.
Fija una versión en producción para que las actualizaciones ocurran cuando tú decidas.
1. Prepara un directorio
sudo mkdir -p /srv/sol2flow && cd /srv/sol2flow
Crea compose.yml:
name: sol2flow
services:
app:
image: ghcr.io/sol2flow/sol2flow:${SOL2FLOW_VERSION:-latest}
restart: unless-stopped
env_file: .env
environment:
DATABASE_URL: postgresql://sol2flow:${POSTGRES_PASSWORD:-sol2flow}@db:5432/sol2flow?schema=public
ports: ["127.0.0.1:3000:3000"] # only the reverse proxy on this host talks to the app
volumes: [uploads:/data/uploads, logs:/data/logs] # logs: only used with LOG_FILE
depends_on: { db: { condition: service_healthy } }
db:
image: postgres:18-alpine
restart: unless-stopped
environment: { POSTGRES_USER: sol2flow, POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-sol2flow}", POSTGRES_DB: sol2flow }
volumes: [pgdata:/var/lib/postgresql] # from PostgreSQL 18 not …/data (data: …/18/docker)
healthcheck: { test: ["CMD-SHELL", "pg_isready -U sol2flow"], interval: 5s, retries: 20 }
volumes: { pgdata: {}, uploads: {}, logs: {} }
La app se ejecuta con el usuario y el grupo 1001, no como root, y es la propietaria de /data/uploads y /data/logs
en la imagen. Los volúmenes con nombre como los de arriba lo adoptan por sí solos. Para guardar los archivos en un
directorio del host (un bind mount como /srv/sol2flow/uploads:/data/uploads), dáselo antes a ese usuario:
sudo mkdir -p /srv/sol2flow/uploads /srv/sol2flow/logs
sudo chown -R 1001:1001 /srv/sol2flow/uploads /srv/sol2flow/logs
2. Configura
Crea .env junto a él. Lo mínimo para producción:
SOL2FLOW_VERSION=latest
APP_URL=https://flow.example.com
AUTH_SECRET=<openssl rand -base64 32>
APP_ENCRYPTION_KEY=<openssl rand -base64 32>
POSTGRES_PASSWORD=<openssl rand -hex 16>
MAIL_DRIVER=smtp
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_PASSWORD=<your SMTP password>
BOOTSTRAP_ADMIN_USERNAME=admin
BOOTSTRAP_ADMIN_PASSWORD=<a long password>
Sustituye cada <…> por un valor real. Haz que .env solo lo pueda leer root (chmod 600 .env).
APP_ENCRYPTION_KEY cifra los secretos guardados en la base de datos (secretos del proveedor de identidad, secretos de la verificación
en dos pasos, secretos de webhooks). Guarda una copia fuera del servidor y fuera de las copias de seguridad de la base de datos. Si la pierdes o la
cambias, esos secretos quedan ilegibles: consulta Copias de seguridad y actualizaciones.
Todas las variables se describen en Configuración.
3. Arranca
docker compose pull
docker compose up -d
docker compose logs -f app
En cada arranque, la app aplica las migraciones pendientes de la base de datos y luego inicia el servidor en el puerto 3000. Dos endpoints te indican que está en marcha:
/api/healthresponde cuando el proceso se está ejecutando./api/readyresponde cuando además se puede acceder a la base de datos.
curl -fsS http://127.0.0.1:3000/api/ready
4. Pon HTTPS delante
Apunta tu proxy inverso a 127.0.0.1:3000 y sírvelo en tu dominio: Proxy inverso y TLS.
APP_URL debe ser exactamente la dirección que escriben las personas (esquema, host y puerto si no es el predeterminado).
5. El primer administrador
Con BOOTSTRAP_ADMIN_PASSWORD definida, el primer arranque crea la cuenta de BOOTSTRAP_ADMIN_* como Propietario
de la instancia, el rol de staff más alto. El nombre de usuario es admin si no pones otro; el correo es opcional.
Solo ocurre mientras la instancia no tiene usuarios, así que después puedes quitar BOOTSTRAP_ADMIN_PASSWORD de .env.
Su dirección de correo cuenta como confirmada, igual que la de cada cuenta que crea el script create-admin de abajo.
Una instancia nueva empieza vacía: esta cuenta es la única y no hay datos de demostración. Las plantillas de tablero predefinidas están disponibles desde el principio.
Sin BOOTSTRAP_ADMIN_PASSWORD, crea el Propietario desde la línea de comandos:
docker compose exec app node scripts/create-admin.mjs <username> <email> <password>
El script también funciona más adelante:
- Mientras la instancia no tiene Propietario, crea (o asciende) a un Propietario; si no, a un Admin.
- Elige otro rol de staff con
--role owner|admin|support|auditor, por ejemplodocker compose exec app node scripts/create-admin.mjs lea [email protected] '…' --role support. - Una cuenta existente conserva su contraseña y recibe el rol. El script nunca degrada a un Propietario existente: cambia los roles en Administración → Usuarios.
Qué puede hacer cada rol: Roles de staff.
6. Después del primer inicio de sesión
- Administración → General: el nombre de la instancia, los límites de subida y los correos de notificación. Consulta Ajustes de la instancia.
- Administración → Autenticación: inicio de sesión con contraseña, registro libre, inicio de sesión único. Consulta Inicio de sesión.
- Envíate un restablecimiento de contraseña para comprobar que el correo funciona.
- Configura las copias de seguridad antes de que lleguen datos reales.
Worker aparte (opcional)
El trabajo en segundo plano (correos, limpiezas, exportaciones, importaciones, webhooks) se ejecuta dentro del contenedor de la app. En una instancia con mucha carga puedes moverlo a un contenedor propio:
- Pon
ROLE=weben los contenedores que atienden a los usuarios: dejan de ejecutar trabajos en segundo plano. - Añade un servicio más con la misma imagen y el mismo
.env, conROLE=workery sin puerto publicado. Ese ejecuta los trabajos.