Exécuter Docker Compose en tant que service Linux avec systemd

Lancement de Docker Compose au démarrage, géré par systemd.

Sommaire

Docker Compose sur un serveur Linux doit démarrer au boot, s’arrêter proprement à l’extinction et survivre aux redémarrages sans intervention manuelle.

Docker Compose n’est pas Kubernetes, et c’est acceptable pour les charges de travail que ce guide cible. Pour de nombreux systèmes réels, un projet Compose sur un hôte Linux unique constitue la juste dose d’infrastructure : simple, lisible, facile à sauvegarder et suffisamment performant pour les outils internes, les projets annexes, les services auto-hébergés, les environnements de préproduction, les petites applications de production et l’infrastructure de développement.

docker compose config ont the table with laptop

L’éléman tmanquant est généralement la gestion des services. Exécuter cela manuellement ne suffit pas :

docker compose up -d

Une seule commande démarre la pile, mais elle ne documente pas comment la pile doit démarrer au boot, s’arrêter pendant l’extinction, recharger après des changements, écrire les journaux, récupérer après des échecs ou être mise à jour en toute sécurité. C’est là que systemd intervient.

Ce guide explique comment exécuter un projet Docker Compose en tant que service Linux avec systemd — fichiers unit, ordre de démarrage, mises à jour, journaux et sauvegardes. La répartition des responsabilités est volontaire : Docker exécute les conteneurs, Compose définit la pile, et systemd démarre et arrête le projet sur l’hôte. Cela fait partie de Outils pour développeurs - Un guide des flux de travail de développement.

Quand il est logique d’utiliser Docker Compose en tant que service

Exécuter Compose sous systemd a du sens lorsque vous avez :

  • Un serveur Linux unique
  • Une petite application auto-hébergée
  • Une pile de proxy inverse
  • Une pile de surveillance
  • Une plateforme de développement locale
  • Un outil interne
  • Un environnement de préproduction
  • Un service de production simple avec des limites connues

Exemples :

  • Nginx Proxy Manager
  • Traefik
  • Gitea
  • Grafana et Prometheus
  • PostgreSQL plus une petite application web
  • Uptime Kuma
  • Services auxiliaires Home Assistant
  • Registre privé
  • API interne plus worker plus Redis

Compose est un bon choix lorsque le modèle opérationnel reste compréhensible par une seule personne lisant un seul répertoire.

Quand Docker Compose ne suffit pas

Utilisez autre chose lorsque vous avez besoin de :

  • Planification multi-nœuds
  • Réplanification automatique entre les hôtes
  • Découverte de services au niveau du cluster
  • Mise à l’échelle horizontale automatique
  • Déploiements progressifs sur de nombreuses machines
  • Identité de charge de travail fine
  • Politiques réseau complexes
  • Opérations de plateforme multi-équipes de grande envergure

À ce stade, Kubernetes, Nomad, Swarm ou une plateforme gérée peuvent être plus adaptés.

Ma règle pratique est d’éviter d’utiliser Kubernetes juste pour éviter d’apprendre systemd, et d’éviter d’utiliser Compose lorsque la charge de travail nécessite clairement une orchestration sur plusieurs hôtes.

L’architecture de base

Une configuration propre sépare les fichiers du projet, l’unité systemd et les données persistantes sur l’hôte. Le projet Compose réside sous /opt/myapp/ avec compose.yaml, .env, data/, backups/ et des scripts optionnels tels que scripts/update.sh. Le fichier unit systemd se trouve à /etc/systemd/system/myapp.service.

flowchart TB subgraph host["Linux host"] systemd["systemd unit\n/etc/systemd/system/myapp.service"] compose["Docker Compose\n/opt/myapp/compose.yaml"] docker["Docker Engine"] fs["Persistent data\n/opt/myapp/data/"] end systemd -->|"ExecStart: docker compose up -d"| compose compose --> docker docker --> fs

Chaque couche a un rôle clair : Docker exécute les conteneurs, Compose définit la pile d’application, systemd démarre et arrête le projet Compose au boot et à l’extinction, le système de fichiers de l’hôte stocke les données persistantes, les sauvegardes restent explicites, et les mises à jour passent par des étapes scriptées et révisables. Cette disposition est délibérément ennuyeuse, car une infrastructure ennuyeuse est plus facile à réparer lorsque quelque chose casse à 2 heures du matin.

Préparer le répertoire du projet Compose

Créez un répertoire sous /opt :

sudo mkdir -p /opt/myapp
sudo chown -R "$USER":"$USER" /opt/myapp
cd /opt/myapp

Créez un fichier Compose :

nano compose.yaml

Exemple :

services:
  web:
    image: nginx:stable
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    healthcheck:
      test: ["CMD-SHELL", "nginx -t || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

volumes: {}

Créez le répertoire de contenu :

mkdir -p html
echo "Hello from Docker Compose" > html/index.html

Testez d’abord manuellement :

docker compose up -d
docker compose ps
docker compose logs --tail=50

Ensuite, arrêtez-le avant de confier le cycle de vie à systemd :

docker compose down

Ne créez pas de service systemd tant que le projet Compose ne fonctionne pas manuellement. Pendant vos tests, gardez la Fiche de référence Docker Compose à portée de main pour ps, logs, pull et la structure du projet.

Utiliser la commande moderne docker compose

Docker Engine et le plugin Compose doivent être installés avant d’écrire un fichier unit. Sous Ubuntu, Installer Docker sur Ubuntu détaille APT, Snap, le mode sans privilèges et la sécurité post-installation pour que vous obteniez une commande docker compose fonctionnelle.

Utilisez ceci :

docker compose version

Pas ceci :

docker-compose version

L’ancien binaire docker-compose existe encore sur de nombreuses machines, mais Docker moderne utilise Compose en tant que plugin CLI Docker.

Dans les fichiers de service et les scripts, préférez :

/usr/bin/docker compose

Vous pouvez trouver le chemin Docker avec :

command -v docker

En général, c’est :

/usr/bin/docker

Créer un service systemd pour Docker Compose

Si les fichiers unit sont nouveaux pour vous, Exécuter n’importe quel exécutable en tant que service sous Linux explique Type, ExecStart, systemctl et le workflow général de systemd. Cette section applique ces modèles spécifiquement à une pile Compose.

Créez le fichier de service :

sudo nano /etc/systemd/system/myapp.service

Utilisez cette unité :

[Unit]
Description=MyApp Docker Compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Rechargez systemd :

sudo systemctl daemon-reload

Démarrez le service :

sudo systemctl start myapp.service

Activez-le au démarrage :

sudo systemctl enable myapp.service

Vérifiez l’état :

systemctl status myapp.service

Vérifiez les conteneurs :

cd /opt/myapp
docker compose ps

Pourquoi Type=oneshot et RemainAfterExit=yes ?

C’est la partie que de nombreux guides traitent de manière subtilement incorrecte.

docker compose up -d démarre les conteneurs en mode détaché et se termine, donc il n’y a pas de processus Compose en premier plan de longue durée pour que systemd puisse superviser. L’unité systemd ne devrait pas prétendre que docker compose up -d est un démon de longue durée.

Utilisez :

Type=oneshot
RemainAfterExit=yes

Cela indique à systemd :

  • Exécuter la commande de démarrage.
  • Considérer l’unité active après que la commande se soit terminée avec succès.
  • Exécuter ExecStop lorsque le service est arrêté.

Cela correspond au comportement réel de Compose détaché, c’est pourquoi Type=oneshot avec RemainAfterExit=yes est le paramètre par défaut correct pour la plupart des piles.

Pourquoi pas Type=simple ?

Avec Type=simple, systemd s’attend à ce que le processus ExecStart continue de s’exécuter, mais docker compose up -d se termine après avoir démarré les conteneurs. Cela peut amener systemd à penser que le service s’est terminé, puis à appeler la logique d’arrêt ou à marquer l’unité inactive selon la configuration.

Si vous voulez Type=simple, vous exécuteriez généralement Compose en premier plan :

ExecStart=/usr/bin/docker compose up

Cela peut fonctionner, mais je ne le préfère généralement pas pour les piles Compose sur les serveurs. Les conteneurs détachés plus ExecStop explicite sont plus faciles à exploiter.

Une unité plus adaptée à la production

Pour un serveur réel, je préfère une unité légèrement plus stricte :

[Unit]
Description=MyApp Docker Compose stack
Documentation=https://example.com/docs/myapp
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
EnvironmentFile=-/opt/myapp/.env.systemd
ExecStartPre=/usr/bin/docker compose config --quiet
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecReload=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Détails importants :

  • WorkingDirectory pointe vers le projet Compose.
  • ExecStartPre valide la configuration Compose.
  • ExecReload recrée les services modifiés.
  • ExecStop arrête et supprime les conteneurs du projet Compose et le réseau par défaut.
  • EnvironmentFile=-... signifie que le fichier est optionnel.

Créez le fichier d’environnement systemd optionnel :

nano /opt/myapp/.env.systemd

Exemple :

COMPOSE_PROJECT_NAME=myapp

Ensuite, rechargez systemd :

sudo systemctl daemon-reload
sudo systemctl restart myapp.service

Compose .env vs EnvironmentFile systemd

Compose et systemd ont chacun leur propre mécanisme d’environnement, et les mélanger cause des échecs déroutants de « variable non définie » au démarrage.

Compose lit automatiquement un fichier .env dans le répertoire du projet pour la substitution de variables dans le fichier Compose.

Exemple .env :

APP_TAG=1.2.3
WEB_PORT=8080

Exemple compose.yaml :

services:
  web:
    image: nginx:${APP_TAG}
    ports:
      - "${WEB_PORT}:80"

Un EnvironmentFile systemd définit les variables d’environnement pour la commande docker compose elle-même.

Exemple :

EnvironmentFile=-/opt/myapp/.env.systemd

Pour de nombreux projets, vous n’avez besoin que de .env Compose.

Utilisez un fichier d’environnement systemd lorsque vous voulez définir des éléments tels que :

COMPOSE_PROJECT_NAME=myapp
COMPOSE_FILE=compose.yaml
DOCKER_HOST=unix:///var/run/docker.sock

N’utilisez aucun de ces fichiers comme coffre-fort de secrets décontracté. Si les secrets comptent, utilisez les secrets Docker, un gestionnaire de secrets externe, des fichiers chiffrés ou au moins des permissions strictes.

Définissez des permissions restrictives :

chmod 600 /opt/myapp/.env
chmod 600 /opt/myapp/.env.systemd

Politiques de redémarrage : Docker vs systemd

Il y a deux couches de redémarrage — la politique de redémarrage des conteneurs dans Compose et la politique de redémarrage du service systemd — et elles ne devraient pas être mélangées aveuglément.

Pour les conteneurs de longue durée, définissez les politiques de redémarrage dans Compose :

services:
  web:
    image: nginx:stable
    restart: unless-stopped

Valeurs de redémarrage courantes :

Politique Signification
no Ne pas redémarrer automatiquement
always Redémarrer après sortie et redémarrage du daemon
on-failure Redémarrer uniquement après échec
unless-stopped Redémarrer sauf si arrêté manuellement

Pour la plupart des services persistants, je préfère :

restart: unless-stopped

C’est prévisible et respecte les arrêts manuels intentionnels.

L’unité systemd elle-même ne devrait généralement pas redémarrer en boucle, car docker compose up -d n’est pas la charge de travail en cours. Ce sont les conteneurs.

Évitez donc ceci sauf si vous avez une raison spécifique :

Restart=always

Dans la plupart des unités Compose-en-service, laissez Docker gérer les redémarrages des conteneurs.

Contrôles de santé (Health Checks)

Les politiques de redémarrage redémarrent les conteneurs lorsque les processus se terminent. Elles ne corrigent pas magiquement chaque application malsaine.

Ajoutez des contrôles de santé là où ils sont utiles :

services:
  app:
    image: example/app:latest
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://localhost:8080/health || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s

Vérifiez la santé :

docker compose ps

Inspectez un conteneur :

docker inspect container-name

Les contrôles de santé sont particulièrement utiles pour :

  • Applications web
  • Proxies inverses
  • Bases de données
  • Files d’attente
  • API internes
  • Workers avec un point de terminaison de santé

Ils sont moins utiles lorsqu’ils ne vérifient que l’existence d’un processus, car un processus vivant mais bloqué semble toujours en bonne santé. Un mauvais contrôle de santé n’est qu’un autre mensonge en YAML.

Ordre de démarrage et depends_on

Compose peut définir des dépendances :

services:
  app:
    image: example/app:latest
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

Cela peut aider l’ordre de démarrage, mais ne faites pas trop confiance. Les applications devraient toujours gérer les tentatives — les bases de données redémarrent, les réseaux fluctuent, le DNS prend du temps, et une application résiliente réessaie les connexions au lieu de supposer un ordre de démarrage parfait.

Journaux : journalctl et docker compose logs

Deux vues de journaux couvrent la plupart du débogage : systemd capture le cycle de vie de l’unité elle-même, tandis que Compose capture la sortie de l’application des conteneurs en cours d’exécution.

Journaux du service systemd :

journalctl -u myapp.service -n 100 --no-pager

Suivre les journaux systemd :

journalctl -u myapp.service -f

Journaux du service Compose :

cd /opt/myapp
docker compose logs --tail=100
docker compose logs -f
docker compose logs -f web

Pour la plupart des débogages d’application, docker compose logs est plus utile ; pour le débogage du cycle de vie — échecs de démarrage, plantages d’unité, erreurs de permissions — journalctl est plus utile. Si systemctl start myapp échoue, vérifiez journalctl en premier. Si la pile démarre mais que l’application est cassée, vérifiez docker compose logs.

Rotation des journaux

Les journaux Docker peuvent croître indéfiniment si vous ne les configurez pas.

Pour les petits serveurs, configurez la rotation des journaux Docker dans /etc/docker/daemon.json :

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

Redémarrez Docker :

sudo systemctl restart docker

Ensuite, redémarrez la pile Compose :

sudo systemctl restart myapp.service

Cela s’applique aux conteneurs nouvellement créés. Recréez les conteneurs si nécessaire :

cd /opt/myapp
docker compose up -d --force-recreate

La rotation des journaux n’est pas glamour, mais c’est l’un des moyens les plus simples d’empêcher une panne de disque plein sur un petit serveur.

Mettre à jour un service Compose

Un flux de mise à jour manuel simple :

cd /opt/myapp
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f

Si géré par systemd, vous pouvez utiliser :

sudo systemctl reload myapp.service

Si votre unité a :

ExecReload=/usr/bin/docker compose up -d --remove-orphans

Mais notez : ExecReload ne télécharge pas les images à moins que vous n’incluez cette étape.

Pour des mises à jour explicites, créez un script.

mkdir -p /opt/myapp/scripts
nano /opt/myapp/scripts/update.sh

Script :

#!/usr/bin/env bash
set -euo pipefail

cd /opt/myapp

docker compose config --quiet
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f
docker compose ps

Rendez-le exécutable :

chmod +x /opt/myapp/scripts/update.sh

Exécutez-le :

/opt/myapp/scripts/update.sh

Ensuite, l’unité de service peut rester focalisée sur le cycle de vie, tandis que le script de mise à jour gère le déploiement.

Script de mise à jour plus sûr avec sauvegarde

Pour les services avec état, mettez à jour uniquement après sauvegarde.

#!/usr/bin/env bash
set -euo pipefail

APP_DIR="/opt/myapp"
BACKUP_DIR="/opt/myapp/backups"

cd "$APP_DIR"

mkdir -p "$BACKUP_DIR"

echo "Validation du fichier compose"
docker compose config --quiet

echo "Exécution de la sauvegarde"
if [ -x "$APP_DIR/scripts/backup.sh" ]; then
  "$APP_DIR/scripts/backup.sh"
else
  echo "Aucun hook de sauvegarde trouvé"
fi

echo "Téléchargement des images"
docker compose pull

echo "Recréation des services"
docker compose up -d --remove-orphans

echo "Nettoyage des images inutilisées"
docker image prune -f

echo "État actuel"
docker compose ps

Ceci est toujours simple, mais encode maintenant une habitude opérationnelle : sauvegarder avant de changer.

Arrêter le service

Arrêtez la pile :

sudo systemctl stop myapp.service

Cela exécute :

docker compose down

Par défaut, docker compose down supprime :

  • Les conteneurs pour les services dans le fichier Compose
  • Les réseaux définis par le fichier Compose
  • Le réseau par défaut

Il ne supprime pas les volumes nommés à moins que vous ne le lui demandiez.

N’utilisez pas casually :

docker compose down -v

Cela supprime les volumes nommés déclarés dans le fichier Compose et les volumes anonymes attachés aux conteneurs. Pour les bases de données et les applications avec état, cela peut signifier supprimer de vraies données.

Utilisez down -v uniquement lorsque vous voulez dire “détruire cet environnement”.

Redémarrer le service

Redémarrez l’unité systemd :

sudo systemctl restart myapp.service

Cela exécute la commande d’arrêt puis la commande de démarrage.

Pour ne redémarrer que les conteneurs sans les recréer :

cd /opt/myapp
docker compose restart

Distinction importante :

  • docker compose restart redémarre les conteneurs existants.
  • docker compose up -d applique les modifications de configuration ou d’image en recréant les conteneurs si nécessaire.

Si vous avez modifié compose.yaml, utilisez :

docker compose up -d

Pas juste :

docker compose restart

Gérer les conteneurs orphelins

Si vous renommez ou supprimez un service dans compose.yaml, les anciens conteneurs peuvent rester comme orphelins.

Utilisez :

docker compose up -d --remove-orphans

C’est pourquoi les exemples de service systemd de ce guide utilisent :

ExecStart=/usr/bin/docker compose up -d --remove-orphans

Cela garde la pile plus proche du fichier Compose actuel.

Sauvegardes

Les sauvegardes dépendent de la charge de travail, mais les principes sont stables.

Pour les montages de liaison :

/opt/myapp/data/

Sauvegardez ce répertoire.

Pour les volumes nommés :

docker volume ls

Inspectez un volume :

docker volume inspect volume-name

Pour les bases de données, les copies de système de fichiers ne suffisent pas toujours. Utilisez des sauvegardes conscientes de l’application :

Exemple PostgreSQL :

docker compose exec -T db pg_dump -U postgres appdb > backups/appdb.sql

Exemple MariaDB :

docker compose exec -T db mariadb-dump -u root -p appdb > backups/appdb.sql

Exemple Redis :

docker compose exec redis redis-cli BGSAVE

Une pile Compose sans plan de sauvegarde n’est pas un service — c’est une expérience temporaire qui a la chance d’avoir du temps de fonctionnement.

Sécurité de base

Pour un petit service Compose sur Linux, commencez par cette base :

  • Gardez le projet Compose sous /opt/appname.
  • Utilisez des tags d’image explicites, pas seulement latest, lorsque la stabilité compte.
  • Utilisez les montages de liaison ou les volumes nommés délibérément.
  • N’exposez pas les ports dont vous n’avez pas besoin.
  • Placez les services publics derrière un proxy inverse.
  • Utilisez HTTPS à la périphérie.
  • Gardez les secrets hors de Git.
  • Restreignez les permissions de .env.
  • Évitez les conteneurs privilégiés sauf si réellement requis.
  • Évitez de monter le socket Docker dans les conteneurs.
  • Gardez Docker et les images à jour.
  • Testez le comportement du pare-feu depuis une autre machine.

Un modèle dangereux :

volumes:
  - /var/run/docker.sock:/var/run/docker.sock

Cela donne au conteneur le contrôle sur Docker. En pratique, cela peut devenir un contrôle au niveau de l’hôte. Utilisez-le uniquement lorsque vous comprenez le risque.

Limites de ressources

Sur les petits serveurs, un seul mauvais conteneur peut consommer l’hôte.

Compose prend en charge les paramètres liés aux ressources, mais le comportement peut dépendre de la version de Docker Engine et Compose. Pour une protection simple, commencez par les limites au niveau de l’application et les limites de journalisation Docker.

Pour certaines charges de travail, vous pouvez ajouter des limites de mémoire :

services:
  app:
    image: example/app:stable
    restart: unless-stopped
    mem_limit: 512m

Configurez également les nombres de workers au niveau de l’application, les limites de file d’attente et les tailles de cache. Les limites des conteneurs sont utiles, mais elles ne remplacent pas la compréhension de l’application.

Exemple : Un service Compose réaliste

Répertoire :

/opt/whoami/
  compose.yaml
  .env

Fichier Compose :

services:
  whoami:
    image: traefik/whoami:v1.10
    restart: unless-stopped
    ports:
      - "${WHOAMI_PORT}:80"
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://localhost || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3

Fichier .env :

WHOAMI_PORT=8080
COMPOSE_PROJECT_NAME=whoami

Unité systemd :

[Unit]
Description=Whoami Docker Compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/whoami
ExecStartPre=/usr/bin/docker compose config --quiet
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecReload=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Installez-le :

sudo systemctl daemon-reload
sudo systemctl enable --now whoami.service

Testez :

curl http://localhost:8080

Vérifiez l’état :

systemctl status whoami.service
cd /opt/whoami
docker compose ps

Dépannage

Le service démarre mais les conteneurs ne sont pas en cours d’exécution

Vérifiez systemd :

journalctl -u myapp.service -n 100 --no-pager

Validez Compose :

cd /opt/myapp
docker compose config

Vérifiez Docker :

systemctl status docker
docker info

WorkingDirectory est incorrect

Si systemd ne peut pas trouver votre fichier Compose, confirmez :

WorkingDirectory=/opt/myapp

Ensuite, vérifiez :

ls -la /opt/myapp
ls -la /opt/myapp/compose.yaml

Le service s’exécute depuis WorkingDirectory, pas depuis votre répertoire de shell actuel.

Permission refusée Docker

Si l’unité s’exécute en tant que root, elle peut normalement accéder à Docker.

Si vous définissez User=someuser, cet utilisateur doit pouvoir accéder à Docker. Habituellement, cela signifie l’appartenance au groupe docker, ou une configuration Docker sans privilèges.

Vérifiez :

groups someuser

Ajoutez l’utilisateur si approprié :

sudo usermod -aG docker someuser

Soyez prudent. Le groupe Docker est effectivement privilégié.

Commande Compose introuvable

Trouvez Docker :

command -v docker

Utilisez le chemin complet dans l’unité :

ExecStart=/usr/bin/docker compose up -d --remove-orphans

Si le plugin Compose est manquant :

docker compose version

Installez-le en utilisant votre source de package Docker.

Variables d’environnement manquantes

Vérifiez la configuration Compose telle que systemd la verrait :

cd /opt/myapp
docker compose config

Si systemd a besoin de variables d’environnement supplémentaires, utilisez :

EnvironmentFile=-/opt/myapp/.env.systemd

Si Compose a besoin de variables pour la substitution, utilisez :

/opt/myapp/.env

Ces éléments sont liés, mais ne sont pas identiques.

Les conteneurs ne démarrent pas après le redémarrage

Vérifiez si le service systemd est activé :

systemctl is-enabled myapp.service

Activez-le :

sudo systemctl enable myapp.service

Vérifiez Docker :

systemctl is-enabled docker
systemctl status docker

Vérifiez les journaux de démarrage :

journalctl -u myapp.service -b --no-pager

L’application démarre avant que la base de données ne soit prête

Ajoutez un contrôle de santé de base de données et depends_on avec service_healthy.

Corrigez également l’application. Elle devrait réessayer les connexions à la base de données. L’ordre de démarrage de l’infrastructure est utile, mais la logique de tentative de l’application est meilleure.

Disque rempli avec les journaux Docker

Vérifiez l’utilisation du disque Docker :

docker system df

Vérifiez les journaux de conteneurs volumineux :

sudo du -h /var/lib/docker/containers | sort -h | tail

Configurez la rotation des journaux Docker dans /etc/docker/daemon.json.

Ensuite, recréez les conteneurs.

Erreurs courantes

Erreur 1 : Exécuter docker compose up dans rc.local

Exécuter docker compose up depuis rc.local ou un script de connexion fonctionne jusqu’à ce que cela ne fonctionne plus — utilisez une unité systemd appropriée à la place.

Erreur 2 : Utiliser Restart=always dans systemd et restart: always dans Compose

Généralement, vous n’avez besoin que des politiques de redémarrage des conteneurs dans Compose. Évitez que deux superviseurs ne se battent.

Erreur 3 : Oublier –remove-orphans

Les renommages et suppressions de services peuvent laisser d’anciens conteneurs derrière. Utilisez :

docker compose up -d --remove-orphans

Erreur 4 : Utiliser docker compose restart après des modifications de configuration

restart redémarre les conteneurs. Il n’applique pas toutes les modifications de configuration.

Utilisez :

docker compose up -d

Erreur 5 : Exécuter down -v sans réfléchir

Cela peut supprimer des volumes. Pour les services avec état, cela peut signifier supprimer des données.

Erreur 6 : Pas de sauvegarde avant Pull

De nouvelles images peuvent casser. Les bases de données peuvent migrer. Les tags peuvent bouger. Sauvegardez d’abord.

Erreur 7 : Publier chaque port

Ne publiez que ce que l’hôte a besoin d’exposer. Le trafic interne service-à-service peut rester sur le réseau Compose.

Modèle final recommandé

Pour la plupart des services Linux sur un seul hôte, utilisez ce modèle :

Fichier Compose :

services:
  app:
    image: example/app:stable
    restart: unless-stopped
    ports:
      - "8080:8080"
    env_file:
      - .env

Unité systemd :

[Unit]
Description=MyApp Docker Compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStartPre=/usr/bin/docker compose config --quiet
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecReload=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Activez-le :

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service

Exploitez-le :

sudo systemctl status myapp.service
sudo systemctl restart myapp.service
journalctl -u myapp.service -f
cd /opt/myapp && docker compose logs -f

Ce modèle n’est pas fancy, et c’est le but. Docker Compose est excellent pour les petits systèmes compréhensibles, systemd est excellent pour démarrer et arrêter les services de l’hôte, et ensemble, ils vous offrent un modèle de déploiement sur un seul serveur fiable sans prétendre que chaque projet a besoin d’un cluster. Pour les commandes au niveau des conteneurs en dehors de Compose — images, volumes, réseaux et nettoyage — consultez la Fiche de référence Docker.

S'abonner

Recevez de nouveaux articles sur les systèmes, l'infrastructure et l'ingénierie IA.