Ollama in Docker Compose con GPU e archiviazione persistente dei modelli
Server Ollama con approccio compose-first, GPU e persistenza.
Ollama funziona egregiamente su hardware nudo (bare metal). Diventa ancora più interessante quando lo si tratta come un servizio: un endpoint stabile, versioni bloccate, archiviazione persistente e una GPU che è disponibile o non lo è.
Questo post si concentra su un unico obiettivo: un “server” Ollama locale o a singolo nodo riproducibile utilizzando Docker Compose, con accelerazione GPU e archiviazione persistente dei modelli.

Salta intenzionalmente le nozioni di base generiche di Docker e Compose. Quando hai bisogno di un elenco compatto dei comandi che utilizzi più spesso (immagini, container, volumi, docker compose), il Docker Cheatsheet è un buon compagno.
Quando desideri HTTPS davanti a Ollama, un proxying corretto per lo streaming e WebSocket, e controlli perimetrali (autenticazione, timeout, limiti di velocità), consulta Ollama dietro un reverse proxy con Caddy o Nginx per lo streaming HTTPS.
Per capire come Ollama si integra con vLLM, Docker Model Runner, LocalAI e i compromessi dell’hosting cloud, vedi Hosting LLM nel 2026: Locale, Self-Hosted & Cloud Infrastructure Compared.
Quando Compose vince rispetto a un’installazione su bare metal
Un’installazione nativa è priva di attriti per uno sviluppatore su una singola macchina. Nel momento in cui ti trovi con una delle seguenti situazioni, Compose inizia a vincere in termini di ergonomia:
Una configurazione per team trae beneficio perché la definizione del servizio è un file che puoi revisionare, versionare e condividere. Un server a singolo nodo trae beneficio perché gli aggiornamenti si trasformano in un cambio del tag dell’immagine e un riavvio, mentre la tua archiviazione dei modelli rimane ferma (purché sia su un volume). Ollama tende anche a convivere con sidecar: un’interfaccia web, un reverse proxy, un gateway di autenticazione, un DB vettoriale o un runtime per agenti. Compose è ottimo per “un comando per avviare l’intera stack”, senza trasformare il tuo host in un fiocco di neve unico.
Questo approccio si allinea bene con il modo in cui il container ufficiale di Ollama è progettato: l’immagine esegue ollama serve di default, espone la porta 11434 ed è destinata a mantenere lo stato in una directory montabile.
Uno scheletro Compose effettivamente utile per Ollama
Inizia con due decisioni:
Primo, come bloccherai le versioni. L’immagine Docker Hub è ollama/ollama, quindi puoi bloccare un tag specifico in .env invece di affidarti a latest.
Secondo, dove risiederanno i dati del modello. La documentazione ufficiale monta un volume su /root/.ollama in modo che i modelli non vengano ridownloaded ogni volta che il container viene sostituito.
Ecco un file Compose che incorpora queste decisioni e mantiene le “manopole” vicine al servizio:
services:
ollama:
image: ollama/ollama:${OLLAMA_IMAGE_TAG:-latest}
container_name: ollama
restart: unless-stopped
# Tienilo locale di default, esponilo successivamente se necessario.
ports:
- "${OLLAMA_BIND_IP:-127.0.0.1}:11434:11434"
# Modelli persistenti e stato del server.
volumes:
- ollama:/root/.ollama
environment:
# L'immagine ufficiale ha già di default 0.0.0.0:11434 all'interno del container,
# ma mantenerlo esplicito aiuta quando si sovrascrivono le impostazioni in seguito.
- OLLAMA_HOST=0.0.0.0:11434
# Regolazioni del servizio.
- OLLAMA_KEEP_ALIVE=${OLLAMA_KEEP_ALIVE:-5m}
- OLLAMA_NUM_PARALLEL=${OLLAMA_NUM_PARALLEL:-1}
- OLLAMA_MAX_LOADED_MODELS=${OLLAMA_MAX_LOADED_MODELS:-1}
# Opzionale, ma rilevante quando un'interfaccia web parla direttamente con Ollama.
# Vedi la sezione Networking per il motivo per cui esiste.
- OLLAMA_ORIGINS=${OLLAMA_ORIGINS:-}
# La riserva GPU è una sezione separata qui sotto.
# Aggiungila solo sugli host che hanno effettivamente GPU NVIDIA.
volumes:
ollama: {}
Un file .env corrispondente rende gli aggiornamenti noiosi:
# Blocca la versione dell'immagine che hai testato.
OLLAMA_IMAGE_TAG=latest
# Locale di default. Cambia a 0.0.0.0 quando vuoi espormi intenzionalmente.
OLLAMA_BIND_IP=127.0.0.1
# Regolazioni keep-alive per latenza al primo avvio vs impronta di memoria.
OLLAMA_KEEP_ALIVE=5m
# Manopole di concorrenza.
OLLAMA_NUM_PARALLEL=1
OLLAMA_MAX_LOADED_MODELS=1
# Lascia vuoto a meno che non stia servendo client browser che colpiscono Ollama direttamente.
OLLAMA_ORIGINS=
Una piccola ma importante sfumatura: Ollama stesso ha un bind host di default su 127.0.0.1:11434 nella configurazione generale, ma l’immagine container ufficiale imposta OLLAMA_HOST=0.0.0.0:11434 in modo che il servizio sia raggiungibile attraverso le porte pubblicate.
Se desideri un rapido controllo di sanità senza coinvolgere SDK client, l’API Ollama include un endpoint “lista modelli locali” su GET /api/tags.
Archiviazione persistente dei modelli e il modo meno doloroso per spostarla
Se ricordi una sola cosa, che sia questa: il container deve avere un archiviazione persistente, altrimenti ogni rebuild è un ridownload.
Ollama ti permette di scegliere la directory dei modelli usando OLLAMA_MODELS. Nell’implementazione di riferimento, il default è $HOME/.ollama/models, e impostare OLLAMA_MODELS lo sovrascrive.
All’interno dell’immagine Docker ufficiale, $HOME mappa naturalmente sul layout /root usato dal mount del volume documentato (/root/.ollama), il che è esattamente il motivo per cui gli esempi ufficiali docker run montano quella directory.
Ci sono due pattern di archiviazione che tendono a funzionare bene nella pratica:
Un volume Docker nominato è il più semplice e portabile. È anche facile orfanarlo per sbaglio, quindi vale la pena nominarlo intenzionalmente (ad esempio ollama) e mantenerlo stabile attraverso i refactoring di Compose.
Un bind mount su un disco dedicato è migliore quando le dimensioni dei modelli iniziano a dominare il filesystem root. In tal caso, monti l’intera /root/.ollama su quel disco, o monti una directory personalizzata e punti OLLAMA_MODELS su di essa.
Se stai riorganizzando attivamente l’archiviazione, è qui che uno “script per spostare i modelli” esplicito aiuta. Vedi: move-ollama-models .
Supporto GPU NVIDIA con Compose e NVIDIA Container Toolkit
Ollama può usare GPU NVIDIA in Docker, ma l’immagine non può far apparire magicamente una GPU. L’host deve avere driver NVIDIA funzionanti e il NVIDIA Container Toolkit, e Docker deve essere configurato per usarlo. La documentazione Docker di Ollama cita esplicitamente l’installazione di nvidia-container-toolkit, la configurazione del runtime tramite nvidia-ctk runtime configure --runtime=docker e il riavvio di Docker.
Dal lato Compose, il modo pulito e moderno è la riserva dei device. Docker documenta l’accesso GPU in Compose usando deploy.resources.reservations.devices, con capabilities: [gpu], driver: nvidia e sia count (incluso all) che device_ids.
Aggiungi questo al servizio ollama quando sei su un host NVIDIA:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
Se hai più GPU e vuoi mantenere Ollama su device specifici, passa da count a device_ids come documentato da Docker (sono mutualmente esclusivi).
A volte vedrai esempi di Compose legacy che usano runtime: nvidia. Questo può fallire su configurazioni più recenti con errori come “unknown or invalid runtime name: nvidia”, che è un forte indizio che dovresti passare al pattern di riserva device supportato e assicurarti che il toolkit sia configurato sull’host.
Un dettaglio utile nascosto in bella vista: l’immagine ufficiale ollama/ollama imposta NVIDIA_VISIBLE_DEVICES=all e NVIDIA_DRIVER_CAPABILITIES=compute,utility. Queste sono manopole standard riconosciute dal runtime container NVIDIA e sono già presenti a meno che non le sovrascriva.
Per confermare se stai ottenendo effettivamente l’inferenza GPU (non solo un container che parte), Ollama consiglia di usare ollama ps e controllare la colonna “Processor”, che mostra se il modello è nella memoria GPU.
Controllo di realtà della piattaforma: Ollama nota che l’accelerazione GPU in Docker è disponibile su Linux (e Windows con WSL2), e non è disponibile su Docker Desktop per macOS a causa della mancanza di GPU passthrough.
Scelte di rete: host vs bridge, porte e CORS
La rete è dove nascono la maggior parte dei bug del tipo “funziona ma la mia app non può connettersi”.
Networking bridge con porte pubblicate
La rete Compose di default è una rete bridge. In questa configurazione, pubblicare 11434:11434 rende Ollama raggiungibile dall’host sulla porta 11434, mentre altri container dovrebbero parlarci usando il nome del servizio ollama (non localhost). Molti inciampano su questo perché localhost all’interno di un container significa “questo container”, non “il container Ollama”.
Ollama stesso esegue un server HTTP sulla porta 11434 (l’immagine lo espone), e la convenzione comune è che i client usino http://localhost:11434 sull’host quando le porte sono pubblicate.
Networking host
network_mode: host può essere allettante su un server a singolo nodo perché rimuove la pubblicazione delle porte e semplifica la semantica di localhost. Il compromesso è che perdi i benefici di isolamento e namespacing di una rete bridge, e sei più soggetto a conflitti di porte.
Esporre Ollama intenzionalmente
Ollama in un’installazione normale si lega a 127.0.0.1 di default, e il modo documentato per cambiare l’indirizzo di bind è OLLAMA_HOST.
In Docker, hai due livelli:
Indirizzo di bind di Ollama, controllato da OLLAMA_HOST (l’immagine container ha di default il bind su tutte le interfacce all’interno del container).
Raggiungibilità dall’esterno del container, controllata da ports di Compose e dal firewall dell’host.
Un pattern che mi piace è “bind localmente di default” tramite 127.0.0.1:11434:11434, poi passare a 0.0.0.0:11434:11434 solo quando ho una ragione per esporlo.
Client browser e OLLAMA_ORIGINS
Se un’interfaccia web o un’estensione chiama Ollama direttamente, sei nel territorio CORS. Ollama permette richieste cross-origin da 127.0.0.1 e 0.0.0.0 di default, e puoi configurare origini aggiuntive usando OLLAMA_ORIGINS.
Questo importa anche su un singolo nodo, perché “funziona con curl” non significa “funziona da un’app browser”.
Pattern di aggiornamento e rollback adatti a un server a singolo nodo
Ollama evolve rapidamente. Il tuo file Compose può rendere questo un processo calmo invece di una sorpresa notturna.
Aggiornamento alzando un tag, non sperando che “latest” si comporti bene
La strategia di aggiornamento più pratica è bloccare l’immagine su un tag noto-buono in .env e alzarlo intenzionalmente. L’immagine è pubblicata come ollama/ollama su Docker Hub.
Poiché i dati del modello e lo stato del server sono archiviati sotto una directory montata (/root/.ollama nella documentazione ufficiale), sostituire il container non implica il ridownload dei modelli.
Il rollback è solo cambiare il tag indietro
Il rollback è lo stesso meccanismo al contrario: imposta il tag precedente, ricrea il container, mantieni lo stesso volume. È qui che il blocco del tag ripaga il suo prezzo.
La migrazione dei dati riguarda principalmente i percorsi di archiviazione
La maggior parte delle “migrazioni” in una configurazione a singolo nodo non riguarda gli schemi del database. Riguardano il layout del disco. Se cambi la directory dei modelli (tramite OLLAMA_MODELS) o sposti il volume montato su un nuovo disco, stai facendo una migrazione dei dati anche se non la chiami così.
Se desideri una guida pratica per riorganizzare la directory dei modelli su macchine reali, vedi: move-ollama-models .
Una nota finale facile da perdere: la documentazione API di Ollama dice esplicitamente che l’API è attesa essere stabile e retrocompatibile, con deprecazioni rare annunciate nelle note di rilascio. Questo rende “aggiornare il server, mantenere i client funzionanti” un’aspettativa ragionevole per un endpoint di servizio a singolo nodo.
Fallimenti comuni: permessi GPU, incompatibilità driver e OOM
Questa sezione è deliberatamente guidata dai sintomi. L’obiettivo non è “ogni possibile errore Docker”, ma solo i fallimenti che appaiono specificamente nelle configurazioni Ollama + GPU + archiviazione persistente.
GPU visibile sull’host, mancante nel container
Se l’host ha un driver NVIDIA funzionante ma il container non vede una GPU, le cause comuni sono:
Il NVIDIA Container Toolkit non è installato o il runtime Docker non è configurato tramite nvidia-ctk. La documentazione Docker di Ollama lo cita direttamente.
Compose non sta riservando un device GPU. Il modo supportato è deploy.resources.reservations.devices con la capability gpu come documentato da Docker.
Una configurazione legacy runtime: nvidia sta venendo usata su un daemon che non la riconosce, producendo “unknown or invalid runtime name: nvidia”.
Per la validazione, ollama ps ti dà un controllo pragmatico: mostra se un modello è caricato nella memoria GPU.
Permesso negato sui device GPU
Il sapore “permesso negato” dei fallimenti GPU punta tipicamente a vincoli ambientali piuttosto che a Ollama stesso. Gli esempi includono l’esecuzione di Docker senza root, politiche di sicurezza, o device node non esposti come previsto. La documentazione Docker Compose per il supporto GPU è esplicita che l’host deve avere device GPU e che il daemon Docker deve essere impostato di conseguenza.
In caso di dubbio, riduci le variabili: conferma la configurazione del toolkit (host), poi conferma la riserva GPU (Compose), poi conferma l’uso della GPU (ollama ps).
Driver sbagliato, aspettativa sbagliata
Ollama in Docker si affida allo stack del driver dell’host. Se il driver dell’host manca, è troppo vecchio o è configurato male, vedrai fallimenti che sembrano “Ollama è rotto” ma sono in realtà “lo stack CUDA non è utilizzabile”. La documentazione ufficiale pone il toolkit container e la configurazione del daemon Docker come prerequisiti per l’uso della GPU NVIDIA.
Memoria insufficiente: VRAM o RAM sparisce velocemente
OOM è il modo di fallimento più prevedibile per l’inferenza locale, ed è solitamente auto-inflitto dalla configurazione.
Ollama supporta l’elaborazione concorrente attraverso modelli multipli caricati e gestione parallela delle richieste, ma è vincolato dalla memoria disponibile (RAM di sistema per l’inferenza CPU, VRAM per l’inferenza GPU). Quando l’inferenza GPU è usata, i nuovi modelli devono stare nella VRAM per permettere carichi di modelli concorrenti.
Due dettagli di configurazione meritano di essere trattati come “impostazioni di server” di prima classe:
OLLAMA_NUM_PARALLEL aumenta l’elaborazione parallela delle richieste per modello, ma la memoria richiesta scala con OLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH.
OLLAMA_KEEP_ALIVE controlla quanto tempo i modelli rimangono caricati (il default è 5 minuti). Mantenere i modelli caricati riduce la latenza al primo avvio, ma blocca anche la memoria.
Se stai stabilizzando un servizio a singolo nodo sotto carico, le correzioni non drammatiche di solito sembrano:
Abbassa i default di parallelismo e contesto prima di cambiare altro.
Limita quanti modelli sono permessi di rimanere caricati in modo concorrente.
Considera funzionalità di riduzione della memoria come Flash Attention (OLLAMA_FLASH_ATTENTION=1) e tipi di cache K/V a precisione inferiore (OLLAMA_KV_CACHE_TYPE) quando il collo di bottiglia è la memoria, non la potenza di calcolo grezza.
Quando non è Ollama: scegliere Docker Model Runner invece
A volte il “fallimento” è davvero un disallineamento degli strumenti. Se la tua organizzazione ha già standardizzato su artefatti e workflow nativi Docker, Docker Model Runner (DMR) può essere una scelta migliore rispetto all’esecuzione di Ollama come container servizio a lunga vita.
Docker posiziona DMR come un modo per gestire, eseguire e servire modelli direttamente tramite Docker, estraendo da Docker Hub o altri registri OCI, e servendo API compatibili con OpenAI e Ollama.
Supporta anche motori di inferenza multipli (inclusi llama.cpp, e vLLM su Linux con GPU NVIDIA), il che può importare se ti interessa le caratteristiche di throughput, non solo “eseguire un modello localmente”.
Se desideri un riferimento pratico ai comandi e un angolo di confronto più approfondito, vedi: Docker Model Runner Cheatsheet: Commands & Examples.