Saltar al contenido principal

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.yml de 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
MAIL_FROM="sol2flow <[email protected]>"
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_PASSWORD=<your SMTP password>

BOOTSTRAP_ADMIN_EMAIL=[email protected]
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).

Guarda bien APP_ENCRYPTION_KEY

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/health responde cuando el proceso se está ejecutando.
  • /api/ready responde 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 ejemplo docker 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​

  1. Administración → General: el nombre de la instancia, los límites de subida y los correos de notificación. Consulta Ajustes de la instancia.
  2. Administración → Autenticación: inicio de sesión con contraseña, registro libre, inicio de sesión único. Consulta Inicio de sesión.
  3. Envíate un restablecimiento de contraseña para comprobar que el correo funciona.
  4. 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:

  1. Pon ROLE=web en los contenedores que atienden a los usuarios: dejan de ejecutar trabajos en segundo plano.
  2. Añade un servicio más con la misma imagen y el mismo .env, con ROLE=worker y sin puerto publicado. Ese ejecuta los trabajos.