Ollama in Podman rootless su un VPS
Configura Ollama in Podman rootless su VPS con utente dedicato, lingering, Quadlet, riavvio dopo reboot, etichette SELinux e porta 11434 chiusa.
Eseguire Ollama in Podman rootless su un VPS
Per eseguire Ollama in Podman rootless su un server, devono essere vere cinque condizioni che una guida per desktop può tralasciare. Un utente non privilegiato dedicato deve essere proprietario del container. Per questo utente deve essere abilitato il lingering, in modo che il container continui a essere eseguito dopo la disconnessione. Un file Quadlet deve affidare il container a systemd, così il container viene riavviato dopo un reboot. Nelle distribuzioni che lo applicano, la directory dei modelli deve avere un'etichetta SELinux. L'API deve essere in ascolto solo su loopback e deve essere raggiunta tramite un tunnel SSH (secure shell).
Ollama è un server per modelli linguistici di grandi dimensioni (LLM). Memorizza i pesi dei modelli su disco, li carica in memoria e risponde alle richieste HTTP sulla porta 11434. Non dispone di login, API key o account utente, quindi l'unico controllo degli accessi disponibile è quello di rete. Podman esegue i container senza daemon e senza root, quindi qualsiasi processo che esca dal container viene inizialmente eseguito come utente non privilegiato ordinario. Se vuoi prima un confronto tra i runtime, leggi come differiscono Podman e Docker su un VPS. Se preferisci evitare del tutto i container, installare Ollama direttamente su un VPS è un percorso più breve.
SSD Nodes fornisce Fedora tra le proprie immagini e Fedora include Podman e SELinux (security-enhanced Linux) per impostazione predefinita. Tutti i comandi riportati di seguito funzionano su qualsiasi distribuzione che includa Podman 5 o versione successiva.
Perché la versione per laptop richiede modifiche su un server
Fedora Magazine ha pubblicato il 5 agosto 2026 una guida chiara a questo stack: Eseguire Ollama localmente con Podman su Fedora Linux, di Yazan Monshed. È un buon punto di partenza per conoscere gli strumenti nella prima ora. Tuttavia, la guida è pensata per un laptop e quattro delle sue scelte si comportano in modo diverso su una macchina con un indirizzo IP pubblico.
- Avvia il container con un semplice
podman run -d. Un container avviato manualmente non viene riavviato dopo un reboot, perché non è mai stato configurato per partire automaticamente. - Usa il tag mobile
ollama/ollama. Su un laptop ci si accorge del cambiamento nel giorno in cui il comportamento cambia. Su un server, il primo segnale è spesso uno script che ha smesso di funzionare durante la notte. - Pubblica il servizio con
-p 11434:11434, che esegue il binding su tutte le interfacce. Dietro un router domestico, il servizio non è raggiungibile da Internet. Su un VPS, invece, diventa un'API di inferenza pubblica senza password. - Esegue il servizio con il proprio utente di accesso. Su un server, l'account proprietario del container non dovrebbe possedere nient'altro, in modo che un'eventuale evasione dal container conduca a una directory home vuota.
Nessuna di queste scelte è errata per la macchina a cui era destinata la guida. Sono semplicemente decisioni da rivalutare quando il server è raggiungibile da Internet e nessuno si trova davanti alla macchina.
Crea l’utente non privilegiato e controlla subuid
Rootless Podman associa gli ID utente (UID) interni al container a un intervallo di ID non utilizzati sull’host. Questo intervallo è dichiarato in /etc/subuid e /etc/subgid. Senza questa configurazione, i container rootless non possono avviarsi.
sudo dnf install -y podman # or: sudo apt install -y podman
sudo useradd --create-home --shell /bin/bash --comment "Ollama container owner" ollama
sudo passwd --lock ollama
grep ollama /etc/subuid /etc/subgidgrep dovrebbe stampare due righe, una per ciascun file. Ogni riga deve indicare un intervallo di 65536 ID:
/etc/subuid:ollama:100000:65536
/etc/subgid:ollama:100000:65536Il numero iniziale sarà diverso, ed è normale. Se grep non stampa nulla, useradd non ha assegnato alcun intervallo e il primo comando podman eseguito con quell’utente non riesce con questo errore:
Error: cannot find UID/GID for user ollama: no subuid ranges found for user "ollama" in /etc/subuidAssegna un intervallo non utilizzato da altri utenti, quindi comunica a Podman che la precedente mappatura non è più valida:
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 ollama
sudo -iu ollama podman system migrateBloccare la password impedisce a chiunque di accedere direttamente come ollama. Per raggiungere l’account, usa sudo -iu ollama dall’utente amministratore.
Abilitare il lingering per mantenere il servizio attivo dopo il logout
L'istanza systemd di un utente normalmente viene avviata al login e arrestata al logout; insieme a essa viene rimosso anche /run/user/<uid>. Ogni container rootless appartenente a quell'utente si arresta nello stesso momento. Il lingering mantiene in esecuzione l'istanza dell'utente senza una sessione associata.
sudo loginctl enable-linger ollama
loginctl show-user ollama --property=LingerIl comando dovrebbe stampare Linger=yes. Abilitatelo prima di creare l'unità, perché la directory necessaria all'unità, /run/user/<uid>, esiste solo quando il lingering è attivo.
È necessario eseguire un ulteriore passaggio. sudo -iu ollama fornisce una shell, ma non un session bus; di conseguenza systemctl --user fallisce immediatamente:
Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not definedsystemd cerca il bus dell'utente in $XDG_RUNTIME_DIR/bus, ma sudo -i non imposta questa variabile. Impostatela manualmente in ogni shell amministrativa da cui gestite questo servizio:
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user statusDove vengono salvati i blob dei modelli e quanto spazio su disco pianificare
Ollama scrive i pesi in /root/.ollama/models all'interno del container. Collega una directory della home dell'utente a quel percorso: i file verranno salvati in una posizione che puoi misurare, /home/ollama/ollama-data/models. I blob vengono salvati in models/blobs come file indirizzati tramite contenuto, mentre models/manifests contiene il piccolo indice che li identifica. Se usi invece un volume denominato, come nell'articolo di Fedora Magazine, lo stesso albero si trova in /home/ollama/.local/share/containers/storage/volumes/<volume>/_data. In entrambi i casi, ollama pull e ollama run scrivono i pesi nello stesso albero; ciò che distingue i due comandi è soltanto l'apertura di una sessione di chat al termine del download.
Verifica lo spazio su disco prima di scaricare qualsiasi file. Le dimensioni di download pubblicate indicano il requisito minimo.
The data behind this chart
[
{
"label": "gemma3:4b",
"download_gb": 3.3
},
{
"label": "mistral:7b",
"download_gb": 4.4
},
{
"label": "qwen3:8b",
"download_gb": 5.2
},
{
"label": "gemma3:12b",
"download_gb": 8.1
},
{
"label": "qwen3:14b",
"download_gb": 9.3
},
{
"label": "gemma3:27b",
"download_gb": 17
},
{
"label": "qwen3:30b",
"download_gb": 19
}
]Tutte le 7 righe riportano valori pubblicati su ollama.com/library, non dimensioni misurate su un disco. Il tag più piccolo in questa tabella, gemma3:4b, richiede il download di 3.3 GB. Il più grande, qwen3:30b, richiede il download di 19 GB. L'immagine del container occupa inoltre spazio nello storage gestito da Podman, quindi verifica entrambi i valori con podman system df e df -h /home. Durante il caricamento, un modello richiede inoltre una quantità di RAM all'incirca pari alla dimensione del file, oltre allo spazio per la finestra di contesto; per questo un modello da 19 GB non può essere eseguito su una VPS con 16 GB.
Blocca il tag dell'immagine e usa il nome completo del registry
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
mkdir -p ~/ollama-data ~/.config/containers/systemd
podman pull docker.io/ollama/ollama:0.32.9Usa un tag di versione rilasciata, 0.32.9 ad agosto 2026, non latest. Un tag bloccato garantisce che un riavvio alle 04:00 utilizzi lo stesso binario testato. Ogni variazione del comportamento dipende quindi da una modifica effettuata da te. Docker Hub pubblica anche i tag -rc e -rocm per le stesse versioni. Usa il tag semplice, a meno che tu non disponga di una GPU AMD.
Specifica anche l'host del registry. Su Fedora, un nome breve in un'unità systemd non dispone di un terminale in cui visualizzare la richiesta di conferma. L'unità termina quindi con:
Error: short-name "ollama/ollama" did not resolve to an alias and no unqualified-search registries are definedEseguire prima il pull manualmente è facoltativo, ma utile. In questo modo il download di diversi gigabyte non rientra nel timeout di avvio dell'unità.
L'unit Quadlet che sopravvive a un riavvio
Quadlet è il generatore systemd di Podman. Si scrive un file .container, systemd lo trasforma in un servizio all'avvio e podman generate systemd non è più necessario. Salvare il file come /home/ollama/.config/containers/systemd/ollama.container, assegnandone la proprietà all'utente ollama.
[Unit]
Description=Ollama API (rootless)
After=network-online.target
Wants=network-online.target
[Container]
Image=docker.io/ollama/ollama:0.32.9
ContainerName=ollama
PublishPort=127.0.0.1:11434:11434
Volume=/home/ollama/ollama-data:/root/.ollama:Z
Environment=OLLAMA_KEEP_ALIVE=30m
Environment=OLLAMA_MAX_LOADED_MODELS=1
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.targetIl nome del file determina il nome del servizio, quindi ollama.container diventa ollama.service.
systemctl --user daemon-reload
systemctl --user start ollama.service
systemctl --user status ollama.servicestatus dovrebbe mostrare active (running). Non eseguire systemctl --user enable ollama.service. L'unità non esiste come file sul disco, quindi systemd rifiuta il comando:
Failed to enable unit: Unit file /run/user/1001/systemd/generator/ollama.service is transient or generated.La sezione [Install] svolge già questo compito. Quadlet crea autonomamente il collegamento per l'avvio automatico durante daemon-reload, quindi quel comando è obbligatorio. TimeoutStartSec=900 gestisce un primo avvio che deve ancora scaricare l'immagine, perché i 90 secondi predefiniti non sono sufficienti per un download di due gigabyte e systemd interromperebbe l'avvio segnalandolo come non riuscito. OLLAMA_KEEP_ALIVE=30m mantiene un modello in memoria tra una richiesta e l'altra invece di scaricarlo dopo cinque minuti; i compromessi sono descritti in mantenere un modello Ollama caricato in memoria. Se alcuni termini di systemd non sono familiari, come funzionano i servizi e i timer systemd su un VPS descrive le unità in dettaglio.
Perché la directory dei modelli restituisce «permission denied» con SELinux
Su Fedora, RHEL, Rocky e AlmaLinux, SELinux è attivo in modalità enforcing per impostazione predefinita. Un processo del container viene eseguito nel dominio container_t, mentre una directory nella home dell'utente ha l'etichetta user_home_t. La policy non consente l'accesso tra questi due contesti. Di conseguenza, Ollama non può creare la propria struttura di directory per i modelli e il container termina. Su questi sistemi, getenforce visualizza Enforcing e il diniego viene registrato:
sudo ausearch -m avc -ts recentVerrà visualizzata una riga che indica il dominio e l'etichetta della destinazione:
avc: denied { write } for pid=1842 comm="ollama" name="models" dev="vda1" ino=131077 scontext=system_u:system_r:container_t:s0:c214,c827 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=dir permlisted=0Il :Z alla fine della riga Volume= risolve il problema. Rietichetta la directory dell'host con container_file_t e le assegna una categoria MCS (multi-category security) privata, utilizzata solo da questo container. La variante con :z minuscolo applica invece un'etichetta condivisa. È quella da usare quando due container devono leggere la stessa directory.
Prestare attenzione a :Z, perché è un comando distruttivo e non produce output. La rietichettatura viene applicata ricorsivamente. Se si indica /home/ollama, ogni file contenuto nella directory home viene rietichettato. Questo impedisce all'utente di accedere alle proprie chiavi SSH. Specificare sempre a :Z una sottodirectory dedicata, che non contenga altri dati. I volumi nominati non richiedono questo comando, perché Podman applica automaticamente le etichette corrette quando li crea. Per una panoramica più ampia, Nozioni di base su SELinux per un server descrive i contesti e i booleani. Su Ubuntu e Debian viene usato AppArmor. In questi sistemi :Z non esegue alcuna operazione e lasciarlo nell'unità non causa problemi.
Chiudere la porta 11434 e raggiungere l'API tramite SSH
PublishPort=127.0.0.1:11434:11434 associa il lato host all'interfaccia di loopback. Verificalo:
ss -ltnp | grep 11434
curl http://127.0.0.1:11434L'output di ss deve mostrare 127.0.0.1:11434. 0.0.0.0:11434 o *:11434 indica che la porta è aperta a Internet e curl deve rispondere con Ollama is running.
Specifica con precisione quale lato stai associando. L'indirizzo in PublishPort è l'indirizzo dell'host. All'interno del container, Ollama deve continuare ad ascoltare su tutte le interfacce, come previsto dall'immagine. Impostando Environment=OLLAMA_HOST=127.0.0.1, Ollama viene associato alla loopback del container. Podman inoltra invece il traffico pubblicato all'indirizzo di rete del container, quindi ogni richiesta viene rifiutata, anche dall'host.
Una porta 11434 esposta comporta due rischi. Ollama non usa autenticazione, quindi chiunque raggiunga la porta può elencare i modelli tramite /api/tags, eseguire inferenza sulla CPU e sul traffico dati disponibili tramite /api/generate, scaricare nuovi modelli sul disco ed eliminare quelli esistenti. Inoltre, HTTP in chiaro verso una porta remota trasmette prompt e completamenti senza cifratura, quindi ogni macchina lungo il percorso può leggerli. Entrambi i problemi scompaiono se la porta non lascia mai il server.
Dalla workstation, inoltra la porta tramite SSH:
ssh -N -L 11434:127.0.0.1:11434 you@vps.example.comA questo punto http://127.0.0.1:11434 sul laptop è l'istanza Ollama del server, all'interno della cifratura della sessione SSH. Se il laptop esegue già Ollama, l'associazione locale non riesce con bind [127.0.0.1]:11434: Address already in use; usa -L 11435:127.0.0.1:11434 e configura il client per puntare a 11435.
Quando serve un client browser, anteponi invece un reverse proxy con password. Un blocco di sito Caddy richiede quattro righe e caddy hash-password stampa l'hash bcrypt richiesto:
ollama.example.com {
basic_auth {
you $2a$14$replace_with_the_generated_hash
}
reverse_proxy 127.0.0.1:11434
}Caddy ottiene autonomamente un certificato tramite TLS (transport layer security), quindi il traffico è cifrato. Verifica prima il client: molti strumenti che comunicano con Ollama non dispongono di un campo per un header Authorization e falliscono con l'autenticazione di base se ricevono soltanto 401 Unauthorized. Il tunnel SSH non presenta questo problema, motivo per cui è la raccomandazione predefinita in questo caso.
Scaricare un modello e verificare l'intero percorso
podman exec -it ollama ollama pull gemma3:4b
curl -s http://127.0.0.1:11434/api/tags
curl -s http://127.0.0.1:11434/api/generate -d '{"model":"gemma3:4b","prompt":"Reply with the single word: ready","stream":false}'
du -sh ~/ollama-data/models/api/tags restituisce un elenco JSON di gemma3:4b. /api/generate restituisce un oggetto JSON con un campo response, dopo una pausa necessaria per caricare i pesi dal disco. du dovrebbe restituire un numero vicino alla dimensione di download pubblicata. Verificare quindi la parte a cui è dedicata l'intera guida:
sudo reboot
# reconnect, then:
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user is-active ollama.serviceactive indica che il processo precedente è ancora attivo, che la sezione [Install] e daemon-reload hanno funzionato correttamente. inactive indica che manca uno dei tre elementi.
Modalità di errore e stringhe visualizzate
Il container scompare dopo un riavvio. Controllare prima loginctl show-user ollama --property=Linger, perché senza Linger=yes l'istanza systemd dell'utente non viene mai avviata al boot. Se il lingering è attivo, nella [Install] manca la sezione .container oppure il file è stato modificato senza eseguire systemctl --user daemon-reload.
Error: statfs /home/ollama/ollama-data: no such file or directory. La sorgente del bind mount deve esistere prima dell'avvio del container. Podman non crea automaticamente le directory sull'host. Eseguire mkdir -p ~/ollama-data come utente ollama.
L'avvio fallisce dopo 90 secondi. journalctl --user -u ollama.service mostra Start operation timed out. Terminating. perché il download dell'immagine era ancora in corso. Eseguire manualmente il pull oppure mantenere TimeoutStartSec=900.
Il container si avvia e termina. podman logs ollama e sudo ausearch -m avc -ts recent, considerati insieme, indicano se il problema riguarda l'etichetta SELinux. Un AVC che riporta container_t e user_home_t indica che manca :Z.
Le richieste dall'host vengono rifiutate. curl: (7) Failed to connect to 127.0.0.1 port 11434: Connection refused con il servizio active indica in genere che OLLAMA_HOST è stato impostato su un indirizzo di loopback all'interno del container. Rimuovere quella riga.
La generazione è molto lenta oppure il container viene terminato. In assenza di una GPU, l'inferenza viene eseguita sulla CPU e un modello di grandi dimensioni è naturalmente lento. Se un container termina durante una richiesta e nei log compare signal: killed, significa che è intervenuto il kernel out-of-memory killer. Scegliere quindi un tag più piccolo dalla tabella precedente.
Aggiornamento di un'immagine fissata a una versione
Il version pinning fa sì che gli aggiornamenti siano operazioni eseguite dall'amministratore, non eventi automatici. Modifica Image= in ollama.container, quindi ricarica la configurazione e riavvia:
systemctl --user daemon-reload
systemctl --user restart ollama.service
podman exec ollama ollama --versionI modelli si trovano nel bind mount, quindi restano invariati quando cambia l'immagine. AutoUpdate=registry nella sezione [Container] serve a chi utilizza un tag mobile e non è utile insieme a un tag con versione fissa, perché il contenuto di quel tag non cambia mai. Esegui il backup di /home/ollama/ollama-data/models/manifests e del file .container e non includere i blob: sono di grandi dimensioni e ollama pull li scarica nuovamente su un nuovo server.
FAQ
Perché il mio container Podman rootless si arresta quando chiudo la sessione?
L'istanza systemd di un utente e la relativa directory /run/user/<uid> vengono rimosse quando termina l'ultima sessione di quell'utente, e con esse vengono arrestati tutti i container rootless. Esegui sudo loginctl enable-linger ollama e verifica che loginctl show-user ollama --property=Linger restituisca Linger=yes. Abilita il lingering prima di creare l'unità Quadlet, perché la directory runtime necessaria all'unità esiste solo quando il lingering è attivo.
Sono necessarie le etichette SELinux nella directory dei modelli Ollama?
Su Fedora, RHEL, Rocky e AlmaLinux sì, se esegui il bind mount di una directory dell'host. Il container viene eseguito nel dominio container_t e una directory nella home dell'utente ha l'etichetta user_home_t, quindi la scrittura viene negata e Ollama termina. Aggiungi :Z alla riga Volume= e assegnagli una sottodirectory dedicata, perché la rietichettatura è ricorsiva e indicare :Z per un'intera directory home impedisce a quell'utente di accedere alle chiavi SSH. I volumi denominati ricevono automaticamente le etichette corrette da Podman e non richiedono altro.
Quanto spazio su disco richiede un modello Ollama?
Parti dalla dimensione pubblicata del download su ollama.com/library, che va da 3.3 GB per gemma3:4b a 19 GB per qwen3:30b. Aggiungi l'immagine Podman e lascia spazio libero aggiuntivo, perché un secondo modello non sostituisce il primo sul disco. Controlla df -h /home prima del download e du -sh ~/ollama-data/models dopo. Pianifica la RAM nello stesso modo: durante il caricamento, un modello richiede in memoria circa lo spazio occupato dal relativo file, oltre alla finestra di contesto.
È sicuro esporre la porta 11434 su un VPS?
No. Ollama viene distribuito senza alcun tipo di autenticazione, quindi chiunque raggiunga la porta può elencare i modelli, eliminarli, scaricarne di nuovi sul disco ed eseguire inferenze usando la CPU e la quota di traffico disponibile. Inoltre, il semplice HTTP su Internet trasmette ogni prompt e ogni completamento in chiaro. Associa il lato host a 127.0.0.1 con PublishPort=127.0.0.1:11434:11434, verifica con ss -ltnp | grep 11434 e accedi tramite un tunnel SSH oppure un reverse proxy che richieda una password.