Kubernetes
Libre WebUI incluye un chart Helm en helm/libre-webui.
Work en Kubernetes
Work se ejecuta de forma nativa en Kubernetes: no intervienen el daemon, CLI ni socket de Docker. Actívalo al instalar:
helm install libre-webui ./helm/libre-webui --set work.enabled=true
Esto cambia el backend a WORK_RUNTIME_BACKEND=kubernetes y crea:
- un namespace dedicado (
work.namespace, predeterminadolibre-webui-work) con un Pod por entorno activo y un PersistentVolumeClaim por espacio de tarea (work.workspaceSize, predeterminado5Gi, una cuota real de disco; una política Work con nombre puede usar otro tamaño); - un Role y RoleBinding limitados al namespace que conceden al ServiceAccount del backend exactamente
pods(get/list/create/delete),pods/exec(get/create) ypersistentvolumeclaims(get/list/create/delete), sin secretos ni ámbito de clúster. Sustituye por completo el socket Docker: el servidor de API impide que un entorno monte rutas del host; - NetworkPolicies que deniegan todo tráfico de forma predeterminada, permiten entrada solo desde el backend al puerto de vista previa y conceden salida a Internet a los entornos con red excepto
work.networkPolicy.blockedEgressCidrs(por defecto rangos privados, CGNAT usado por algunos clústeres para CIDR de pods/servicios y metadatos locales de la nube; comprueba que cubra los CIDR de tu clúster). El DNS solo se permite akube-system; el DNS local por nodo necesita su propia excepción.
Los entornos se ejecutan sin root, con sistema raíz de solo lectura, todas las capacidades eliminadas, seccomp RuntimeDefault y sin token ServiceAccount. Archivos, comandos, Git y terminales usan el subrecurso exec; la vista previa se sirve desde la IP del Pod por el proxy firmado del mismo origen, por lo que el backend debe ejecutarse dentro del clúster. No se admiten espacios basados en carpetas del host.
Dos notas: NetworkPolicy requiere un CNI que la aplique (Calico, Cilium, versiones recientes de kind y la mayoría de clústeres administrados); verifícalo antes de considerar activo el aislamiento. La suite integral informa si el clúster de CI lo aplica. Y nunca montes el socket del entorno de contenedores de un nodo en el Pod de la WebUI: este backend existe precisamente para evitarlo.
Instalación
helm install libre-webui oci://ghcr.io/libre-webui/charts/libre-webui
El chart predeterminado despliega Libre WebUI con almacenamiento persistente y Ollama incluido. La transición 0.14.1 está fijada al resumen verificado de su imagen multiarquitectura; los charts posteriores usan la imagen correspondiente a appVersion. Define image.tag o image.digest solo si quieres otra imagen. Un image.tag no vacío prevalece sobre el resumen de transición.
El perfil solo acepta replicaCount: 0 para una suspensión deliberada o replicaCount: 1 para funcionar. Rechaza valores mayores y HorizontalPodAutoscaler porque SQLite, los archivos locales y la coordinación en proceso no son seguros tras varios Pods. Con cero réplicas se aprovisionan recursos de control, pero no se sirve tráfico.
Para varias réplicas, configura el perfil team completo. Utiliza PostgreSQL/PGVector, blobs compatibles con S3, Redis y un trabajador duradero aparte; el chart rechaza mezclas parciales. Parte de un archivo protegido:
replicaCount: 3
env:
LIBRE_PLATFORM_MODE: team
DATABASE_BACKEND: postgres
DATABASE_SSL_MODE: verify-full
POSTGRES_MIGRATION_MODE: apply
POSTGRES_POOL_MAX: 10
POSTGRES_CONNECT_TIMEOUT_MS: 5000
POSTGRES_IDLE_TIMEOUT_MS: 30000
POSTGRES_STATEMENT_TIMEOUT_MS: 30000
POSTGRES_MIGRATION_LOCK_TIMEOUT_MS: 60000
OLLAMA_TIMEOUT: 300000
OLLAMA_LONG_OPERATION_TIMEOUT: 900000
OLLAMA_MAX_CONTEXT: 32768
BLOB_STORE_BACKEND: s3
VECTOR_STORE_BACKEND: pgvector
COORDINATION_BACKEND: redis
JOB_WORKER_MODE: external
STORAGE_ENCRYPTION_ACTIVE_KEY_ID: active
S3_BUCKET: libre-blobs
S3_REGION: us-east-1
S3_BLOB_PREFIX: libre/blobs
worker:
replicaCount: 1
secrets:
databaseUrl: postgresql://libre:replace-me@postgres.example/libre
redisUrl: rediss://redis.example:6379/0
jwtSecret: '<one-stable-high-entropy-secret-for-every-replica>'
encryptionKey: '<legacy-64-character-lowercase-hex-key>'
storageEncryptionKeys: '{"legacy":"<legacy-64-character-lowercase-hex-key>","active":"<active-64-character-lowercase-hex-key>"}'
s3AccessKeyId: replace-me
s3SecretAccessKey: replace-me
secrets.encryptionKey debe coincidir exactamente con legacy y el mapa contener STORAGE_ENCRYPTION_ACTIVE_KEY_ID. secrets.jwtSecret debe ser un valor estable y de alta entropía compartido por todos los Pods; el chart rechaza team sin él para que las sesiones no dependan de material local. Mantén TLS verificado para PostgreSQL administrado y no añadas parámetros TLS a databaseUrl. Los límites del pool son por proceso: reserva al menos (replicaCount + worker.replicaCount) * POSTGRES_POOL_MAX conexiones más margen. Instala con el archivo protegido:
helm upgrade --install libre-webui \
oci://ghcr.io/libre-webui/charts/libre-webui \
--values /absolute/path/to/libre-team-values.yaml
No confirmes el archivo ni pases secretos mediante --set. Guárdalo con un flujo cifrado. Escala proveedores y Pods de Work de forma independiente. Con work.enabled=true, el trabajador recibe la misma imagen, StorageClass y límites work.env que la aplicación, además del mismo endpoint Ollama, esperas y contexto máximo, porque ejecuta embeddings, chats duraderos y Work. Una aplicación team activa (replicaCount positivo o autoescalado) exige al menos un trabajador; el chart rechaza cero. Define replicaCount y worker.replicaCount a cero para suspender todo. Cero aplicación con trabajador positivo es un modo deliberado de drenaje/recuperación: no sirve web, pero procesa la cola.
Actualizaciones de team y compatibilidad de esquema
Libre exige una versión exacta del esquema, no actualizaciones mixtas ni sin interrupción. Los Deployments de aplicación y trabajador usan Recreate, evitando solapamiento dentro de cada uno, pero Kubernetes no los coordina como una frontera. Antes de actualizar, detén la entrada, deja terminar o cancela trabajos, escala ambos a cero, realiza una copia verificada y confirma que todos los Pods antiguos terminaron. Luego actualiza con POSTGRES_MIGRATION_MODE=apply; un proceso nuevo toma el bloqueo asesor y los demás esperan y validan el mismo registro. Para revertir, restaura la copia anterior en destinos PostgreSQL/S3 limpios; nunca apuntes un binario antiguo a un esquema que no admita exactamente. Habrá una interrupción deliberada.
Acceso local
kubectl port-forward svc/libre-webui 8080:8080
Abre http://localhost:8080.
Ollama externo
Utiliza un endpoint existente:
helm install libre-webui oci://ghcr.io/libre-webui/charts/libre-webui \
--set ollama.bundled.enabled=false \
--set ollama.external.enabled=true \
--set ollama.external.url=http://my-ollama:11434
Secretos
Define un secreto JWT y una clave estables. El chart crea <release>-libre-webui-secrets con valores secrets.* no vacíos:
helm upgrade --install libre-webui \
oci://ghcr.io/libre-webui/charts/libre-webui \
--set-string secrets.jwtSecret="$(openssl rand -hex 64)" \
--set-string secrets.encryptionKey="$(openssl rand -hex 32)"
Para un Secret gestionado por el operador, define secrets.existingSecret. El chart no genera ninguno y ambos tipos de Pod referencian el objeto:
secrets:
existingSecret: libre-webui-runtime
Créalo antes de instalar. Debe contener jwt-secret y encryption-key; team exige además database-url, redis-url y storage-encryption-keys. Las claves opcionales son session-secret, s3-access-key-id, s3-secret-access-key y s3-session-token. OAuth de GitHub y Hugging Face puede leer pares *-client-id y *-client-secret cuando un valor no vacío de secrets.githubClientId o secrets.huggingfaceClientId activa la integración. El chart no valida ni copia valores; una clave ausente impide iniciar el Pod.
Para automatización, prefiere secrets.existingSecret con un controlador externo o valores estables cifrados. Los valores --set pueden verse en procesos y se guardan en metadatos de Helm. Añade credenciales mediante una extensión deliberada o configúralas por usuario en la WebUI.
NetworkPolicies de aplicación y trabajador
Define networkPolicy.enabled=true para generar políticas de entrada:
networkPolicy:
enabled: true
La aplicación solo acepta entrada en su puerto HTTP y el trabajador ninguna. No restringen salida: ambos deben alcanzar PostgreSQL, Redis, S3, Ollama, herramientas y proveedores, y el operador decide dónde residen.
Es independiente de work.networkPolicy.enabled, que controla la denegación predeterminada en el namespace de Work y se activa al habilitar Work. Ambas requieren un CNI que aplique NetworkPolicy; representar objetos no demuestra aislamiento.
Persistencia
Mantén los PVC de datos de Libre WebUI y modelos de Ollama en almacenamiento persistente. Copia conjuntamente el volumen de datos y la clave.
Los espacios de tareas residen en sus propios PVC del namespace, no en el PVC de datos. Una recuperación completa necesita la base (propiedad, nombres y ejecuciones) y esos PVC; cópialos juntos.
Ingress
Para acceso público, configura Ingress con HTTPS y el origen exacto mediante el chart:
helm upgrade libre-webui \
oci://ghcr.io/libre-webui/charts/libre-webui \
--reuse-values \
--set env.TRUST_PROXY=1 \
--set-string env.CORS_ORIGIN=https://your-domain.example
TRUST_PROXY es un número exacto de saltos, no booleano. El valor seguro 0 ignora direcciones reenviadas. Usa 1 solo con un proxy Ingress directo; cuenta cada equilibrador o proxy de una cadena fija y mantén el Service inaccesible fuera. Un valor pequeño agrupa clientes y agota límites compartidos; uno grande puede confiar en una dirección del cliente. El chart acepta 0 a 16, nunca true, y solo lo envía a Pods HTTP.
El chart actual no expone BASE_URL ni URL de devolución OAuth. Los despliegues OAuth deben extender el chart o parchear el Deployment, y las URL deben coincidir con el dominio público.
Planificación de recursos
Para Ollama local, programa el Pod en nodos con memoria y GPU suficientes. Si el clúster ya tiene un servicio de inferencia, Ollama externo suele ser más sencillo.