Installer Ollama avec Podman rootless sur un VPS
Configurez Ollama dans Podman rootless sur un VPS avec utilisateur dédié, lingering, Quadlet, labels SELinux et API privée sur loopback.
Exécuter Ollama dans Podman rootless sur un VPS
Pour exécuter Ollama dans Podman rootless sur un serveur, cinq conditions doivent être réunies, contrairement à un guide pour poste de travail qui peut en ignorer certaines. Un utilisateur non privilégié dédié possède le conteneur. Le lingering est activé pour cet utilisateur, afin que le conteneur continue de fonctionner après sa déconnexion. Un fichier Quadlet confie le conteneur à systemd, afin qu’il redémarre après un reboot. Le répertoire des modèles porte un label SELinux sur les distributions qui l’imposent. L’API écoute uniquement sur loopback, et vous y accédez au moyen d’un tunnel SSH (secure shell).
Ollama est un serveur pour les grands modèles de langage (LLM). Il stocke les poids des modèles sur le disque, les charge en mémoire et répond aux requêtes HTTP sur le port 11434. Il ne fournit ni connexion, ni clé API, ni comptes utilisateur : le réseau est donc le seul contrôle d’accès disponible. Podman exécute les conteneurs sans daemon et sans root. Ainsi, tout élément qui s’échappe du conteneur commence avec les droits d’un utilisateur non privilégié ordinaire. Si vous voulez d’abord comparer les environnements d’exécution, consultez les différences entre Podman et Docker sur un VPS. Si vous préférez éviter complètement les conteneurs, installer Ollama directement sur un VPS est une solution plus courte.
SSD Nodes propose Fedora parmi ses images, et Fedora fournit par défaut Podman et SELinux (security-enhanced Linux). Toutes les commandes ci-dessous fonctionnent sur toute distribution équipée de Podman 5 ou version ultérieure.
Pourquoi la version pour ordinateur portable doit être adaptée à un serveur
Fedora Magazine a publié un guide clair sur cette pile le 5 août 2026 : Exécuter Ollama localement avec Podman sur Fedora Linux, par Yazan Monshed. C’est une bonne introduction aux outils. Le guide vise toutefois un ordinateur portable, et quatre de ses choix se comportent différemment sur une machine disposant d’une adresse IP publique.
- Le conteneur est démarré avec un simple
podman run -d. Un conteneur démarré manuellement ne redémarre pas après un reboot, car aucune configuration ne demande son démarrage. - Le tag évolutif
ollama/ollamaest utilisé. Sur un ordinateur portable, vous remarquez le jour où le comportement change. Sur un serveur, le premier signe est souvent un script qui cesse de fonctionner pendant la nuit. - La publication utilise
-p 11434:11434, qui se lie à toutes les interfaces. Derrière un routeur domestique, le service est inaccessible depuis Internet. Sur un VPS, cela expose une API d’inférence publique sans mot de passe. - Le service s’exécute avec votre propre utilisateur de connexion. Sur un serveur, le compte propriétaire du conteneur ne doit rien posséder d’autre, afin qu’une sortie du conteneur aboutisse dans un répertoire personnel vide.
Aucun de ces choix n’est incorrect pour la machine visée par le guide. Il s’agit simplement de décisions à réévaluer lorsque le serveur est accessible depuis partout et que personne ne se trouve devant la machine.
Créer l’utilisateur non privilégié et vérifier subuid
Rootless Podman mappe les identifiants utilisateur (UID) internes du conteneur vers un bloc d’identifiants inutilisés sur l’hôte. Ce bloc est déclaré dans /etc/subuid et /etc/subgid. Sans ce bloc, les conteneurs rootless ne peuvent pas démarrer.
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/subgidLa commande grep doit afficher deux lignes, une provenant de chaque fichier, chacune indiquant une plage de 65536 identifiants :
/etc/subuid:ollama:100000:65536
/etc/subgid:ollama:100000:65536Le nombre de départ sera différent dans votre cas, ce qui est normal. Si la commande grep n’affiche rien, useradd n’a pas attribué de plage. La première commande podman exécutée avec cet utilisateur échoue alors comme suit :
Error: cannot find UID/GID for user ollama: no subuid ranges found for user "ollama" in /etc/subuidAttribuez une plage qu’aucun autre utilisateur ne possède, puis indiquez à Podman que son ancienne configuration de mapping est obsolète :
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 ollama
sudo -iu ollama podman system migrateVerrouiller le mot de passe empêche toute connexion directe en tant que ollama. Vous accédez à ce compte depuis votre utilisateur administrateur avec sudo -iu ollama.
Activez le lingering pour que le service survive à la déconnexion
L’instance systemd d’un utilisateur démarre normalement à la connexion et s’arrête à la déconnexion. /run/user/<uid> est alors supprimé avec elle. Tous les conteneurs rootless appartenant à cet utilisateur s’arrêtent au même moment. Le lingering maintient l’instance utilisateur en fonctionnement sans session associée.
sudo loginctl enable-linger ollama
loginctl show-user ollama --property=LingerLa commande doit afficher Linger=yes. Activez le lingering avant de créer l’unité, car le répertoire requis par celle-ci, /run/user/<uid>, n’existe qu’une fois le lingering activé.
Il reste une étape que l’on ne prévoit généralement pas. sudo -iu ollama fournit un shell, mais pas de session bus. systemctl --user échoue donc immédiatement :
Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not definedsystemd recherche le user bus à l’emplacement $XDG_RUNTIME_DIR/bus. sudo -i ne définit pas cette variable. Définissez-la manuellement dans chaque shell d’administration depuis lequel vous gérez ce service :
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user statusEmplacement des blobs des modèles et espace disque à prévoir
Ollama écrit les poids dans /root/.ollama/models à l’intérieur du conteneur. Montez un répertoire du home de l’utilisateur sur ce chemin afin que les fichiers soient stockés à un emplacement que vous pouvez mesurer : /home/ollama/ollama-data/models. Les blobs sont stockés dans models/blobs sous forme de fichiers adressés par leur contenu, tandis que models/manifests contient le petit index qui les référence. Si vous utilisez plutôt un volume nommé, comme dans l’article de Fedora Magazine, la même arborescence se trouve sous /home/ollama/.local/share/containers/storage/volumes/<volume>/_data.
Dimensionnez le disque avant de télécharger quoi que ce soit. Les tailles de téléchargement publiées constituent le minimum à prévoir.
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
}
]Les 7 lignes correspondent à des chiffres publiés sur ollama.com/library, et non à des tailles mesurées sur un disque. Le tag le plus petit, gemma3:4b, télécharge 3.3 Go. Le plus grand, qwen3:30b, télécharge 19 Go. L’image du conteneur s’ajoute à ces données dans le stockage propre à Podman. Vérifiez donc les deux valeurs avec podman system df et df -h /home. Un modèle nécessite également environ l’équivalent de sa taille de fichier en RAM lorsqu’il est chargé, ainsi qu’un espace supplémentaire pour la fenêtre de contexte. Un modèle de 19 Go ne fonctionnera donc pas sur un VPS doté de 16 Go de RAM.
Épinglez le tag de l’image et utilisez le nom complet du registre
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.9Utilisez un tag de version publiée, 0.32.9 en août 2026, et non latest. Avec un tag épinglé, un redémarrage à 04:00 utilise le même binaire que celui que vous avez testé. Tout changement de comportement vient donc d’une modification que vous avez effectuée. Docker Hub publie également les tags -rc et -rocm pour les mêmes versions. Choisissez le tag standard, sauf si vous avez un GPU AMD.
Indiquez également l’hôte du registre. Sous Fedora, un nom court dans une unité systemd ne dispose d’aucun terminal permettant de demander une confirmation. L’unité échoue alors avec :
Error: short-name "ollama/ollama" did not resolve to an alias and no unqualified-search registries are definedEffectuer d’abord le pull manuellement est facultatif, mais utile. Cela évite que le téléchargement de plusieurs gigaoctets ne consomme le délai d’expiration du démarrage de l’unité.
L’unité Quadlet qui survit à un redémarrage
Quadlet est le générateur systemd de Podman. Vous écrivez un fichier .container, systemd le transforme en service au démarrage, et podman generate systemd n’est plus nécessaire. Enregistrez-le sous /home/ollama/.config/containers/systemd/ollama.container, avec comme propriétaire l’utilisateur 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.targetLe nom du fichier définit le nom du service. Ainsi, ollama.container devient ollama.service.
systemctl --user daemon-reload
systemctl --user start ollama.service
systemctl --user status ollama.servicestatus doit afficher active (running). N’exécutez pas systemctl --user enable ollama.service. L’unité n’existe pas sous forme de fichier sur le disque, donc systemd refuse :
Failed to enable unit: Unit file /run/user/1001/systemd/generator/ollama.service is transient or generated.La section [Install] s’en charge déjà. Quadlet crée lui-même le lien de démarrage automatique pendant daemon-reload, ce qui rend cette commande obligatoire. TimeoutStartSec=900 couvre un premier démarrage qui doit encore télécharger l’image, car le délai par défaut de 90 secondes ne suffit pas pour un téléchargement de deux gigaoctets et systemd arrête alors le démarrage en le marquant comme échoué. OLLAMA_KEEP_ALIVE=30m conserve un modèle en mémoire entre les requêtes au lieu de le décharger après cinq minutes ; les compromis sont expliqués dans conserver un modèle Ollama en mémoire. Si certains termes systemd utilisés ici vous sont inconnus, fonctionnement des services et des timers systemd sur un VPS présente les unités elles-mêmes.
Pourquoi le répertoire des modèles renvoie « permission denied » avec SELinux
Sur Fedora, RHEL, Rocky et AlmaLinux, SELinux est appliqué par défaut. Un processus de conteneur s’exécute dans le domaine container_t, tandis qu’un répertoire situé dans le répertoire personnel d’un utilisateur porte l’étiquette user_home_t. La politique ne permet pas à l’un d’accéder à l’autre. Ollama ne peut donc pas créer son arborescence de modèles et le conteneur s’arrête. getenforce affiche Enforcing sur ces systèmes, et le refus est enregistré :
sudo ausearch -m avc -ts recentVous verrez une ligne qui indique le domaine et l’étiquette cible :
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=0Le :Z à la fin de la ligne Volume= corrige le problème. Il réétiquette le répertoire de l’hôte avec container_file_t et lui attribue une catégorie MCS (multi-category security) privée que seul ce conteneur possède. La forme en minuscules :z utilise à la place une étiquette partagée. C’est celle qu’il faut utiliser lorsque deux conteneurs lisent le même répertoire.
Attention à :Z, car cette commande est destructive et silencieuse. Le réétiquetage est récursif. Si vous l’appliquez à /home/ollama, tous les fichiers de ce répertoire personnel sont réétiquetés, ce qui empêche l’utilisateur d’accéder à ses clés SSH. Donnez toujours à :Z un sous-répertoire dédié qui ne contient rien d’autre. Les volumes nommés n’en ont pas besoin, car Podman les étiquette correctement lors de leur création. Pour une vue d’ensemble, les bases de SELinux pour un serveur explique les contextes et les booléens. Ubuntu et Debian utilisent AppArmor à la place. :Z n’y fait rien, et sa présence dans l’unité ne pose aucun problème.
Fermez le port 11434 et accédez à l’API via SSH
PublishPort=127.0.0.1:11434:11434 lie le côté hôte à l’interface loopback. Vérifiez-le :
ss -ltnp | grep 11434
curl http://127.0.0.1:11434La sortie de ss doit afficher 127.0.0.1:11434. 0.0.0.0:11434 ou *:11434 signifie que le port est ouvert sur Internet, et curl doit répondre Ollama is running.
Soyez précis sur le côté auquel vous liez le service. L’adresse indiquée dans PublishPort est l’adresse de l’hôte. Dans le conteneur, Ollama doit continuer à écouter sur toutes les interfaces, ce qui correspond au comportement par défaut de l’image. Définir Environment=OLLAMA_HOST=127.0.0.1 lie Ollama à la loopback du conteneur. Podman redirige alors le trafic publié vers l’adresse réseau du conteneur, et chaque requête est refusée, même depuis l’hôte.
Un port 11434 ouvert vous expose à deux problèmes. Ollama n’a aucune authentification. Toute personne qui atteint le port peut donc lister vos modèles avec /api/tags, exécuter des inférences sur votre CPU et consommer votre quota de bande passante avec /api/generate, télécharger de nouveaux modèles sur votre disque et supprimer ceux que vous possédez. Ensuite, des requêtes HTTP non chiffrées vers un port distant envoient les prompts et les réponses en clair. Chaque machine située sur le trajet peut donc les lire. Ces deux problèmes disparaissent si le port reste limité à la machine.
Depuis votre poste de travail, redirigez le port via SSH :
ssh -N -L 11434:127.0.0.1:11434 you@vps.example.comDésormais, http://127.0.0.1:11434 sur votre ordinateur portable correspond à l’Ollama du serveur, à l’intérieur du chiffrement de la session SSH. Si Ollama est déjà exécuté sur votre ordinateur portable, la liaison locale échoue avec bind [127.0.0.1]:11434: Address already in use. Utilisez -L 11435:127.0.0.1:11434 et configurez votre client pour utiliser le port 11435.
Si un client web en a besoin, placez plutôt un reverse proxy avec mot de passe devant le service. Un bloc de site Caddy tient en quatre lignes, et caddy hash-password affiche le hash bcrypt attendu :
ollama.example.com {
basic_auth {
you $2a$14$replace_with_the_generated_hash
}
reverse_proxy 127.0.0.1:11434
}Caddy obtient lui-même un certificat via TLS (transport layer security), ce qui chiffre le trafic. Testez d’abord votre client. De nombreux outils qui communiquent avec Ollama ne permettent pas de renseigner un en-tête Authorization. Ils échoueront donc avec l’authentification basic, avec un simple 401 Unauthorized. Le tunnel SSH ne présente pas ce problème. C’est pourquoi il constitue la recommandation par défaut ici.
Récupérer un modèle et vérifier l’ensemble du chemin
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 renvoie un JSON qui répertorie gemma3:4b. /api/generate renvoie un objet JSON contenant un champ response, après un délai nécessaire au chargement des poids depuis le disque. du doit renvoyer un nombre proche de la taille de téléchargement publiée. Vérifiez ensuite la partie qui constitue l’objectif de ce guide :
sudo reboot
# reconnect, then:
sudo -iu ollama
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user is-active ollama.serviceactive signifie que le modèle reste chargé ; la section [Install] et daemon-reload ont toutes deux fonctionné. inactive signifie que l’un des trois éléments manque.
Modes d’échec et messages affichés
Le conteneur disparaît après un redémarrage. Vérifiez d’abord loginctl show-user ollama --property=Linger, car sans Linger=yes l’instance systemd de l’utilisateur ne démarre jamais au boot. Si le lingering est activé, la section [Install] est absente du fichier .container, ou vous avez modifié le fichier sans exécuter systemctl --user daemon-reload.
Error: statfs /home/ollama/ollama-data: no such file or directory. La source du bind mount doit exister avant le démarrage du conteneur. Podman ne crée pas les répertoires sur l’hôte pour vous. Exécutez mkdir -p ~/ollama-data avec l’utilisateur ollama.
Le démarrage échoue après 90 secondes. journalctl --user -u ollama.service affiche Start operation timed out. Terminating., car le téléchargement de l’image était toujours en cours. Téléchargez l’image manuellement ou conservez TimeoutStartSec=900.
Le conteneur démarre puis s’arrête. podman logs ollama et sudo ausearch -m avc -ts recent indiquent ensemble si le problème vient du label SELinux. Un AVC mentionnant container_t et user_home_t signifie que :Z est absent.
Les requêtes sont refusées depuis l’hôte. curl: (7) Failed to connect to 127.0.0.1 port 11434: Connection refused avec le service active signifie généralement que OLLAMA_HOST a été défini sur une adresse loopback dans le conteneur. Supprimez cette ligne.
La génération est très lente ou le conteneur est arrêté. Sans GPU, l’inférence s’exécute sur le CPU et un modèle volumineux est naturellement lent. Un conteneur qui s’arrête au milieu d’une requête avec signal: killed dans les journaux est arrêté par l’oom killer du kernel. Choisissez donc un tag plus petit dans le tableau ci-dessus.
Mettre à jour une image épinglée
L’épinglage signifie que les mises à jour sont déclenchées par vous, et non qu’elles surviennent automatiquement. Modifiez Image= dans ollama.container, puis rechargez et redémarrez :
systemctl --user daemon-reload
systemctl --user restart ollama.service
podman exec ollama ollama --versionLes modèles se trouvent dans le bind mount et restent donc intacts lors du changement d’image. AutoUpdate=registry dans la section [Container] s’adresse aux personnes qui utilisent un tag mobile. Cette option n’est pas utile avec un tag de version fixe, car le contenu de ce tag ne change jamais. Sauvegardez /home/ollama/ollama-data/models/manifests et le fichier .container, mais pas les blobs : ils sont volumineux et ollama pull les télécharge de nouveau sur une nouvelle machine.
FAQ
Pourquoi mon conteneur Podman rootless s’arrête-t-il quand je me déconnecte ?
L’instance systemd d’un utilisateur et son répertoire /run/user/<uid> sont supprimés lorsque la dernière session de cet utilisateur se termine. Tous ses conteneurs rootless sont alors arrêtés. Exécutez sudo loginctl enable-linger ollama et vérifiez que loginctl show-user ollama --property=Linger affiche Linger=yes. Activez le lingering avant de créer l’unité Quadlet, car le runtime directory dont l’unité a besoin n’existe qu’une fois le lingering activé.
Les labels SELinux sont-ils nécessaires sur le répertoire des modèles Ollama ?
Sur Fedora, RHEL, Rocky et AlmaLinux, oui, si vous montez un répertoire de l’hôte avec bind mount. Le conteneur s’exécute dans le domaine container_t et un répertoire situé dans un dossier personnel porte le label user_home_t. L’écriture est donc refusée et Ollama s’arrête. Ajoutez :Z à la ligne Volume= et utilisez-lui un sous-répertoire dédié, car le relabelling est récursif. Pointer :Z vers un dossier personnel entier empêcherait l’accès de cet utilisateur à ses clés SSH. Les volumes nommés reçoivent les bons labels avec Podman et ne nécessitent rien de plus.
De quel espace disque un modèle Ollama a-t-il besoin ?
Commencez par consulter la taille de téléchargement publiée sur ollama.com/library. Elle va de 3.3 GB pour gemma3:4b à 19 GB pour qwen3:30b. Ajoutez ensuite la taille de l’image Podman et prévoyez une marge, car un second modèle ne remplace pas le premier sur le disque. Vérifiez df -h /home avant le téléchargement et du -sh ~/ollama-data/models après. Prévoyez la RAM de la même manière : un modèle nécessite environ la taille de son fichier en mémoire lorsqu’il est chargé, plus la fenêtre de contexte.
Est-il sûr d’exposer le port 11434 sur un VPS ?
Non. Ollama ne fournit aucune authentification. Toute personne qui atteint ce port peut lister vos modèles, les supprimer, en télécharger de nouveaux sur votre disque et utiliser votre CPU ainsi que votre quota de bande passante pour exécuter des inférences. Le HTTP en clair sur Internet transmet également chaque prompt et chaque réponse sans chiffrement. Liez le côté hôte à 127.0.0.1 avec PublishPort=127.0.0.1:11434:11434, vérifiez avec ss -ltnp | grep 11434, puis utilisez-le via un tunnel SSH ou un reverse proxy qui exige un mot de passe.