Ollama en Docker Compose con GPU y almacenamiento persistente de modelos
Servidor de Ollama con enfoque en composición, GPU y persistencia.
Ollama funciona muy bien en hardware sin intermediarios (bare metal). Se vuelve aún más interesante cuando lo tratas como un servicio: un punto de conexión estable, versiones fijas, almacenamiento persistente y una GPU que está disponible o no está.
Este artículo se centra en un solo objetivo: un “servidor” local o de un solo nodo de Ollama reproducible usando Docker Compose, con aceleración por GPU y almacenamiento persistente de modelos.

Se omiten intencionalmente los conceptos básicos genéricos de Docker y Compose. Cuando necesites una lista compacta de los comandos que más utilizas (imágenes, contenedores, volúmenes, docker compose), la Hoja de referencia de Docker es una buena compañera.
Cuando quieras HTTPS frente a Ollama, transmisión (streaming) y proxying de WebSocket correctos, y controles de borde (autenticación, tiempos de espera, límites de frecuencia), consulta Ollama detrás de un proxy inverso con Caddy o Nginx para HTTPS streaming.
Para saber cómo encaja Ollama junto con vLLM, Docker Model Runner, LocalAI y las compensaciones (trade-offs) del alojamiento en la nube, consulta Alojamiento de LLM en 2026: Comparación de infraestructura local, autoalojada y en la nube.
Una vez que este stack de Compose esté manejando tráfico concurrente real, la siguiente pregunta suele ser si Ollama sigue siendo el motor adecuado debajo. De Ollama a vLLM: Cuándo migrar tu servidor local de LLM ofrece una ruta de migración por etapas que mantiene ambos contenedores funcionando lado a lado durante la validación.
Cuando Compose supera a una instalación en hardware sin intermediarios
Una instalación nativa es fluida para un desarrollador en una máquina. En el momento en que tienes cualquiera de las siguientes situaciones, Compose comienza a ganar en ergonomía:
Un entorno de equipo se beneficia porque la definición del servicio es un archivo que se puede revisar, versionar y compartir. Un servidor de un solo nodo se beneficia porque las actualizaciones se convierten en un cambio de etiqueta de imagen y un reinicio, mientras que el almacenamiento de modelos permanece intacto (siempre que esté en un volumen). Ollama también suele vivir junto a servicios secundarios (sidecars): una interfaz de usuario web, un proxy inverso, una pasarela de autenticación, una BD vectorial o un runtime de agentes. Compose es bueno para “un comando para iniciar todo el stack”, sin convertir tu host en una anomalía (snowflake).
Este enfoque se alinea bien con el diseño del contenedor oficial de Ollama: la imagen ejecuta ollama serve por defecto, expone el puerto 11434 y está destinada a mantener el estado bajo un directorio montable. El conjunto completo de variables de entorno OLLAMA_* que ollama serve lee — OLLAMA_HOST, OLLAMA_CONTEXT_LENGTH, OLLAMA_KEEP_ALIVE, OLLAMA_NUM_PARALLEL y el resto — está documentado en la Hoja de referencia de la CLI de Ollama.
Un esqueleto de Compose que es realmente útil para Ollama
Comienza con dos decisiones:
Primera, cómo fijarás las versiones. La imagen de Docker Hub es ollama/ollama, por lo que puedes fijar una etiqueta específica en .env en lugar de depender de latest.
Segunda, dónde vivirán los datos de los modelos. La documentación oficial monta un volumen en /root/.ollama para que los modelos no se vuelvan a descargar cada vez que se reemplaza el contenedor.
Aquí hay un archivo de Compose que integra esas decisiones y mantiene los “ajustes” cerca del servicio:
services:
ollama:
image: ollama/ollama:${OLLAMA_IMAGE_TAG:-latest}
container_name: ollama
restart: unless-stopped
# Mántengalo local por defecto, expóngalo más tarde si lo necesita.
ports:
- "${OLLAMA_BIND_IP:-127.0.0.1}:11434:11434"
# Modelos persistentes y estado del servidor.
volumes:
- ollama:/root/.ollama
environment:
# La imagen oficial ya tiene 0.0.0.0:11434 por defecto dentro del contenedor,
# pero mantenerlo explícito ayuda cuando sobreescribes cosas más tarde.
- OLLAMA_HOST=0.0.0.0:11434
# Ajustes del servicio.
- OLLAMA_KEEP_ALIVE=${OLLAMA_KEEP_ALIVE:-5m}
- OLLAMA_NUM_PARALLEL=${OLLAMA_NUM_PARALLEL:-1}
- OLLAMA_MAX_LOADED_MODELS=${OLLAMA_MAX_LOADED_MODELS:-1}
# Opcional, pero relevante cuando una interfaz de usuario basada en el navegador se comunica directamente con Ollama.
# Consulte la sección de Red para saber por qué existe esto.
- OLLAMA_ORIGINS=${OLLAMA_ORIGINS:-}
# La reserva de GPU es una sección aparte más abajo.
# Agrégala solo en hosts que realmente tengan GPUs de NVIDIA.
volumes:
ollama: {}
Un archivo .env correspondiente mantiene las actualizaciones aburridas (predecibles):
# Fija la versión de la imagen que has probado.
OLLAMA_IMAGE_TAG=latest
# Local por defecto. Cambia a 0.0.0.0 cuando decidas exponerlo intencionalmente.
OLLAMA_BIND_IP=127.0.0.1
# Ajustes de keep-alive: latencia de arranque en frío vs huella de memoria.
OLLAMA_KEEP_ALIVE=5m
# Ajustes de concurrencia.
OLLAMA_NUM_PARALLEL=1
OLLAMA_MAX_LOADED_MODELS=1
# Deja vacío a menos que estés sirviendo clientes de navegador que acceden directamente a Ollama.
OLLAMA_ORIGINS=
Un matiz pequeño pero importante: Ollama en sí tiene una vinculación de host por defecto de 127.0.0.1:11434 en la configuración general, pero la imagen de contenedor oficial establece OLLAMA_HOST=0.0.0.0:11434 para que el servicio sea alcanzable a través de puertos publicados.
Si quieres una rápida verificación de sanidad sin involucrar SDKs de cliente, la API de Ollama incluye un punto de conexión para “listar modelos locales” en GET /api/tags.
Almacenamiento persistente de modelos y la forma menos dolorosa de moverlo
Si solo recuerdas una cosa, que sea esta: el contenedor debe tener almacenamiento persistente, de lo contrario cada reconstrucción es una nueva descarga.
Ollama te permite elegir el directorio de modelos usando OLLAMA_MODELS. En la implementación de referencia, el valor por defecto es $HOME/.ollama/models, y establecer OLLAMA_MODELS lo sobreescribe.
Dentro de la imagen de Docker oficial, $HOME se mapea naturalmente al layout /root usado por la montura de volumen documentada (/root/.ollama), que es exactamente la razón por la que los ejemplos oficiales de docker run montan ese directorio.
Hay dos patrones de almacenamiento que suelen funcionar bien en la práctica:
Un volumen de Docker nombrado es lo más simple y portable. También es fácil aislarlo accidentalmente (orphan), por lo que vale la pena nombrarlo intencionalmente (por ejemplo, ollama) y mantenerlo estable entre refactorizaciones de Compose.
Una montura de enlace (bind mount) a un disco dedicado es mejor cuando el tamaño de los modelos comienza a dominar tu sistema de archivos raíz. En ese caso, o bien montas todo /root/.ollama en ese disco, o bien montas un directorio personalizado y apuntas OLLAMA_MODELS a él.
Si estás reorganizando activamente el almacenamiento, es aquí donde un playbook explícito de “mover modelos” ayuda. Consulta: mover-modelos-de-ollama .
Soporte de GPU de NVIDIA con Compose y la NVIDIA Container Toolkit
Ollama puede usar GPUs de NVIDIA en Docker, pero la imagen no puede hacer aparecer una GPU por magia. El host necesita controladores de NVIDIA funcionales y la NVIDIA Container Toolkit, y Docker debe estar configurado para usarlo. La documentación de Docker de Ollama menciona explícitamente la instalación de nvidia-container-toolkit, la configuración del runtime mediante nvidia-ctk runtime configure --runtime=docker y el reinicio de Docker.
En el lado de Compose, la forma limpia y moderna es la reserva de dispositivos. Docker documenta el acceso a GPU en Compose usando deploy.resources.reservations.devices, con capabilities: [gpu], driver: nvidia, y ya sea count (incluyendo all) o device_ids.
Agrega esto al servicio ollama cuando estés en un host de NVIDIA:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
Si tienes múltiples GPUs y quieres mantener Ollama en dispositivos específicos, cambia de count a device_ids como lo documenta Docker (son mutuamente excluyentes).
A veces verás ejemplos legados de Compose que usan runtime: nvidia. Eso puede fallar en configuraciones más nuevas con errores como “unknown or invalid runtime name: nvidia” (nombre de runtime desconocido o inválido: nvidia), lo cual es una fuerte pista de que debes migrar al patrón de reserva de dispositivos soportado y asegurarte de que el toolkit esté configurado en el host.
Un detalle útil escondido a plena vista: la imagen oficial ollama/ollama establece NVIDIA_VISIBLE_DEVICES=all y NVIDIA_DRIVER_CAPABILITIES=compute,utility. Estos son ajustes estándar reconocidos por el runtime de contenedor de NVIDIA, y ya están presentes a menos que los sobreescribas.
Para confirmar si realmente estás obteniendo inferencia por GPU (no solo un contenedor que arranca), Ollama recomienda usar ollama ps y revisar la columna “Processor”, que muestra si el modelo está en memoria GPU.
Verificación de realidad de plataforma: Ollama señala que la aceleración por GPU en Docker está disponible en Linux (y Windows con WSL2), y no está disponible en Docker Desktop para macOS debido a la falta de paso de GPU (GPU passthrough).
Opciones de red: host vs bridge, puertos y CORS
La red es donde la mayoría de los errores de “se ejecuta pero mi aplicación no puede conectarse” provienen.
Red de puente (bridge) con puertos publicados
La red por defecto de Compose es una red de puente (bridge). En esta configuración, publicar 11434:11434 hace que Ollama sea alcanzable desde el host en el puerto 11434, mientras que otros contenedores deben comunicarse con él usando el nombre del servicio ollama (no localhost). Muchas personas tropiezan con esto porque localhost dentro de un contenedor significa “este contenedor”, no “el contenedor de Ollama”.
Ollama en sí ejecuta un servidor HTTP en el puerto 11434 (la imagen lo expone), y la convención común es que los clientes usen http://localhost:11434 en el host cuando los puertos están publicados.
Red de host (host networking)
network_mode: host puede ser tentador en un servidor de un solo nodo porque elimina la publicación de puertos y simplifica la semántica de localhost. La compensación es que pierdes los beneficios de aislamiento y namespaces de una red de puente, y es más probable que choques con conflictos de puertos.
Exponer Ollama intencionalmente
Ollama en una instalación normal se vincula a 127.0.0.1 por defecto, y la forma documentada para cambiar la dirección de vinculación es OLLAMA_HOST.
En Docker, tienes dos capas:
Dirección de vinculación de Ollama, controlada por OLLAMA_HOST (la imagen del contenedor tiene por defecto la vinculación en todas las interfaces dentro del contenedor).
Alcance desde fuera del contenedor, controlado por ports de Compose y el cortafuegos del host.
Un patrón que me gusta es “vincular localmente por defecto” mediante 127.0.0.1:11434:11434, y luego cambiar a 0.0.0.0:11434:11434 solo cuando tenga una razón para exponerlo.
Clientes de navegador y OLLAMA_ORIGINS
Si una interfaz de usuario basada en navegador o una extensión llama directamente a Ollama, estás en el terreno de CORS. Ollama permite solicitudes de origen cruzado desde 127.0.0.1 y 0.0.0.0 por defecto, y puedes configurar orígenes adicionales usando OLLAMA_ORIGINS.
Esto importa incluso en un solo nodo, porque “funciona con curl” no significa “funciona desde una aplicación de navegador”.
Patrones de actualización y reversión que se ajustan a un servidor de un solo nodo
Ollama evoluciona rápidamente. Tu archivo de Compose puede hacer que eso sea un proceso calmado en lugar de una sorpresa a altas horas de la noche.
Actualizar cambiando una etiqueta, no esperando que “latest” se comporte
La estrategia de actualización más práctica es fijar la imagen a una etiqueta de buen funcionamiento conocida en .env, y aumentarla intencionalmente. La imagen se publica como ollama/ollama en Docker Hub.
Dado que los datos de los modelos y el estado del servidor se almacenan bajo un directorio montado (/root/.ollama en la documentación oficial), reemplazar el contenedor no implica volver a descargar los modelos.
La reversión (rollback) es solo volver a cambiar la etiqueta
La reversión es el mismo mecanismo al revés: establece la etiqueta anterior, recrea el contenedor, mantén el mismo volumen. Aquí es donde fijar la versión se paga solo.
La migración de datos es mayoritariamente sobre rutas de almacenamiento
La mayoría de las “migraciones” en una configuración de un solo nodo no son sobre esquemas de base de datos. Son sobre el layout del disco. Si cambias el directorio de modelos (mediante OLLAMA_MODELS) o mueves el volumen montado a un nuevo disco, estás realizando una migración de datos, llames a eso como quieras.
Si quieres una guía práctica para reorganizar el directorio de modelos en máquinas reales, consulta: mover-modelos-de-ollama .
Una nota final que es fácil pasar por alto: la documentación de la API de Ollama dice explícitamente que la API se espera que sea estable y compatible hacia atrás, con deprecaciones raras anunciadas en las notas de versión. Eso hace que “actualizar el servidor, mantener los clientes funcionando” sea una expectativa por defecto razonable para un punto de conexión de servicio de un solo nodo.
Fallos comunes: permisos de GPU, desajuste de controladores y OOM
Esta sección es deliberadamente orientada a síntomas. El objetivo no es “cada posible error de Docker”, sino solo los fallos que aparecen específicamente en configuraciones de Ollama + GPU + almacenamiento persistente.
GPU visible en el host, ausente en el contenedor
Si el host tiene un controlador de NVIDIA funcional pero el contenedor no ve una GPU, las causas comunes son:
La NVIDIA Container Toolkit no está instalada o el runtime de Docker no está configurado mediante nvidia-ctk. La documentación de Docker de Ollama lo señala directamente.
Compose no está reservando un dispositivo de GPU. La forma soportada es deploy.resources.reservations.devices con la capacidad gpu como lo documenta Docker.
Una configuración legada de runtime: nvidia se está usando en un daemon que no lo reconoce, produciendo “unknown or invalid runtime name: nvidia”.
Para la validación, ollama ps te da una comprobación pragmática: muestra si un modelo está cargado en memoria GPU.
Permiso denegado en dispositivos de GPU
El sabor “permiso denegado” (permission denied) de los fallos de GPU generalmente apunta a restricciones de entorno en lugar de Ollama en sí. Los ejemplos incluyen ejecutar Docker sin root (rootless), políticas de seguridad, o nodos de dispositivos que no se exponen como se espera. La documentación de soporte de GPU de Docker Compose es explícita de que el host debe tener dispositivos de GPU y que el demonio de Docker debe estar configurado en consecuencia.
Cuando tengas dudas, reduce las variables: confirma la configuración del toolkit (host), luego confirma la reserva de GPU (Compose), luego confirma el uso de GPU (ollama ps).
Controlador equivocado, expectativa equivocada
Ollama en Docker depende del stack de controladores del host. Si el controlador del host falta, es demasiado antiguo o está mal configurado, verás fallos que parecen “Ollama está roto” pero en realidad son “el stack de CUDA no es utilizable”. La documentación oficial coloca el toolkit de contenedor y la configuración del demonio de Docker como prerrequisitos para el uso de GPU de NVIDIA.
Memoria insuficiente: la VRAM o la RAM desaparece rápido
OOM (Out of Memory) es el modo de fallo más predecible para la inferencia local, y generalmente es autoinfligido por la configuración.
Ollama admite el procesamiento concurrente a través de múltiples modelos cargados y el manejo de solicitudes paralelas, pero está limitado por la memoria disponible (RAM del sistema para inferencia por CPU, VRAM para inferencia por GPU). Cuando se usa inferencia por GPU, los nuevos modelos deben caber en VRAM para permitir cargas de modelos concurrentes.
Dos detalles de configuración vale la pena tratarlos como ajustes de “servidor” de primera clase:
OLLAMA_NUM_PARALLEL aumenta el procesamiento de solicitudes paralelas por modelo, pero la memoria requerida escala con OLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH.
OLLAMA_KEEP_ALIVE controla cuánto tiempo permanecen cargados los modelos (el valor por defecto es 5 minutos). Mantener modelos cargados reduce la latencia de arranque en frío, pero también fija la memoria.
Si estás estabilizando un servicio de un solo nodo bajo carga, las correcciones no dramáticas suelen parecerse a esto:
Reduce la concurrencia y los valores por defecto de contexto antes de cambiar cualquier otra cosa.
Limita cuántos modelos se les permite permanecer cargados concurrentemente.
Considera funciones de reducción de memoria como Flash Attention (OLLAMA_FLASH_ATTENTION=1) y tipos de caché K/V de menor precisión (OLLAMA_KV_CACHE_TYPE) cuando tu cuello de botella es la memoria, no el cómputo en bruto.
Cuando no es Ollama: eligiendo Docker Model Runner en su lugar
A veces el “fallo” es realmente un desajuste de herramientas. Si tu organización ya estandariza artefactos y flujos de trabajo nativos de Docker, Docker Model Runner (DMR) puede ser un mejor ajuste que ejecutar Ollama como un contenedor de servicio de larga duración.
Docker posiciona DMR como una forma de gestionar, ejecutar y servir modelos directamente mediante Docker, extrayéndolos de Docker Hub u otros registros OCI, y sirviendo APIs compatibles con OpenAI y compatibles con Ollama.
También soporta múltiples motores de inferencia (incluyendo llama.cpp, y vLLM en Linux con GPUs de NVIDIA), lo cual puede importar si te preocupas por las características de rendimiento (throughput), no solo por “ejecutar un modelo localmente”.
Si quieres una referencia de comandos práctica y un ángulo de comparación más profundo, consulta: Hoja de referencia de Docker Model Runner: Comandos y Ejemplos.