Kubernetes
O Libre WebUI fornece um chart Helm em helm/libre-webui.
Work no Kubernetes
Work roda nativamente no Kubernetes — nenhum daemon, CLI ou socket Docker participa. Ative na instalação:
helm install libre-webui ./helm/libre-webui --set work.enabled=true
Isso seleciona WORK_RUNTIME_BACKEND=kubernetes e cria:
- um namespace dedicado (
work.namespace, padrãolibre-webui-work) com um Pod por sandbox em execução e um PersistentVolumeClaim por espaço de tarefa (work.workspaceSize, padrão5Gi— cota real por tarefa; políticas nomeadas podem definir outro tamanho); - Role e RoleBinding limitados ao namespace, concedendo ao ServiceAccount apenas
pods(get/list/create/delete),pods/exec(get/create) epersistentvolumeclaims(get/list/create/delete), sem secrets nem escopo de cluster. Isso substitui totalmente o socket: o servidor da API impede montagens de caminhos do host; - NetworkPolicies que negam tudo por padrão, permitem entrada somente do backend na porta de prévia e dão saída à internet aos sandboxes habilitados, exceto
work.networkPolicy.blockedEgressCidrs(por padrão faixas privadas, CGNAT usada por alguns clusters e metadados link-local; confira os CIDRs do cluster). DNS é permitido somente emkube-system; DNS local ao nó exige exceção própria.
Sandboxes rodam sem root, com raiz somente leitura, todas as capabilities removidas, seccomp RuntimeDefault e sem token ServiceAccount. Arquivos, comandos, git e terminais usam o subrecurso exec. A prévia sai do IP do Pod pelo proxy assinado de mesma origem, exigindo backend dentro do cluster. Pastas do host não são compatíveis.
NetworkPolicy exige CNI que a aplique (Calico, Cilium, versões recentes do kind e a maioria dos clusters gerenciados). Verifique antes de considerar o isolamento ativo; a suíte de CI informa se há aplicação. Nunca monte o socket de runtime de um nó no Pod do WebUI; este backend existe justamente para evitar isso.
Instalação
helm install libre-webui oci://ghcr.io/libre-webui/charts/libre-webui
O chart padrão implanta armazenamento persistente e Ollama incluído. A transição 0.14.1 fixa seu digest verificado de múltiplas arquiteturas; charts posteriores usam a imagem do appVersion semântico. Defina image.tag ou image.digest somente para outra imagem intencional. Uma image.tag não vazia prevalece sobre o digest de transição.
O perfil solo aceita replicaCount: 0 para suspensão ou replicaCount: 1 para operação. Rejeita valores maiores e HPA porque SQLite, arquivos locais e coordenação em processo não são seguros em múltiplos Pods. Com zero, recursos de controle são criados, mas não há tráfego.
Para réplicas, configure o perfil team completo com PostgreSQL/PGVector, blobs S3, Redis e worker durável separado; misturas parciais são recusadas. Comece com um arquivo 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 deve coincidir exatamente com legacy, e o mapa precisa conter STORAGE_ENCRYPTION_ACTIVE_KEY_ID. secrets.jwtSecret deve ser um valor estável e forte compartilhado por todos os Pods; sem ele, o modo team é rejeitado. Preserve TLS verificado no PostgreSQL e não adicione parâmetros do driver em databaseUrl. Os limites de pool valem por processo: reserve pelo menos (replicaCount + worker.replicaCount) * POSTGRES_POOL_MAX conexões, além de margem. Instale:
helm upgrade --install libre-webui \
oci://ghcr.io/libre-webui/charts/libre-webui \
--values /absolute/path/to/libre-team-values.yaml
Não faça commit do arquivo nem passe segredos por --set; use um fluxo criptografado. Escale provedores e Pods Work separadamente. Com work.enabled=true, o worker recebe a mesma imagem, StorageClass e limites work.env dos Pods da aplicação, além do mesmo endpoint Ollama, timeouts e contexto, pois embeddings, chats duráveis e Work executam chamadas nele. Uma aplicação team ativa exige pelo menos um worker; o chart rejeita zero. Para suspensão total, zere replicaCount e worker.replicaCount. Somente app em zero é modo deliberado de drenagem/recuperação: sem web, mas processando fila.
Atualizações team e compatibilidade de esquema
O Libre exige versão exata do esquema, não oferece atualização mista ou sem interrupção. Os Deployments da aplicação e worker usam Recreate, impedindo sobreposição dentro de cada um, mas Kubernetes não coordena ambos como uma fronteira. Antes de atualizar, pare novas entradas, conclua/cancele trabalhos, reduza ambos a zero, faça backup verificado e confirme que todos terminaram. Só então atualize com POSTGRES_MIGRATION_MODE=apply; um processo mantém o advisory lock e os demais aguardam e validam o mesmo registro. Para rollback, restaure o backup anterior em PostgreSQL/S3 limpos; nunca aponte binário antigo para esquema incompatível. Haverá interrupção planejada.
Acesso local
kubectl port-forward svc/libre-webui 8080:8080
Abra http://localhost:8080.
Ollama externo
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
Segredos
Defina segredo JWT e chave estáveis. Por padrão, o chart cria <release>-libre-webui-secrets dos valores não vazios secrets.*:
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 Secret gerenciado pelo operador, defina secrets.existingSecret; o chart não renderiza Secret e ambos os Pods referenciam o objeto:
secrets:
existingSecret: libre-webui-runtime
Crie antes da instalação. Deve conter jwt-secret e encryption-key; team também exige database-url, redis-url, storage-encryption-keys. Chaves opcionais: session-secret, s3-access-key-id, s3-secret-access-key, s3-session-token. OAuth GitHub e Hugging Face podem ler pares *-client-id/*-client-secret quando secrets.githubClientId ou secrets.huggingfaceClientId ativa a integração. O chart não valida nem copia os valores; chave ausente impede o Pod de iniciar.
Em automação, prefira secrets.existingSecret com external-secrets ou values criptografados. Valores --set podem aparecer na inspeção de processos e metadados Helm. Adicione credenciais de provedores por extensão deliberada ou por usuário na interface.
NetworkPolicies da aplicação e worker
networkPolicy.enabled=true renderiza políticas de entrada:
networkPolicy:
enabled: true
A aplicação aceita somente sua porta HTTP; o worker não aceita entrada. A saída não é limitada: ambos precisam alcançar PostgreSQL, Redis, S3, Ollama, ferramentas e provedores configurados.
Isso é separado de work.networkPolicy.enabled, que controla as políticas dos sandboxes e vem ativo com Work. Ambos exigem CNI que realmente aplique NetworkPolicy.
Persistência
Mantenha PVCs de dados e modelos em armazenamento persistente. Faça backup do volume e chave juntos.
Os espaços Work ficam em PVCs próprios no namespace de sandbox, fora do PVC de dados. Recuperação completa exige banco e esses PVCs sob a mesma política.
Ingress
Para acesso público, use HTTPS e defina a origem exata:
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 é número exato de saltos, não booleano. O padrão seguro 0 ignora endereços encaminhados. Use 1 quando um proxy conecta diretamente; conte todos em uma cadeia fixa e mantenha o Service inacessível fora dela. Poucos saltos agrupam clientes sob o proxy e esgotam limites; muitos confiam em endereço do cliente. O chart aceita 0 a 16, nunca true, e envia apenas aos Pods HTTP.
O chart não expõe BASE_URL nem callbacks OAuth. Implantações OAuth devem estender ou alterar o Deployment, com URLs iguais ao domínio público.
Planejamento de recursos
Para Ollama local, agende o Pod em nós com memória e GPU suficientes. Se já houver serviço dedicado, Ollama externo costuma ser mais simples.