Ollama en Docker Compose con GPU y almacenamiento persistente de modelos
Servidor Ollama con prioridad en composición, GPU y persistencia.
Ollama funciona muy bien en hardware físico (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 lo está.
Este artículo se centra en un único objetivo: un “servidor” Ollama local o de un solo nodo reproducible utilizando Docker Compose, con aceleración por GPU y almacenamiento persistente de modelos.

Intencionalmente omite los conceptos básicos genéricos de Docker y Compose. Cuando necesites una lista compacta de los comandos que usas con más frecuencia (imágenes, contenedores, volúmenes, docker compose), la Hoja de trucos de Docker es un buen compañero.
Cuando deseas HTTPS frente a Ollama, proxying correcto de streaming y WebSocket, y controles de borde (autenticación, tiempos de espera, límites de tasa), consulta Ollama detrás de un proxy inverso con Caddy o Nginx para streaming HTTPS.
Para saber cómo encaja Ollama junto a vLLM, Docker Model Runner, LocalAI y las compensaciones de alojamiento en la nube, consulta Alojamiento de LLM en 2026: Infraestructura Local, Autoadministrada y en la Nube Comparada.
Cuando Compose supera a una instalación en hardware físico
Una instalación nativa es sin fricción para un desarrollador en una máquina. En el momento en que tienes cualquiera de los siguientes, Compose comienza a ganar en ergonomía:
La configuración para un equipo se beneficia porque la definición del servicio es un archivo que puedes 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 tu almacenamiento de modelos se mantiene en su lugar (siempre que esté en un volumen). Ollama también tiende a vivir junto a contenedores auxiliares (sidecars): una interfaz web, un proxy inverso, una puerta de enlace de autenticación, una base de datos vectorial o un tiempo de ejecución de agente. Compose es bueno para “un comando para iniciar toda la pila”, sin convertir tu host en un copo de nieve (snowflake).
Este enfoque se alinea bien con cómo está diseñado el contenedor oficial de Ollama: la imagen ejecuta ollama serve por defecto, expone el puerto 11434 y está diseñada para mantener el estado en un directorio montable.
Un esqueleto de Compose que es realmente útil para Ollama
Comienza con dos decisiones:
Primero, 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 confiar en latest.
Segundo, dónde vivirá la data 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í tienes un archivo Compose que incorpora esas decisiones y mantiene los “controles” cerca del servicio:
services:
ollama:
image: ollama/ollama:${OLLAMA_IMAGE_TAG:-latest}
container_name: ollama
restart: unless-stopped
# Mantenerlo local por defecto, expónlo más tarde si necesitas.
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 por defecto 0.0.0.0:11434 dentro del contenedor,
# pero mantenerlo explícito ayuda cuando sobrescribes cosas más tarde.
- OLLAMA_HOST=0.0.0.0:11434
# Ajuste 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 basada en navegador se comunica directamente con Ollama.
# Consulta la sección de Redes para saber por qué existe esto.
- OLLAMA_ORIGINS=${OLLAMA_ORIGINS:-}
# La reserva de GPU es una sección separada a continuación.
# Agrégala solo en hosts que tengan GPUs NVIDIA.
volumes:
ollama: {}
Un .env coincidente mantiene las actualizaciones aburridas:
# Fija la versión de la imagen que has probado.
OLLAMA_IMAGE_TAG=latest
# Local por defecto. Cambia a 0.0.0.0 cuando lo expongas intencionalmente.
OLLAMA_BIND_IP=127.0.0.1
# Ajustes de mantenimiento en vivo (keep-alive) vs huella de memoria.
OLLAMA_KEEP_ALIVE=5m
# Controles de concurrencia.
OLLAMA_NUM_PARALLEL=1
OLLAMA_MAX_LOADED_MODELS=1
# Déjalo vacío a menos que sirvas a clientes de navegador que golpean Ollama directamente.
OLLAMA_ORIGINS=
Un matiz pequeño pero importante: Ollama en sí tiene un enlace 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 accesible a través de los puertos publicados.
Si quieres una verificación de sanidad rápida sin involucrar SDKs de cliente, la API de Ollama incluye un punto de conexión “listar modelos locales” en GET /api/tags.
Almacenamiento persistente de modelos y la manera 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 predeterminado es $HOME/.ollama/models, y establecer OLLAMA_MODELS sobrescribe eso.
Dentro de la imagen de Docker oficial, $HOME se mapea naturalmente al diseño /root utilizado por el montaje de volumen documentado (/root/.ollama), que es exactamente por qué 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 con nombre es lo más simple y portátil. También es fácil huérfanarlo accidentalmente, por lo que vale la pena nombrarlo intencionalmente (por ejemplo ollama) y mantenerlo estable a través de refactorizaciones de Compose.
Un montaje de enlace (bind mount) a un disco dedicado es mejor cuando los tamaños de los modelos comienzan a dominar tu sistema de archivos raíz. En ese caso, o montas todo /root/.ollama en ese disco, o montas un directorio personalizado y apuntas OLLAMA_MODELS hacia él.
Si estás reorganizando activamente el almacenamiento, es aquí donde un plan de “mover modelos” explícito ayuda. Consulta: move-ollama-models .
Soporte de GPU NVIDIA con Compose y el Kit de Herramientas de Contenedor NVIDIA
Ollama puede usar GPUs NVIDIA en Docker, pero la imagen no puede hacer magia para que una GPU aparezca. El host necesita controladores NVIDIA funcionales y el Kit de Herramientas de Contenedor NVIDIA, y Docker debe estar configurado para usarlo. La documentación de Docker de Ollama menciona explícitamente instalar nvidia-container-toolkit, configurar el tiempo de ejecución mediante nvidia-ctk runtime configure --runtime=docker y reiniciar Docker.
En el lado de Compose, la manera limpia y moderna son las reservas de dispositivos. Docker documenta el acceso a la GPU en Compose usando deploy.resources.reservations.devices, con capabilities: [gpu], driver: nvidia y count (incluyendo all) o device_ids.
Agrega esto al servicio ollama cuando estés en un host 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 documenta Docker (son mutuamente excluyentes).
A veces verás ejemplos antiguos de Compose que usan runtime: nvidia. Eso puede fallar en configuraciones más nuevas con errores como “nombre de tiempo de ejecución desconocido o inválido: nvidia”, lo cual es una fuerte indicación de que debes pasar al patrón de reserva de dispositivos soportado y asegurarte de que el kit de herramientas esté configurado en el host.
Un detalle útil oculto a plena vista: la imagen oficial ollama/ollama establece NVIDIA_VISIBLE_DEVICES=all y NVIDIA_DRIVER_CAPABILITIES=compute,utility. Estos son controles estándar reconocidos por el tiempo de ejecución del contenedor NVIDIA, y ya están presentes a menos que los sobrescribas.
Para confirmar si realmente estás obteniendo inferencia por GPU (no solo un contenedor que inicia), Ollama recomienda usar ollama ps y verificar la columna “Procesador”, que muestra si el modelo está en memoria de GPU.
Verificación de realidad de la 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 (passthrough).
Elecciones de red: host vs bridge, puertos y CORS
La red es donde surgen la mayoría de los errores de “funciona pero mi aplicación no puede conectarse”.
Redes puente con puertos publicados
La red predeterminada de Compose es una red de puente (bridge). En esta configuración, publicar 11434:11434 hace que Ollama sea accesible 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
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 espacio de nombres de una red de puente, y es más probable que tengas conflictos de puertos.
Exponer Ollama intencionalmente
Ollama en una instalación normal se enlaza a 127.0.0.1 por defecto, y la manera documentada de cambiar la dirección de enlace es OLLAMA_HOST.
En Docker, tienes dos capas:
Dirección de enlace de Ollama, controlada por OLLAMA_HOST (la imagen del contenedor tiene por defecto el enlace en todas las interfaces dentro del contenedor).
Alcance desde fuera del contenedor, controlado por ports de Compose y el firewall del host.
Un patrón que me gusta es “enlazar localmente por defecto” mediante 127.0.0.1:11434:11434, y luego cambiar a 0.0.0.0:11434:11434 solo cuando tengo una razón para exponerlo.
Clientes de navegador y OLLAMA_ORIGINS
Si una interfaz o extensión basada en navegador llama a Ollama directamente, estás en territorio 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 retroceso que encajan en un servidor de un solo nodo
Ollama evoluciona rápidamente. Tu archivo Compose puede hacer que eso sea un proceso tranquilo en lugar de una sorpresa en la madrugada.
Actualizar subiendo una etiqueta, no esperando que “latest” se comporte bien
La estrategia de actualización más práctica es fijar la imagen a una etiqueta conocida y buena en .env, y subirla intencionalmente. La imagen se publica como ollama/ollama en Docker Hub.
Como los datos del modelo y el estado del servidor se almacenan en un directorio montado (/root/.ollama en la documentación oficial), reemplazar el contenedor no implica volver a descargar los modelos.
El retroceso es simplemente cambiar la etiqueta de vuelta
El retroceso es el mismo mecanismo en reversa: establece la etiqueta anterior, recrea el contenedor y mantén el mismo volumen. Aquí es donde fijar la etiqueta se paga solo.
La migración de datos trata principalmente sobre rutas de almacenamiento
La mayoría de las “migraciones” en una configuración de un solo nodo no tratan sobre esquemas de bases de datos. Tratan sobre la disposición del disco. Si cambias el directorio de modelos (vía OLLAMA_MODELS) o mueves el volumen montado a un nuevo disco, estás haciendo una migración de datos, llámalo como quieras.
Si quieres una guía práctica para reorganizar el directorio de modelos en máquinas reales, consulta: move-ollama-models .
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 lanzamiento. Eso hace que “actualizar el servidor, mantener los clientes funcionando” sea una expectativa predeterminada razonable para un punto de conexión de servicio de un solo nodo.
Fallos comunes: permisos de GPU, incompatibilidad de controladores y OOM
Esta sección está deliberadamente orientada a síntomas. El objetivo no es “cada posible error de Docker”, solo los fallos que aparecen específicamente en configuraciones de Ollama + GPU + almacenamiento persistente.
GPU visible en el host, pero faltando en el contenedor
Si el host tiene un controlador NVIDIA funcional pero el contenedor no ve una GPU, las causas comunes son:
El Kit de Herramientas de Contenedor NVIDIA no está instalado o el tiempo de ejecución de Docker no está configurado mediante nvidia-ctk. La documentación de Docker de Ollama lo menciona directamente.
Compose no está reservando un dispositivo GPU. La manera soportada es deploy.resources.reservations.devices con la capacidad gpu como documenta Docker.
Se está usando una configuración heredada runtime: nvidia en un daemon que no la reconoce, produciendo “nombre de tiempo de ejecución desconocido o inválido: nvidia”.
Para validación, ollama ps te da una verificación pragmática: muestra si un modelo está cargado en memoria de GPU.
Permiso denegado en dispositivos GPU
El sabor de “permiso denegado” de los fallos de GPU típicamente apunta a restricciones del entorno en lugar de Ollama en sí. Los ejemplos incluyen ejecutar Docker sin root, políticas de seguridad, o nodos de dispositivos que no se exponen como se esperaba. La documentación de soporte de GPU de Docker Compose es explícita de que el host debe tener dispositivos GPU y que el daemon de Docker debe configurarse en consecuencia.
Cuando dudes, reduce las variables: confirma la configuración del kit de herramientas (host), luego confirma la reserva de GPU (Compose), luego confirma el uso de GPU (ollama ps).
Controlador incorrecto, expectativa incorrecta
Ollama en Docker depende de la pila 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 que en realidad son “la pila de CUDA no es usable”. La documentación oficial coloca la configuración del kit de herramientas de contenedores y del daemon de Docker como prerrequisitos para el uso de GPU NVIDIA.
Sin memoria: VRAM o RAM desaparece rápido
OOM es el modo de fallo más predecible para la inferencia local, y usualmente es autoinfligido por la configuración.
Ollama soporta procesamiento concurrente a través de múltiples modelos cargados y manejo de solicitudes paralelo, pero está limitado por la memoria disponible (RAM del sistema en inferencia por CPU, VRAM en 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 valen la pena tratar como “configuraciones de servidor” de primera clase:
OLLAMA_NUM_PARALLEL aumenta el procesamiento de solicitudes paralelo 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 predeterminado es 5 minutos). Mantener los modelos cargados reduce la latencia de inicio 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 el paralelismo y los valores predeterminados de contexto antes de cambiar cualquier otra cosa.
Limita cuántos modelos se permiten permanecer cargados concurrentemente.
Considera características 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 sea la memoria, no la potencia bruta de cálculo.
Cuando no es Ollama: elegir Docker Model Runner en su lugar
A veces el “fallo” es realmente una incompatibilidad de herramientas. Si tu organización ya estandariza en 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 vida.
Docker posiciona DMR como una manera de gestionar, ejecutar y servir modelos directamente vía Docker, obteniendo de Docker Hub u otros registros OCI, y sirviendo APIs compatibles con OpenAI y Ollama.
También soporta múltiples motores de inferencia (incluyendo llama.cpp, y vLLM en Linux con GPUs NVIDIA), lo cual puede importar si te importa sobre las características de rendimiento, no solo “ejecutar un modelo localmente”.
Si quieres una referencia práctica de comandos y un ángulo de comparación más profundo, consulta: Hoja de trucos de Docker Model Runner: Comandos y Ejemplos.