Inicio / Artículos / Autoalojar Ollama y Open WebUI en Docker para un equipo

Autoalojar Ollama y Open WebUI en Docker para un equipo

Sustituye las instalaciones sueltas de Ollama por un stack de Docker Compose: Ollama interno, Open WebUI con autenticación, TLS con Caddy, paso de GPU, copias de seguridad y soluciones.

1974 palabras

Las instalaciones de IA local suelen empezar con una persona, una estación de trabajo y una tarde feliz. Alguien instala Ollama, descarga un modelo y las respuestas del chat son geniales. Luego un compañero quiere lo mismo. La respuesta rápida es copiar los archivos del modelo en un pendrive, instalar Ollama en una segunda máquina y apuntar qué comandos hay que ejecutar. Un mes después hay cuatro máquinas, tres versiones de Ollama, archivos de modelo que no coinciden y una hoja de cálculo compartida que asigna direcciones IP a puertos.

Ese arreglo no aguanta. Consume disco, deja que la configuración se desvíe y convierte sin hacer ruido a quien montó la primera máquina en el sysadmin no remunerado de un servicio frágil. A continuación se sustituye por un stack en contenedores en el que Ollama corre en una red interna, Open WebUI gestiona los inicios de sesión y un proxy inverso da la cara al exterior.

Por qué falla repartirlo a mano

Los problemas suelen aparecer solo después de unas semanas de uso.

Almacenamiento duplicado. Un modelo de 7B con cuantización Q4 ocupa unos 4 GB. Un modelo de 70B necesita más bien 40 GB. Copiar esos archivos en cada máquina del equipo llena los discos NVMe e inunda el switch local.

Deriva de versiones. Si una persona usa un runtime antiguo y otra una compilación más reciente, el comportamiento se separa. Los modelos pueden fallar sin error, o dos personas pueden obtener resultados distintos con el mismo prompt. Depurar se convierte entonces en una caza de versiones de software en vez de causas raíz.

Exposición en la red. Es el más peligroso. El atajo habitual es poner OLLAMA_HOST=0.0.0.0 y abrir el puerto 11434 en el cortafuegos. Eso deja la API de ejecución en bruto en la red sin autenticación, sin cifrado y sin límite de peticiones. Cualquiera que llegue a la dirección puede lanzar cargas no fiables, quemar cómputo, leer el contexto activo o manipular los pesos almacenados.

Una instalación en contenedores elimina los tres problemas al hacer el entorno declarativo. Un archivo de Docker Compose se convierte en la especificación exacta. Los datos de los modelos viven en volúmenes persistentes dedicados, el acceso de red está segmentado y el stack arranca igual en cualquier host Linux.

La arquitectura

La ejecución en bruto de los modelos se mantiene separada de todo lo que tocan los usuarios. Ollama solo corre dentro de una red bridge interna. Open WebUI se sitúa delante y gestiona la autenticación. Imagínalo como capas de abajo arriba:

  • Un host Linux con el driver de NVIDIA o ROCm
  • Una red bridge de Docker llamada ai_network (172.28.0.0/16)
  • Un contenedor de Ollama (el motor de inferencia, puerto interno 11434, con la GPU pasada directamente)
  • Un contenedor de Open WebUI (aplicación web más base de datos, puerto interno 8080, autenticación obligatoria)
  • Un volumen con nombre ollama_models para Ollama y otro llamado open_webui_data para Open WebUI
  • Open WebUI publicado solo en 127.0.0.1:3000, con un proxy inverso (Caddy o Nginx con TLS) delante y usuarios autenticados detrás

De qué se encarga cada pieza

Cada capa necesita límites claros antes de conectar nada.

Ollama es solo el motor de inferencia del backend. Carga los pesos en memoria, reparte las operaciones de tensores entre la GPU o la CPU y sirve una API REST HTTP. A propósito no tiene gestión de usuarios, ni sesiones, ni autorización multiinquilino.

Todo su estado vive en el almacén de modelos definido por OLLAMA_MODELS, que por defecto es /root/.ollama dentro del contenedor. Al descargar un modelo, las capas van directas a ese volumen. Como Ollama no guarda nada más, puedes borrar o sustituir la imagen del contenedor y todos los modelos descargados siguen en el disco.

Open WebUI es la aplicación web y la capa de orquestación que la gente usa de verdad. Guarda credenciales, conserva el historial de chats, aplica permisos por roles y ejecuta pipelines de Retrieval Augmented Generation. No hace cálculos de modelo por sí misma: convierte las acciones del navegador en llamadas a la API del servicio interno de Ollama. Su propio estado está en una base de datos SQLite integrada y en archivos locales dentro del volumen open_webui_data. Perder ese volumen afecta a las cuentas y al historial de conversaciones, pero no toca los binarios de los modelos.

Docker aporta aislamiento, política de red y un arranque reproducible. Como los límites y los montajes de volúmenes están en un solo manifiesto, las dependencias del sistema no dependen de lo que tenga instalado el sistema operativo del host.

Por qué no una aplicación de escritorio

A menudo se comparan los servicios headless con herramientas gráficas de escritorio como LM Studio. LM Studio es un buen banco de pruebas para un investigador que prueba prompts. No ofrece una cola de tokens multiusuario nativa, ni gestión de servicios en segundo plano, ni integración modular de microservicios. Ollama en Docker aporta el comportamiento de demonio, una API unificada y la separación limpia que exige un despliegue compartido.

Elegir el hardware

La velocidad de generación local depende sobre todo del ancho de banda de la VRAM, del ancho del bus de memoria y de si el modelo cabe, no de los FLOPS anunciados. Estas combinaciones aproximadas son un punto de partida.

  • Los modelos de 7B a 8B en Q4_K_M necesitan unos 6 GB de VRAM. Una RTX 3060 o RTX 4060 8GB debería dar unos 60 a 85 tokens por segundo.
  • Los modelos de 13B a 14B en Q4_K_M necesitan unos 10 GB. Una RTX 4070 o una RTX 3080 12GB debería llegar a unos 40 a 60 tokens por segundo.
  • Los modelos de 32B a 35B en Q4_K_M necesitan unos 22 GB. Una RTX 3090 o RTX 4090 24GB debería rondar los 25 a 40 tokens por segundo.
  • Los modelos de 70B en Q4_K_M necesitan unos 48 GB, por ejemplo dos tarjetas RTX 3090 o una RTX 6000 Ada, con unos 15 a 25 tokens por segundo.

Requisitos para pasar la GPU

En Linux, instalar los drivers de GPU estándar en el host no permite que los contenedores usen la GPU. Hay que instalar el NVIDIA Container Toolkit oficial para que las llamadas CUDA lleguen al kernel desde dentro del contenedor.

En hardware AMD, usa la etiqueta ROCm de la imagen (ollama/ollama:rocm) y pasa al contenedor los nodos de dispositivo /dev/kfd y /dev/dri.

Docker Desktop en Windows con WSL2 añade sus propias rarezas en el puente de red virtual. Los síntomas son contenedores inaccesibles o errores de socket, y suelen apuntar a problemas de enrutamiento entre host y contenedor en ese puente.

Escribir el archivo Compose

Empieza creando un directorio de trabajo en el servidor:

mkdir -p ~/local-ai && cd ~/local-ai

Después viene el docker-compose.yml. Las especificaciones actuales de Compose marcan como obsoleta la clave version de nivel superior, así que omítela. Primero va el servicio de Ollama:

services:
  ollama:
    image: ollama/ollama:0.6.5
    container_name: ollama
    restart: unless-stopped
    environment:
      - OLLAMA_KEEP_ALIVE=24h
      - OLLAMA_MAX_LOADED_MODELS=1
      - OLLAMA_NUM_PARALLEL=2
    ports:
      - "127.0.0.1:11434:11434"
    volumes:
      - ollama_models:/root/.ollama
    networks:
      - ai_network
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

Open WebUI va a continuación en el mismo archivo:

  open-webui:
    image: ghcr.io/open-webui/open-webui:v0.5.20
    container_name: open-webui
    restart: unless-stopped
    depends_on:
      - ollama
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
      - WEBUI_AUTH=true
      - WEBUI_SECRET_KEY=e8f901c23a4b5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0
    ports:
      - "3000:8080"
    volumes:
      - open_webui_data:/app/backend/data
    networks:
      - ai_networkvolumes:
  ollama_models:
    name: ollama_models
  open_webui_data:
    name: open_webui_datanetworks:
  ai_network:
    name: ai_network
    driver: bridge

Algunos ajustes de esos bloques merecen una mirada más atenta.

  • OLLAMA_KEEP_ALIVE=24h mantiene los tensores del modelo cargados en la VRAM durante largos periodos de inactividad, para que los usuarios activos no esperen una recarga.
  • OLLAMA_MAX_LOADED_MODELS=1 deja que solo un modelo ocupe la VRAM a la vez. Así se evitan la fragmentación y los fallos por falta de memoria.
  • 127.0.0.1:11434:11434 ata Ollama al adaptador loopback del host. El tráfico externo no ve la API, y solo Open WebUI llega a ella a través de la red interna de Docker.
  • WEBUI_SECRET_KEY es la semilla con la que se firman las cookies de sesión JWT. Genera una robusta con openssl rand -hex 32 antes del primer arranque.

Fijar las etiquetas de las imágenes importa aquí más de lo que parece. Una etiqueta flotante como latest puede cambiar bajo el stack de un día para otro, y un reinicio un martes podría traer un runtime que nadie ha probado. Con versiones fijas, reconstruir dentro de un año da el mismo resultado que hoy, y una actualización pasa a ser una edición deliberada de una línea que se puede revisar y revertir.

Arrancarlo y descargar un modelo

Levanta el stack en segundo plano:

docker compose up -d

Comprueba el estado de los contenedores y los puertos publicados:

docker compose ps

Descarga un primer modelo ejecutando el comando dentro del contenedor de Ollama:

docker compose exec ollama ollama pull llama3.2:8b

Confirma que el modelo quedó registrado junto con su cuantización:

docker compose exec ollama ollama list

TLS delante de todo

El puerto 3000 nunca debería dar a la red pública sin TLS y enrutamiento por host. Caddy lo resuelve bien, ya que gestiona los certificados HTTPS automáticamente. Instálalo en el host y escribe /etc/caddy/Caddyfile:

ai.yourdomain.com {
    reverse_proxy 127.0.0.1:3000 {
        header_up Host {host}
        header_up X-Real-IP {remote_host}
    }
}

Recarga Caddy para que obtenga un certificado y empiece a hacer de proxy por una conexión cifrada:

sudo systemctl reload caddy

Más allá del chat

Con el tiempo, los equipos quieren agentes autónomos de varios pasos y automatización de flujos de trabajo, no solo conversaciones. Open WebUI puede entonces convivir con frameworks de orquestación como Dify, o con un backend de alto rendimiento como vLLM. Como Ollama ofrece una API REST estándar en la red interna, otras herramientas de flujo de trabajo en el mismo puente pueden usar sus modelos directamente.

Solución de problemas

Problemas frecuentes con sus causas y soluciones.

  • WebUI muestra un error de conexión con el backend. La causa es un OLLAMA_BASE_URL incorrecto o un desajuste de red entre contenedores. Confirma que la variable coincide con el nombre DNS interno, http://ollama:11434, y luego prueba la conectividad desde dentro del contenedor de Open WebUI haciendo curl a la ruta /api/tags en el nombre de host de Ollama.
  • Ollama no detecta la GPU CUDA. O falta el NVIDIA Container Toolkit o el archivo compose no tiene un bloque de reserva de GPU. Instala nvidia-container-toolkit en el host, ejecuta sudo systemctl restart docker y comprueba que las reservas coinciden con la especificación de compose.
  • Los tokens por segundo caen bruscamente con el tiempo. El modelo es mayor que la VRAM disponible, así que hay capas que se desbordan a la RAM del sistema. Revisa la asignación con nvidia-smi y cambia a un modelo más pequeño o a una cuantización más compacta (Q4_K_M en lugar de Q8_0).
  • Permission denied (permiso denegado) en el volumen open_webui_data. Los permisos del directorio en el host no coinciden con el UID 1000 del proceso del contenedor. Ejecuta sudo chown -R 1000:1000 /var/lib/docker/volumes/open_webui_data.
  • Cortes de WebSocket durante el streaming. El proxy inverso está almacenando en búfer las respuestas en streaming o haciendo fallar las peticiones de upgrade. Asegúrate de que reenvía limpiamente las cabeceras Upgrade y Connection con el búfer desactivado.

Copias de seguridad

Hay que conservar las cuentas de usuario, el historial de chats, los índices RAG y la configuración. Los pesos de los modelos siempre se pueden volver a descargar, así que déjalos fuera de las copias para ahorrar espacio. Escribe un script seguro y no destructivo en /usr/local/bin/backup-local-ai.sh. Empieza con la línea del intérprete y el modo estricto:

#!/usr/bin/env bash
set -euo pipefail

El cuerpo define un directorio de copias y un nombre de archivo con marca de tiempo, pausa Open WebUI, archiva el volumen de datos y reinicia el contenedor:

BACKUP_DIR="/var/backups/local-ai"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
TARGET_FILE="${BACKUP_DIR}/open_webui_data_${TIMESTAMP}.tar.gz"mkdir -p "${BACKUP_DIR}"echo "Stopping Open WebUI container..."
docker stop open-webuiecho "Archiving Open WebUI database and user storage..."
tar -czf "${TARGET_FILE}" -C /var/lib/docker/volumes/open_webui_data/_data .echo "Restarting Open WebUI container..."
docker start open-webuiecho "Cleaning backups older than 14 days..."
find "${BACKUP_DIR}" -type f -name "*.tar.gz" -mtime +14 -deleteecho "Backup execution complete: ${TARGET_FILE}"

Hazlo ejecutable y abre el crontab:

sudo chmod +x /usr/local/bin/backup-local-ai.sh
sudo crontab -e

Después añade una línea para ejecutarlo a diario a las 3:00:

0 3 * * * /usr/local/bin/backup-local-ai.sh >> /var/log/local-ai-backup.log 2>&1

Lista de comprobación final

  • Aislamiento de red: el puerto 11434 de Ollama está ligado a loopback (127.0.0.1) y no es accesible públicamente.
  • Autenticación: WEBUI_AUTH=true está definido explícitamente en Open WebUI.
  • Integridad de la clave: hay configurada una WEBUI_SECRET_KEY propia de 64 caracteres.
  • Imágenes fijadas: las imágenes usan versiones fijas, no etiquetas flotantes.
  • Límites de VRAM: keep-alive y los límites de concurrencia están ajustados para evitar que caiga el driver de la GPU.
  • Copias de seguridad: los volúmenes de base de datos se archivan a diario con retención.

Como referencia, la documentación principal está en el repositorio de GitHub de Ollama, en la documentación del NVIDIA Container Toolkit y en la especificación de Docker Compose.