SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-22

Docker sur un VPS : qu’est-ce qui change vraiment ?

Le moteur Docker reste identique, mais un petit VPS manque vite de RAM, les ports publiés contournent UFW, les conteneurs s’arrêtent au reboot et le disque se remplit.

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à fonctionnent donc toujours. Ce qui change, c’est l’environnement disponible. Un ordinateur portable dispose de mémoire libre, 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 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 une pénurie 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’à saturer le disque.

Chaque section ci-dessous indique la panne, le message que vous verrez réellement et le guide qui la 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 suppose que vous savez déjà démarrer une stack.

Combien de RAM un conteneur Docker utilise-t-il ?

Moins que la plupart des utilisateurs ne le pensent. Un conteneur est un processus placé dans un cgroup (control group), pas une machine virtuelle. Il n’y a donc ni kernel invité ni allocation fixe. La consommation correspond à ce que le processus à l’intérieur utilise réellement. C’est pourquoi une stack complète tient dans 2 GB alors que la même stack, construite avec des machines virtuelles, ne tiendrait pas.

Les valeurs ci-dessous correspondent à des consommations typiques au repos pour des images standard sur Ubuntu 24.04 avec la configuration par défaut. Elles sont relevées dans docker stats quelques minutes après le démarrage. Elles servent de base pour planifier les ressources, pas de benchmark de votre charge de travail. Exécutez docker stats --no-stream sur votre propre serveur avant de vous fier à une valeur, y compris à celles-ci.

ChartTypical idle container memory, stock images, megabytes
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 ce que le conteneur utilise lorsqu’il ne fait rien. budget_mb indique la quantité à réserver lors de la planification, car l’utilisation réelle ne se limite pas à l’état d’inactivité. PostgreSQL utilise environ 45 MB au repos et a besoin de 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 et Nextcloud 210 MB. Le proxy placé devant vos applications consomme presque rien. C’est pour la base de données et l’application PHP que vous devez dimensionner le serveur.

Attention à docker stats : la valeur de mémoire inclut le cache de pages chargé par les lectures de fichiers du 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 : ce qui tient dans 2 GB, 4 GB et 8 GB

Commencez par soustraire 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 consomment environ 100 MB. Vous devez également conserver de la mémoire libre pour le page cache et pour le pic de consommation lorsqu’un build d’image ou un dump de base de données s’exécute.

ChartRAM budget by plan size, megabytes
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, et c’est le seul budget dont vous disposez. 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 utilise davantage de page cache.

Une offre de 2 GB laisse 1280 MB pour les conteneurs. Si vous en consacrez 512 MB à PostgreSQL et 128 MB à Traefik, la moitié du budget est déjà consommée. Le reste suffit pour deux petites applications d’environ 256 MB chacune. C’est un serveur réel et utile. Il ne permet pas d’ajouter Nextcloud et un cluster de recherche.

Une offre de 4 GB laisse 3072 MB. Cette capacité suffit à 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 adaptée à 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, et la limite passe généralement de la mémoire au CPU ou au débit du disque. Certains conteneurs dimensionnent leur consommation selon leur configuration plutôt que selon la charge : un serveur de modèle local réserve un cache KV proportionnel à sa fenêtre de contexte. Ainsi, augmenter le paramètre num_ctx d’Ollama peut ajouter plusieurs gigaoctets au budget avant l’arrivée de la moindre requête. Si les calculs montrent que votre stack ne tient pas, prenez directement l’offre supérieure au lieu de contourner la limite avec des réglages : ce que coûte réellement un VPS indique la valeur mensuelle des gigaoctets supplémentaires.

Deux règles permettent de garder des calculs réalistes. Définissez une limite mémoire pour chaque service afin qu’un processus hors de contrôle ne puisse pas saturer tout le serveur. Laissez également une partie du budget disponible, car docker compose build et pg_dump ont tous deux besoin de mémoire au pire moment. Les limites 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 l’out of memory (OOM) killer l’a arrêté.

CONTAINER ID   IMAGE         STATUS
9f2c1a4d7b3e   postgres:16   Exited (137) 4 minutes ago

Confirmez 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 sélectionné :

Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kB

C’est le bon cas, car le problème reste limité à 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 consommation à l’échelle du système. La ligne du journal ne contient plus le préfixe Memory cgroup et affiche Out of memory: Killed process 2417 (postgres). Le processus sélectionné 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 change le moment où le problème survient, pas les calculs. 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 swap fournit 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 -h

free -h doit maintenant afficher un total non nul 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 y connecter en SSH afin de corriger le problème. Considérez donc le swap comme un tampon 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 UFW protège. Lorsque vous publiez un port avec -p 5432:5432 ou avec une entrée Compose ports:, 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 ajoutées par UFW.

Vous pouvez observer ce comportement sur le serveur :

sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432

UFW 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, même si le pare-feu indique le contraire.

La solution consiste à publier moins de ports. Les conteneurs d’un même projet Compose partagent un réseau et peuvent communiquer entre eux avec 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 la publication à 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 le serveur. 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 présente la chaîne DOCKER-USER pour les cas où vous devez publier un port tout en le filtrant, et Bases du pare-feu UFW présente les règles de l’hôte situées en dessous.

Pourquoi mes conteneurs ont-ils disparu après un redémarrage ?

Parce que rien ne leur a demandé de redémarrer. Un conteneur est créé avec la policy de redémarrage no si vous n’en définissez pas. Après un redémarrage, il reste donc arrêté et le daemon ne s’en préoccupe pas. Les redémarrages ne sont pas rares sur un VPS : les mises à jour du kernel via unattended upgrades, la maintenance du fournisseur et la séquence OOM décrite plus haut peuvent tous en provoquer un.

Deux conditions doivent être réunies. Le daemon doit démarrer au boot :

systemctl is-enabled docker

Cette commande affiche enabled sur une installation Ubuntu standard. Chaque service doit ensuite avoir une policy :

services:
  app:
    image: ghcr.io/example/app:1.4
    restart: unless-stopped

unless-stopped redémarre le conteneur après un reboot, tout en respectant un conteneur que vous avez arrêté volontairement. always redémarre également les conteneurs que vous avez arrêtés délibérément lorsque le daemon redémarre, ce qui peut être surprenant pendant un dépannage. Modifier le fichier ne suffit pas, car la policy de redémarrage 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 RestartPolicy

Redémarrez ensuite volontairement le serveur 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 a besoin d’un ordre de démarrage garanti ou d’un job exécuté 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 demandez pas de nettoyer. Chaque tag d’image que vous avez téléchargé, chaque conteneur arrêté, chaque volume anonyme abandonné lors d’une recréation et chaque couche du cache de build restent sur le disque. Sur un système de fichiers root de 40 GB ou 80 GB, ce qui est courant avec ces tailles d’offre, 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 n’accepte plus les écritures. Le serveur reste accessible, ce qui rend le problème plus difficile à remarquer 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 cache de build, avec une colonne RECLAIMABLE pour chacun. Sur un serveur qui construit ses propres images, le cache de build est généralement la ligne la plus importante.

docker image prune -a
docker builder prune
docker system df

docker image prune -a supprime toutes les images qu’aucun conteneur n’utilise. docker builder prune vide le cache de build. Les deux opérations sont sûres pendant que les services fonctionnent, car les éléments utilisés sont ignorés. En revanche, docker system prune --volumes n’est pas sûr : cette commande supprime tous les volumes auxquels aucun conteneur ne fait actuellement référence. Une stack arrêtée pour le week-end correspond exactement à ce cas, et son volume de base de données est supprimé avec elle. Lisez montages bind et volumes nommés avant d’utiliser ce flag, et faites d’abord une sauvegarde.

Les logs des conteneurs sont une source de croissance plus discrète. Le driver json-file utilisé par défaut n’a aucune limite de taille. Un conteneur très bavard peut donc écrire des gigaoctets dans /var/lib/docker/containers. Limitez la taille pour chaque conteneur dans /etc/docker/daemon.json :

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Appliquez la modification avec sudo systemctl restart docker. Cette commande redémarre vos conteneurs : choisissez donc le moment approprié. La limite s’applique aux conteneurs créés après la modification. Recréez donc ceux qui fonctionnent avec docker compose up -d --force-recreate, puis vérifiez :

docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containers

La sortie de inspect doit indiquer que max-size est défini. Si cette valeur est vide, le conteneur a été créé avant la modification et écrit toujours sans limite.

Habitudes qui permettent de maintenir un petit serveur Docker en bon état

Aucune de ces pratiques ne nécessite un dashboard ni un outil à apprendre.

  • Exécutez docker system df et df -h / le premier jour du mois. Deux commandes, trente secondes, et vous voyez la tendance bien avant qu’elle ne provoque une panne.
  • Définissez une limite de mémoire pour chaque service, y compris ceux que vous pensez être petits. La limite transforme une panne de l’hôte entier 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 utilise environ 95 MB au repos.
  • Sauvegardez les volumes, pas les conteneurs. Le conteneur est jetable, le volume ne l’est pas. sauvegardes restic sur un VPS couvre la planification des sauvegardes et un test de restauration.
  • Épinglez les tags d’image dans le fichier compose et mettez-les à jour le jour de votre choix. Avec latest, la version obtenue lors du prochain docker compose pull est celle publiée ce matin-là.

Un petit VPS qui exécute Docker reste en bon état pendant des années lorsque quatre valeurs restent dans la plage prévue : le budget mémoire, la liste des ports publiés, la politique de redémarrage de chaque service et l’espace disque disponible. Pour le reste, c’est le 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 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 enveloppe. 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 swap avant de commencer. Environ la moitié d’une machine de 1 GB est déjà utilisée une fois le système d’exploitation et le daemon Docker démarrés. 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 provoquera l’arrêt d’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 forwarding. Un paquet destiné au port publié d’un conteneur est donc redirigé vers le conteneur au lieu d’être livré à l’hôte, et 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 la 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 reboot d’un VPS ?

Seulement 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 la machine 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 nettoyer les images Docker ?

Une fois par mois suffit pour la plupart des petits serveurs. Vous pouvez aussi le faire lorsque docker system df indique un espace récupérable dont vous avez besoin. docker image prune -a et docker builder prune peuvent être exécutées pendant que les services fonctionnent : 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, car cette commande supprime les données de toute stack qui se trouve arrêtée.