Skip to content

Instalación con contenedores (Docker) ​

Despliegue de referencia de BIAM: tres contenedores de aplicación (web, cola y planificador) construidos a partir de una misma imagen, más PostgreSQL 18 en un cuarto contenedor, orquestados con Docker Compose. Es el despliegue con el que se instala BIAM en los clientes; la instalación nativa queda como alternativa para quien no quiera contenedores.

Esta página describe el procedimiento tal como funciona hoy (paquete biam-docker, septiembre 2026). Donde un paso es manual porque todavía no lo automatiza la imagen, se dice explícitamente.

Antes de empezar

Ten a mano el paquete de entrega (infraestructura.zip) y los requisitos de hardware. La URL con la que las personas abrirán BIAM (APP_URL) conviene decidirla antes: los enlaces de los correos salen de ahí.

1. Qué se entrega y dónde queda ​

El paquete es un único infraestructura.zip con tres piezas. En el servidor, el código y el estado viven fuera de los contenedores, en /data/biam/biam/: los contenedores solo montan esa carpeta.

infraestructura.zip                       En el servidor
├── app-biam.zip      ──────────────►     /data/biam/biam/html/          ← código (= /var/www/html en los 3 contenedores)
│                                           ├── .env                     ← configuración y secretos de ESTA instancia
│                                           ├── storage/                 ← ESTADO: installed.json, licencia, paquetes, handlers, evidencia RPA, logs
│                                           └── public/                  ← SPA ya construido (index.html + assets/)
├── db.env            ──────────────►     /data/biam/biam/db.env         ← POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB
│                                         /data/biam/biam/postgres-data/ ← datos de PostgreSQL
└── infraestructura/  ──────────────►     <carpeta del compose>/         ← p. ej. /opt/biam-infra
    ├── docker-compose.yaml
    └── DockerFiles/{biam_app, biam.ini}

Todo lo que hay que respaldar y lo que un cambio de versión jamás debe pisar está en /data/biam/biam/: el .env, storage/ y postgres-data/. El código de html/ se reemplaza; el estado se conserva.

2. Contenedores e imagen ​

ContenedorImagenQué correPuertoDetalles
biambayoex/biam (se construye en el servidor desde DockerFiles/biam_app)Apache + API + SPA; el «Probar conexión» de los conectores8005:80Apache arranca como root y atiende como www-data (uid 33)
biam-queuela mismaphp artisan queue:work — tareas de aprovisionamiento, sincronización del AD, correos—corre como 33:33, HOME=/tmp (Chromium), shm_size: 1g, stop_grace_period: 11m
biam-schedulerla mismaphp artisan schedule:work — tareas programadas (sync cada 15 min, ventanas de ciclo de vida, canarios)—corre como 33:33
biam-db (servicio dbbiam)postgres:18PostgreSQL5445:5432credenciales en db.env; datos en /data/biam/biam/postgres-data

La imagen (php:8.4-apache) trae las extensiones PHP que BIAM necesita (ldap, pdo_pgsql, intl, zip, gd, bcmath, opcache, pcntl), Chromium + chromedriver + Python 3 con Selenium para los conectores RPA, git, unzip y composer. biam.ini fija 512 MB de memoria, subidas de hasta 64 MB y OPcache con revalidación cada 60 s (como el código va montado desde /data, un cambio de código se ve en menos de un minuto sin reiniciar Apache).

La imagen no tiene script de arranque

El contenedor biam levanta Apache y nada más: no genera llaves, no ajusta permisos, no migra ni recachea la configuración. Todo eso se hace a mano, en el orden de esta guía. Saltarse un paso da el clásico «500 sin rastro» (ver Solución de problemas).

3. Paso 0 — Docker y Docker Compose ​

El servidor suele venir limpio. Instala Docker Engine y el plugin de Compose desde el repositorio oficial de Docker (no el docker.io/podman de la distribución):

bash
# Podman y runc chocan con docker-ce: quitarlos si están
sudo dnf remove -y podman runc buildah 2>/dev/null
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

# Puerto de BIAM en firewalld (el tráfico publicado por Docker entra por su propia cadena;
# igual conviene declararlo)
sudo firewall-cmd --permanent --add-port=8005/tcp && sudo firewall-cmd --reload
bash
sudo apt-get update && sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

Comprobación: docker compose version (v2) y docker run --rm hello-world. Si el servidor no tiene salida a Internet, pide antes que se abra el acceso a Docker Hub, a los mirrors de Debian y a Packagist/GitHub: la imagen se construye en el servidor y composer install descarga las dependencias desde el servidor (sección 6).

SELinux (RHEL)

Con Docker CE el demonio corre por defecto sin confinamiento SELinux para los contenedores y los volúmenes funcionan tal cual. Si el demonio se configuró con "selinux-enabled": true (o usas Podman), añade la etiqueta :Z a los tres volúmenes del compose (/data/biam/biam/html:/var/www/html:Z, ídem postgres-data), o el contenedor verá «Permission denied».

4. Carpetas, db.env y código ​

bash
sudo mkdir -p /data/biam/biam/{html,postgres-data} /opt/biam-infra
cd /tmp && unzip infraestructura.zip

# 1) el compose y la definición de la imagen
sudo cp -r infraestructura/* /opt/biam-infra/

# 2) credenciales de PostgreSQL — genera una contraseña PROPIA de esta instalación
sudo tee /data/biam/biam/db.env > /dev/null <<EOF
POSTGRES_USER=biam
POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=')
POSTGRES_DB=biam
EOF
sudo chmod 600 /data/biam/biam/db.env

# 3) el código
sudo unzip -q app-biam.zip -d /data/biam/biam/html

# 4) dueño www-data (uid/gid 33 de la imagen) para TODO html/, no solo storage/:
#    el asistente reescribe el .env de forma atómica y necesita escribir en su DIRECTORIO
sudo chown -R 33:33 /data/biam/biam/html

Si el db.env del paquete ya trae una contraseña, cámbiala igual: cada instalación debe nacer con sus propios secretos (ver el aviso de la sección 5).

5. El .env de referencia ​

/data/biam/biam/html/.env es el único archivo de configuración de la aplicación. Esta es su estructura real, con valores de ejemplo:

dotenv
APP_NAME=BIAM
APP_ENV=production
APP_DEBUG=false
# La URL con la que la gente abre BIAM: de aquí salen los enlaces de los correos
# (bienvenida, recuperación de contraseña). Con proxy TLS delante: https://…
APP_URL=http://10.0.0.10:8005
APP_TIMEZONE=America/La_Paz
APP_LOCALE=es
APP_FALLBACK_LOCALE=en

# Llaves PROPIAS de cada instalación (sección 6). Las líneas deben existir,
# aunque estén en blanco: `key:generate` solo reemplaza una línea existente.
APP_KEY=
JWT_SECRET=

LOG_CHANNEL=stack
LOG_STACK=app,critical
# `debug` mientras pruebas; nunca `error` si quieres ver los avisos (W)
LOG_LEVEL=info

# La conexión a la base (DB_HOST/PORT/DATABASE/USERNAME/PASSWORD) la escribe el
# asistente de instalación en el paso «Base de datos». No hace falta ponerla a mano.
DB_CONNECTION=main
DB_QUEUE_RETRY_AFTER=960
SESSION_DRIVER=array
QUEUE_CONNECTION=database
# `file` mientras dura el asistente; `database` después (sección 8)
CACHE_STORE=file
FILESYSTEM_DISK=local

# Licenciamiento y catálogo de conectores (BIAM Hub)
BIAM_HUB_URL=https://hub.b-iam.com
BIAM_HUB_PUBLIC_KEY=<la imprime `php artisan hub:public-key` en el Hub>

# Aprovisionamiento RPA
BIAM_RPA_EVIDENCE=/var/www/html/storage/app/rpa-evidence
# Handlers RPA propios del cliente (se cargan por zip desde la pantalla «Handlers RPA»):
# FUERA del árbol de código, para que un cambio de versión no los pise
BIAM_HANDLERS_PATH=/var/www/html/storage/app/handlers
# Presupuesto por tarea del worker (segundos); DB_QUEUE_RETRY_AFTER debe ser MAYOR
BIAM_TASK_TIMEOUT=900

Lo que no va en el .env: el servidor SMTP, la conexión LDAP/AD, la licencia y las credenciales de los conectores se configuran desde la consola y quedan cifrados en la base de datos.

Secretos propios de cada instalación

APP_KEY, JWT_SECRET y la contraseña de PostgreSQL nunca se copian de otra instancia ni viajan dentro de un zip. Con la misma APP_KEY dos instalaciones comparten la llave que cifra las credenciales de los conectores y del directorio; con el mismo JWT_SECRET, una sesión de una instancia vale en la otra. Si el paquete que recibiste trae valores en esas líneas, regenéralos en la sección 6.

La APP_KEY de una instancia con datos no se regenera nunca (cifra credenciales, secretos TOTP y el fingerprint de la licencia); para rotarla existe php artisan biam:secrets-recrypt. Regenerar JWT_SECRET solo cierra todas las sesiones.

6. Construir, levantar y preparar la aplicación ​

bash
cd /opt/biam-infra

# La imagen se construye aquí (Docker Hub + mirrors de Debian) — 5–10 minutos
docker compose build biam

# Solo la base y el contenedor web por ahora: la cola y el planificador van
# después del asistente (sección 8)
docker compose up -d dbbiam biam

# Dependencias PHP (Packagist/GitHub). Siempre como www-data y con HOME escribible
docker compose exec -u www-data -e HOME=/tmp biam \
  composer install --no-dev --optimize-autoloader

# Llaves PROPIAS de esta instalación (las líneas APP_KEY= y JWT_SECRET= deben existir)
docker compose exec -u www-data biam php artisan key:generate --force
docker compose exec -u www-data biam php artisan jwt:secret --force

# Enlace público de storage
docker compose exec -u www-data biam php artisan storage:link

Edita ahora APP_URL en el .env si aún no lo hiciste. Comprobación antes de seguir: curl -s http://localhost:8005/up responde 200.

Siempre -u www-data

El servicio biam no define usuario, así que docker compose exec biam php artisan … corre como root: lo que artisan cree en storage/ (caché, logs, vistas) nace de root y Apache, que atiende como www-data, ya no puede escribirlo. Si te pasó, repara con chown -R 33:33 /data/biam/biam/html/storage /data/biam/biam/html/bootstrap/cache.

7. Asistente de instalación ​

Abre http://<host>:8005/install y sigue los tres pasos (no existe ningún usuario por defecto):

  1. Requisitos — el asistente comprueba versión de PHP, extensiones y escritura del .env y de storage/. Todo debe estar en verde.
  2. Base de datos — host dbbiam (el nombre del servicio en el compose, no una IP), puerto 5432 (el interno del contenedor, no el 5445 publicado hacia fuera), y base, usuario y contraseña de db.env. El asistente prueba la conexión antes de guardarla (INIT201W si no conecta) y escribe las variables DB_* en el .env.
  3. Migración — crea el esquema y los catálogos iniciales.
  4. Administrador — crea el primer usuario root con las credenciales que tú elijas y cierra la instalación en el mismo paso (marca storage/app/installed.json y cachea la configuración).

Desde ese momento el asistente queda bloqueado (INIT101W si alguien vuelve a intentarlo) y puedes entrar con el root recién creado.

8. Después del asistente: caché en base de datos y el resto de contenedores ​

Durante el asistente la caché de la aplicación vive en archivos (CACHE_STORE=file) porque la tabla cache todavía no existía. En operación debe estar en la base de datos: es lo que hace que los tres contenedores compartan los bloqueos de las corridas RPA, los contadores de intentos y la señal de reinicio del worker.

bash
sudo sed -i 's/^CACHE_STORE=.*/CACHE_STORE=database/' /data/biam/biam/html/.env

# IMPRESCINDIBLE: el cierre del asistente dejó la configuración cacheada; sin este
# comando, editar el .env y reiniciar NO cambia nada
docker compose exec -u www-data biam php artisan config:cache

# Ahora sí, la cola y el planificador (nacen ya con la caché en base de datos)
docker compose up -d biam-queue biam-scheduler

# Comprobación: debe decir Cache: database
docker compose exec -u www-data biam php artisan about --only=drivers

Esta regla vale para cualquier cambio posterior del .env (LOG_LEVEL, BIAM_TASK_TIMEOUT, APP_URL…): editar → config:cache como www-data → docker compose restart biam-queue biam-scheduler.

9. Qué se configura, cuándo y quién ​

MomentoQuéQuién lo hace hoy
Antes de levantarAPP_URL, APP_TIMEZONE, BIAM_HUB_*, db.envLa persona que instala
Antes del primer requestAPP_KEYA mano (key:generate, sección 6) — sin ella la página del asistente ni carga
Antes del primer loginJWT_SECRETA mano (jwt:secret); el asistente lo generaría en el paso «Base de datos» si está en blanco, pero conviene no depender de eso
Asistente, paso «Base de datos»DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORDEl asistente
Asistente, paso «Administrador»installed.json, caché de configuración y rutasEl asistente
Justo después del asistenteCACHE_STORE=database + config:cache + levantar cola y planificadorLa persona que instala (sección 8)
OperaciónBIAM_TASK_TIMEOUT, DB_QUEUE_RETRY_AFTER, LOG_LEVELLa persona, siempre seguido de config:cache y reinicio de cola y planificador

10. Respaldo e identidad de la instancia ​

Inmediatamente después de instalar, guarda fuera del servidor lo que identifica a esta instancia:

bash
# Bloque de respaldo de identidad (APP_KEY, id de instancia y fingerprint): sale por pantalla,
# no deja copia en el servidor. Guardarlo en la bóveda de la organización.
docker compose exec -u www-data biam php artisan biam:secrets-backup > biam-secretos-<cliente>-$(date +%F).txt

Reglas de custodia de ese archivo y el procedimiento de restauración (biam:secrets-restore) están en la documentación técnica Custodia de secretos.

Rutina de respaldo: pg_dump de la base (docker compose exec dbbiam pg_dump -U biam biam > biam-$(date +%F).sql) más la carpeta /data/biam/biam/html/storage/app/ (licencia, paquetes de conectores instalados, handlers cargados, evidencia RPA y el marcador de instalación) y el .env. Sin la APP_KEY original, un respaldo de la base no sirve: los secretos cifrados no se pueden leer.

11. Configuración desde la consola ​

Con el root creado, en este orden:

  1. Licencia (Configuración → Licencia): activación en línea contra el Hub o .lic emitido para esta instancia.
  2. Directorio (LDAP/LDAPS al controlador de dominio del cliente).
  3. Correo saliente (Configuración → Sistema → SMTP) y prueba de envío.
  4. Conectores: instalar los paquetes del catálogo, crear las instancias con sus credenciales y, en los sistemas legacy, configurar la «Ejecución por defecto» (ejecutable + intérprete) y las semillas de {sequence}.
  5. Handlers RPA (Integración → Handlers RPA): si el cliente necesita ajustar el código de un conector RPA, se sube el zip de la carpeta del handler desde esa pantalla; la copia anterior queda en el servidor y se puede restaurar. La primera vez, siembra la carpeta con los handlers del release:
bash
docker compose exec -u www-data biam php artisan biam:handlers-migrate
docker compose exec -u www-data biam php artisan permissions:sync   # si vienes de una versión anterior
  1. Marca (nombre, logos, acento) y zona horaria de las ventanas de ciclo de vida.

Ninguna carpeta de storage/ se crea a mano: rpa-evidence/ la crea BIAM al lanzar el primer handler, handlers/ y private/handler-uploads/ la pantalla de Handlers RPA en su primer uso, private/connectors/ la instalación de un paquete y los logs Laravel. Si alguna «falta», el problema es el dueño de storage/ (sección 6).

Salidas de red que necesita el servidor en operación: el controlador de dominio (389/636), el SMTP, el Hub (https://hub.b-iam.com) y los paneles web de los sistemas legacy que se aprovisionan por RPA (Chromium corre dentro de biam-queue).

12. Verificación ​

  • curl -s http://<host>:8005/up → 200.
  • docker compose ps: los cuatro contenedores Up; docker logs -f biam-queue muestra el worker atendiendo y la sincronización del directorio cada 15 minutos.
  • En cada conector, «Probar conexión» en verde.
  • docker compose exec -u www-data biam php artisan biam:sync-users y un alta de prueba con el monitor de aprovisionamiento abierto.

13. Solución de problemas ​

SíntomaCausaQué hacer
/install (y cualquier página) responde 500 antes de instalarAPP_KEY en blanco: la imagen no la genera y sin ella ninguna ruta web arrancaphp artisan key:generate --force como www-data (la línea APP_KEY= debe existir en el .env)
Requisitos en verde, pero el paso «Base de datos» falla con INIT202W («no se puede escribir el .env»)El asistente reescribe el .env de forma atómica y necesita escribir en /data/biam/biam/html, no solo en el archivochown -R 33:33 /data/biam/biam/html
Paso «Base de datos» con INIT201WHost o puerto equivocadosHost dbbiam, puerto 5432; credenciales de db.env
Instalación «exitosa» y el login da 500JWT_SECRET en blanco (alguien migró a mano y el asistente saltó el paso que lo genera)php artisan jwt:secret --force + config:cache, ambos como www-data
Cambié el .env y no pasa nadaLa configuración está cacheada desde el cierre del asistentephp artisan config:cache como www-data y reiniciar biam-queue y biam-scheduler
500 sin rastro en el log tras correr artisanSe ejecutó artisan como root y storage/ tiene archivos de rootchown -R 33:33 /data/biam/biam/html/storage /data/biam/biam/html/bootstrap/cache
«Probar conexión» o una tarea RPA fallan con «No se pudo crear la carpeta de evidencia RPA…» (o, en versiones anteriores, un PermissionError de Python sobre storage/app/rpa-evidence)storage/app no es escribible por www-data: se corrió artisan/composer como root o el chown no cubrió storage/chown -R 33:33 /data/biam/biam/html/storage. No hace falta crear la carpeta a mano: BIAM la crea al lanzar el handler
Una tarea RPA falla con Chromium («session not created», «crashed»)HOME no escribible o /dev/shm pequeño en el workerConfirmar HOME=/tmp y shm_size: 1g en biam-queue (vienen en el compose de referencia)
Una tarea queda «Ejecutando» tras reiniciar contenedoresEl worker murió a mitad de corridadocker compose stop respeta stop_grace_period; BIAM la marca interrumpida (CONN504W) al vencer su presupuesto y permite reintentarla
La subida de un zip de handler falla por tamañoLímite del zip (30 MB por defecto) o upload_max_filesize (64 MB en biam.ini)Ajustar BIAM_HANDLER_UPLOAD_MAX_KB (y biam.ini si supera 64 MB)

14. Recomendaciones para producción ​

  • HTTPS: por BIAM pasan contraseñas del directorio. Pon un proxy TLS (nginx, HAProxy o el balanceador de la organización) delante del puerto 8005 y usa https://… en APP_URL.
  • PostgreSQL fuera de la LAN: el compose de referencia publica 5445:5432 hacia toda la red. Si no necesitas acceder a la base desde fuera del servidor, quita esa línea o limítala a 127.0.0.1:5445:5432.
  • Rotación de logs de Docker: añade a cada servicio logging: { driver: json-file, options: { max-size: "50m", max-file: "5" } } o configúralo global en /etc/docker/daemon.json; sin tope, los logs JSON crecen indefinidamente.
  • Cambio de versión: descomprime el nuevo app-biam.zip sobre html/ (conservando .env y storage/), composer install, php artisan migrate --force y config:cache como www-data, y reinicia cola y planificador después de que el worker termine su tarea en curso (docker compose stop biam-queue espera hasta stop_grace_period).