Docker sur un VPS : ce qui change vraiment
Sur un VPS, Docker manque de RAM, contourne UFW via les ports publiés, ne redémarre pas toujours après reboot et peut remplir le disque.
Ce qui change lorsque vous exécutez Docker sur un VPS
Docker sur un VPS utilise le même moteur et les mêmes images que Docker sur votre ordinateur portable. Toutes les commandes que vous connaissez déjà continuent donc de fonctionner. Ce qui change, c’est l’environnement disponible autour de Docker. Un ordinateur portable dispose de mémoire inutilisée, d’un pare-feu que personne ne sonde et d’un disque suffisamment grand pour que vous n’ayez jamais à le surveiller. Un serveur loué a une limite de mémoire fixe, une adresse IP publique qui est sondée quelques minutes après le démarrage et un système de fichiers racine que Docker remplira sans vous demander votre avis.
Quatre différences causent la plupart des problèmes sur une petite machine :
- La mémoire est limitée et le kernel résout un manque de mémoire en tuant un processus.
- Un port publié passe directement à travers UFW (uncomplicated firewall), car Docker écrit ses propres règles de pare-feu.
- Les conteneurs ne redémarrent pas après un reboot, sauf si vous l’avez demandé à l’avance.
- Les images, les conteneurs, les volumes et le build cache grossissent jusqu’à remplir le disque.
Chaque section ci-dessous présente le problème, le message que vous verrez réellement et le guide qui le corrige en détail. Si vous n’avez pas encore écrit de fichier Compose, lisez d’abord les bases de Docker Compose sur un VPS, puis revenez ici. Cette page part du principe que vous savez déjà démarrer une stack.
Quelle quantité de RAM utilise un conteneur Docker ?
Moins que la plupart des utilisateurs ne le pensent. Un conteneur est un processus dans un cgroup (control group), pas une machine virtuelle. Il n’a donc ni noyau invité ni allocation fixe. La consommation correspond à ce que touche le processus exécuté dans le conteneur. C’est pourquoi une stack complète tient dans 2 GB alors que la même stack composée de machines virtuelles ne tiendrait pas.
Les chiffres ci-dessous correspondent à des valeurs typiques au repos pour des images standard sur Ubuntu 24.04 avec la configuration par défaut. Ils sont relevés dans docker stats quelques minutes après le démarrage. Ils servent de base à la planification, pas de benchmark de votre charge de travail. Exécutez docker stats --no-stream sur votre propre serveur avant de faire confiance à une valeur, y compris à celles-ci.
The data behind this chart
[
{
"label": "nginx",
"idle_mb": 8,
"budget_mb": 64
},
{
"label": "Redis 7",
"idle_mb": 12,
"budget_mb": 128
},
{
"label": "Traefik v3",
"idle_mb": 40,
"budget_mb": 128
},
{
"label": "PostgreSQL 16",
"idle_mb": 45,
"budget_mb": 512
},
{
"label": "Uptime Kuma",
"idle_mb": 95,
"budget_mb": 256
},
{
"label": "MariaDB 11",
"idle_mb": 190,
"budget_mb": 512
},
{
"label": "Nextcloud (Apache)",
"idle_mb": 210,
"budget_mb": 768
}
]Les deux colonnes ont des fonctions différentes. idle_mb indique la consommation du conteneur lorsqu’il ne fait rien. budget_mb indique la quantité à réserver lors de la planification, car l’utilisation réelle ne correspond pas à l’état d’inactivité. PostgreSQL reste proche de 45 MB au repos et nécessite 512 MB lorsque les connexions, les tris et le cache sont actifs. Planifiez avec la colonne du budget. Effectuez le diagnostic avec la colonne de consommation au repos.
Observez la répartition de ces 7 lignes. nginx utilise 8 MB au repos, contre 210 MB pour Nextcloud. Le proxy placé devant vos applications consomme presque rien. C’est la base de données et l’application PHP qui déterminent la capacité nécessaire du serveur.
Attention à docker stats : la valeur de mémoire inclut le page cache alimenté par les lectures de fichiers effectuées par le conteneur. Elle augmente donc pendant un certain temps après le démarrage, puis se stabilise. Surveillez-la pendant une heure avant de conclure à une fuite mémoire.
Dimensionner un VPS : que faire tenir dans 2 GB, 4 GB et 8 GB
Commencez par retrancher la part de l’hôte. Le kernel, systemd, journald, sshd et le daemon Docker utilisent la même RAM que vos conteneurs, et dockerd avec containerd en consomme environ 100 MB. Vous devez également conserver de la mémoire libre pour le page cache et pour le pic de consommation lors de la construction d’une image ou de l’exécution d’un dump de base de données.
The data behind this chart
[
{
"plan": "2 GB VPS",
"total_mb": 2048,
"host_reserve_mb": 768,
"container_mb": 1280
},
{
"plan": "4 GB VPS",
"total_mb": 4096,
"host_reserve_mb": 1024,
"container_mb": 3072
},
{
"plan": "8 GB VPS",
"total_mb": 8192,
"host_reserve_mb": 1536,
"container_mb": 6656
}
]host_reserve_mb couvre le système d’exploitation, le daemon Docker et la marge nécessaire pour que le serveur reste réactif sous charge. Le reste correspond à container_mb. C’est le seul budget que vous pouvez allouer. La réserve augmente avec l’offre, de 768 MB sur le plus petit serveur à 1536 MB sur le plus grand, car un serveur plus puissant exécute davantage de conteneurs, écrit davantage de logs et a besoin de plus de page cache.
Une offre de 2 GB laisse 1280 MB pour les conteneurs. Si vous en utilisez 512 MB pour PostgreSQL et 128 MB pour Traefik, la moitié du budget est déjà consommée. Il reste deux petites applications d’environ 256 MB chacune. C’est un serveur réel et utile. Il ne peut pas accueillir Nextcloud et un cluster de recherche en plus.
Une offre de 4 GB laisse 3072 MB. Cette capacité permet d’exécuter simultanément une base de données, un reverse proxy, trois applications et un conteneur de monitoring. C’est la plus petite taille raisonnable pour un service important, car la mémoire disponible absorbe les effets d’un déploiement défectueux.
Une offre de 8 GB laisse 6656 MB sur ses 8192 MB. La limite passe alors généralement de la mémoire au CPU ou au débit du disque. Si les calculs montrent que votre stack ne tient pas, choisissez directement l’offre supérieure au lieu de chercher des réglages de contournement : ce que coûte réellement un VPS explique ce que valent les gigaoctets supplémentaires par mois.
Deux règles permettent de conserver un calcul fiable. Définissez une limite de mémoire pour chaque service afin qu’un processus hors de contrôle ne puisse pas saturer tout le serveur. Et ne dépensez pas la totalité du budget, car docker compose build et pg_dump ont tous deux besoin de mémoire au pire moment. Limites de mémoire dans Docker Compose présente la syntaxe et les pièges à éviter.
Pourquoi mon conteneur s’arrête-t-il avec le code 137 ?
Parce que le kernel l’a tué. 137 correspond à 128 plus 9, et le signal 9 est SIGKILL. Le conteneur a demandé plus de mémoire que sa limite ne l’autorisait, puis le tueur OOM (out of memory) l’a arrêté.
CONTAINER ID IMAGE STATUS
9f2c1a4d7b3e postgres:16 Exited (137) 4 minutes agoConfirmez la cause au lieu de la supposer :
docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory""OOMKilled": true signifie que le conteneur a atteint sa propre limite cgroup, et que le journal du kernel indique le processus choisi :
Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kBC’est le bon cas, car les conséquences sont limitées à un seul conteneur. Le mauvais cas concerne un conteneur sans aucune limite. Sans limite, son plafond correspond à toute la machine. Une fuite mémoire dans un service prive alors l’hôte de mémoire, puis le kernel choisit une victime selon sa taille dans l’ensemble du système. La ligne du journal ne contient plus le préfixe Memory cgroup et devient Out of memory: Killed process 2417 (postgres). Le processus choisi est souvent votre base de données, tandis que le conteneur à l’origine de la fuite continue de fonctionner. C’est pourquoi il est plus important de définir une limite pour chaque service que de choisir précisément la valeur d’une seule limite.
Le swap modifie le moment où le problème survient, pas le calcul. La plupart des images VPS sont fournies sans swap. Vérifiez avec swapon --show, qui n’affiche absolument rien lorsqu’il n’y en a pas. Un fichier de swap donne au kernel un emplacement où placer les pages froides, ce qui vous laisse quelques minutes pour détecter le problème.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h doit maintenant afficher un total différent de zéro sur la ligne Swap. Le swap n’ajoute pas de RAM. Une machine soumise à une pression mémoire constante devient suffisamment lente pour vous empêcher de vous connecter en SSH afin de la dépanner. Considérez donc le swap comme une réserve d’alerte et corrigez le dimensionnement.
Pourquoi UFW ne bloque-t-il pas mon port Docker publié ?
Parce que le trafic n’atteint jamais la chaîne que protège UFW. Lorsque vous publiez un port avec -p 5432:5432 ou avec une entrée ports: dans Compose, le daemon ajoute une règle DNAT (translation d’adresse réseau de destination) dans la table nat et une règle d’acceptation dans sa propre chaîne DOCKER. Un paquet destiné à un conteneur est transféré vers ce conteneur au lieu d’être remis à l’hôte. Il suit donc le chemin FORWARD et ne passe jamais par les règles INPUT écrites par UFW.
Vous pouvez observer ce fonctionnement sur le serveur :
sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432UFW peut afficher 5432 DENY IN Anywhere alors que la table nat contient une règle DNAT tcp ... to:172.18.0.2:5432 pour le même port. Depuis une autre machine, nc -vz your.server.ip 5432 se connecte toujours. La base de données est accessible depuis Internet, contrairement à ce qu’indique le pare-feu.
La solution consiste à publier moins de ports. Les conteneurs d’un même projet Compose partagent un réseau et se joignent par le nom du service. Une base de données qui sert uniquement l’application voisine n’a donc besoin d’aucune entrée ports:. Si vous avez besoin d’un accès local, liez le port publié à loopback :
services:
db:
image: postgres:16
ports:
- "127.0.0.1:5432:5432"Après docker compose up -d, nc -vz your.server.ip 5432 échoue depuis l’extérieur, tandis que psql -h 127.0.0.1 -p 5432 fonctionne toujours sur l’hôte. Dans une petite stack correctement configurée, seul le reverse proxy publie des ports, sur 80 et 443. Pourquoi les ports Docker publiés contournent UFW explique l’utilisation de la chaîne DOCKER-USER dans les cas où vous devez publier un port tout en le filtrant, et Principes de base du pare-feu UFW présente les règles de l’hôte sous-jacentes.
Pourquoi mes conteneurs ont-ils disparu après un redémarrage ?
Parce que rien ne leur a demandé de redémarrer. Par défaut, un conteneur est créé avec la restart policy no. Si vous n’en définissez pas, un redémarrage le laisse arrêté et le daemon ne s’en préoccupe pas. Les redémarrages ne sont pas rares sur un VPS : mises à jour du kernel par unattended upgrades, maintenance du fournisseur et séquence OOM précédente peuvent tous en provoquer un.
Deux conditions doivent être réunies. Le daemon doit démarrer au boot :
systemctl is-enabled dockerCette commande affiche enabled sur une installation Ubuntu standard. Ensuite, chaque service doit avoir une policy :
services:
app:
image: ghcr.io/example/app:1.4
restart: unless-stoppedunless-stopped redémarre le conteneur après un reboot et respecte un conteneur que vous avez arrêté volontairement. always redémarre également ceux que vous avez arrêtés délibérément lorsque le daemon redémarre, ce qui peut surprendre en plein débogage. Modifier le fichier ne suffit pas, car la restart policy est définie lors de la création du conteneur. Exécutez docker compose up -d pour le recréer, puis vérifiez la valeur active :
docker inspect my-app | grep -A3 RestartPolicyRedémarrez ensuite volontairement la machine et exécutez docker compose ps dans le répertoire du projet. Une stack qui survit à un redémarrage planifié survivra aussi à un redémarrage imprévu. Si votre stack nécessite un ordre de démarrage garanti ou une tâche exécutée une seule fois au boot, une unité systemd est préférable : démarrer Docker Compose au boot contient le fichier d’unité. Pour vérifier qu’un conteneur redémarré fournit réellement le service attendu, ajoutez des healthchecks Compose.
Pourquoi le disque de mon VPS est-il plein ?
Parce que Docker conserve tout tant que vous ne lui indiquez pas de faire le ménage. Chaque tag d’image que vous avez téléchargé, chaque conteneur arrêté, chaque volume anonyme laissé après une recréation et chaque couche du build cache restent sur le disque. Avec un système de fichiers root de 40 GB ou 80 GB, ce qui est courant pour ces tailles de forfait, cela provoque une panne en quelques mois plutôt qu’en quelques années.
Un disque plein ne ressemble pas à un crash. Vous obtenez no space left on device depuis un conteneur, depuis apt, depuis journald et depuis docker pull au cours de la même heure. PostgreSQL cesse d’accepter les écritures. La machine reste accessible, ce qui rend le problème plus difficile à détecter qu’une boucle de redémarrage.
Vérifiez avant de supprimer :
docker system df
df -h /docker system df répartit l’espace total entre les images, les conteneurs, les volumes locaux et le build cache, avec une colonne RECLAIMABLE pour chacun. Sur une machine qui construit ses propres images, le build cache est généralement la ligne la plus importante.
docker image prune -a
docker builder prune
docker system dfdocker image prune -a supprime toutes les images qu’aucun conteneur n’utilise. docker builder prune vide le build cache. Ces deux opérations sont sûres pendant l’exécution des services, car les éléments utilisés sont ignorés. En revanche, docker system prune --volumes n’est pas sans risque : cette commande supprime tous les volumes auxquels aucun conteneur ne fait référence à ce moment-là. Une stack que vous avez arrêtée pour le week-end correspond exactement à ce cas, et son volume de base de données est supprimé avec elle. Lisez bind mounts et volumes nommés avant d’utiliser ce flag, et effectuez d’abord une sauvegarde.
Les logs des conteneurs grossissent plus discrètement. Le driver json-file par défaut n’a aucune limite de taille. Un seul conteneur très verbeux peut donc écrire des gigabytes dans /var/lib/docker/containers. Définissez une limite pour chaque conteneur dans /etc/docker/daemon.json :
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Appliquez-la avec sudo systemctl restart docker. Cette commande redémarre vos conteneurs, choisissez donc le bon moment. La limite s’applique aux conteneurs créés après la modification. Recréez les conteneurs existants avec docker compose up -d --force-recreate, puis vérifiez :
docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containersLa sortie de inspect doit afficher max-size comme valeur définie. Si ce champ est vide, le conteneur a été créé avant la modification et continue d’écrire sans limite.
Habitudes qui préservent la santé d’un petit serveur Docker
Rien de tout cela ne nécessite un dashboard ni un outil à apprendre.
- Exécutez
docker system dfetdf -h /le premier jour du mois. Deux commandes, trente secondes, et vous voyez la tendance bien avant qu’elle ne provoque une interruption de service. - Définissez une limite de mémoire pour chaque service, y compris ceux que vous pensez être suffisamment petits. Cette limite transforme une panne de l’hôte en un seul conteneur redémarré.
- Surveillez le serveur depuis un autre emplacement afin d’être informé de la pression sur la mémoire ou le disque avant que le kernel n’intervienne. Uptime Kuma s’exécute dans un conteneur et consomme environ 95 MB au repos.
- Sauvegardez les volumes, pas les conteneurs. Le conteneur est remplaçable, contrairement au volume. sauvegardes restic sur un VPS couvre la planification des sauvegardes et un test de restauration.
- Épinglez les tags des images dans le fichier compose et mettez-les à jour le jour de votre choix. Avec
latest, la version obtenue lors du prochaindocker compose pullcorrespond à celle publiée ce matin-là.
Un petit VPS qui exécute Docker reste sain pendant des années lorsque quatre valeurs restent dans une plage acceptable : le budget mémoire, la liste des ports publiés, la restart policy de chaque service et l’espace disque disponible. Pour le reste, il s’agit du même Docker que celui que vous utilisez déjà chez vous.
FAQ
De quelle quantité de RAM ai-je besoin pour exécuter Docker sur un VPS ?
Docker lui-même consomme peu de ressources. Le daemon et containerd utilisent ensemble environ 100 MB. Le reste dépend de vos conteneurs. Réservez d’abord la part de l’hôte : 768 MB sur une machine de 2048 MB pour le système d’exploitation, le daemon et une marge de sécurité. Il reste alors 1280 MB pour les conteneurs. Une base de données à 512 MB, un reverse proxy à 128 MB et deux petites applications tiennent dans cette limite. Mesurez votre propre stack avec docker stats --no-stream au lieu de vous fier à une valeur publiée.
Puis-je exécuter Docker sur un VPS de 1 GB ?
Oui, pour un ou deux conteneurs légers. Ajoutez un fichier de swap avant de commencer. Environ la moitié d’une machine de 1 GB est déjà utilisée lorsque le système d’exploitation et le daemon Docker fonctionnent. Il reste assez de ressources pour une petite application et un reverse proxy, mais pas pour une base de données soumise à une charge réelle. La construction d’images sur une machine de cette taille échouera ou arrêtera un autre service. Construisez donc les images ailleurs, puis récupérez l’image terminée.
UFW protège-t-il un conteneur Docker ?
Pas pour les ports que vous publiez. Docker crée ses propres règles DNAT et de forward. Un paquet destiné à un port de conteneur publié est donc transféré vers le conteneur au lieu d’être remis à l’hôte. Les règles INPUT gérées par UFW ne le voient jamais. ufw deny 5432 peut être actif alors que ce port répond depuis Internet. Publiez le port sur loopback avec 127.0.0.1:5432:5432, ne publiez pas les services internes ou filtrez le trafic dans la chaîne DOCKER-USER.
Mes conteneurs redémarreront-ils après le redémarrage d’un VPS ?
Uniquement s’ils ont été créés avec une restart policy. Définissez restart: unless-stopped pour chaque service, exécutez docker compose up -d afin de recréer les conteneurs avec cette policy, puis vérifiez que systemctl is-enabled docker affiche enabled. Redémarrez ensuite volontairement le VPS et contrôlez docker compose ps. Une restart policy que vous n’avez jamais testée n’est pas une restart policy fiable.
À quelle fréquence dois-je supprimer les images Docker inutilisées ?
Une fois par mois suffit pour la plupart des petits serveurs. Vous pouvez aussi le faire lorsque docker system df indique qu’un espace récupérable important serait utile. docker image prune -a et docker builder prune sont sans danger pendant l’exécution des services, car les images et le cache utilisés sont ignorés. Évitez docker system prune --volumes, sauf si vous savez exactement quels volumes ne sont référencés par aucun conteneur. Cette commande supprime les données de toute stack qui serait arrêtée.