← Blog
Un cadre en céramique verte contenant un panneau crème et une petite plaque corail sur fond lavande.
Publié le
2026-11-02

Docker Compose Watch : synchroniser, redémarrer et reconstruire

Docker Compose Watch relie les fichiers enregistrés sur votre ordinateur à un service de développement en cours d’exécution. Une règle peut copier un fichier modifié, redémarrer le conteneur après la copie ou reconstruire l’image. Le bon choix dépend de la façon dont l’application lit le fichier.

Ce guide utilise un petit exemple Python avec des règles distinctes pour le HTML, le code et le Dockerfile. La configuration et le comportement HTTP local ont passé nos contrôles. Le moteur Docker était indisponible pendant la préparation : la construction de l’image, les événements Watch et les mises à jour des conteneurs restent non testés.

Choisir sync, sync+restart ou rebuild

La spécification Compose Develop définit les actions Watch. L’exemple en utilise trois :

ActionModification de l’exempleRésultat attendu
syncModifier site/index.htmlCopier le fichier, lu par le serveur à la requête suivante
sync+restartModifier server.pyCopier le code, puis redémarrer le conteneur pour le charger
rebuildModifier DockerfileReconstruire l’image et recréer le service

Watch est disponible depuis Compose 2.22.0 ; sync+restart nécessite au moins la version 2.23.0. Utilisez une version récente prise en charge pour l’exemple complet, qui contient aussi initial_sync. Nos contrôles de configuration ont utilisé v5.1.4. Une validation réussie ne vérifie pas la surveillance des fichiers en exécution.

Créer un petit serveur de développement

Dans un nouveau répertoire, créez server.py, Dockerfile, .dockerignore, compose.yaml et un dossier site contenant index.html. Utilisez des conteneurs Linux. Aucun paquet Python externe n’est nécessaire.

Enregistrez ceci sous server.py :

from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path

SITE = Path(__file__).resolve().parent / "site"


class Handler(SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, directory=str(SITE), **kwargs)

    def end_headers(self):
        self.send_header("X-Dev-Example", "first")
        self.send_header("Cache-Control", "no-store")
        super().end_headers()


if __name__ == "__main__":
    ThreadingHTTPServer(("0.0.0.0", 8000), Handler).serve_forever()

Enregistrez ceci sous site/index.html :

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Compose Watch demo</title>
<h1>First edit</h1>
</html>

Le gestionnaire lit les fichiers dans site, ajoute un en-tête de diagnostic et demande au client de ne pas mettre la réponse en cache. La documentation de http.server déconseille son utilisation en production. Gardez cet exemple sur votre machine de développement.

Lors du test local, le serveur a renvoyé la page et les deux en-têtes, répondu 404 pour un fichier absent et servi un fichier HTML modifié sans redémarrer. Ces observations concernent le programme Python sur l’hôte, pas la synchronisation Docker.

Construire une image avec des cibles accessibles en écriture

Enregistrez ceci sous Dockerfile :

FROM python:3.13-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
RUN mkdir -p /app/site && chown -R 1000:1000 /app \
    && command -v stat && command -v mkdir && command -v rmdir
COPY --chown=1000:1000 server.py ./
COPY --chown=1000:1000 site/ ./site/
USER 1000:1000
CMD ["python", "server.py"]

L’image s’exécute avec l’UID et le GID 1000. La préparation du répertoire et COPY --chown donnent à cette identité la propriété des fichiers. La construction vérifie aussi la présence de stat, mkdir et rmdir, prérequis indiqués par Docker pour Watch. Cette construction n’a pas été exécutée ici.

Watch doit pouvoir écrire dans ses cibles. Un répertoire applicatif en lecture seule serait incompatible avec ces règles. Corrigez les droits de l’utilisateur prévu plutôt que de passer le service en root pour masquer le problème. Pour un environnement reproductible, utilisez une empreinte d’image testée ; le tag lisible de l’exemple peut changer.

Enregistrez ces exclusions dans .dockerignore :

.git
.venv
**/__pycache__
**/*.pyc
**/*.tmp
.env
.env.*
secrets/

Les règles du contexte de build excluent les fichiers correspondants des données envoyées au constructeur. Le Dockerfile ne copie que le serveur et le site. Gardez les identifiants secrets hors de l’exemple ; ces règles ne retirent pas des secrets déjà présents dans une ancienne image.

Définir les règles Watch

Enregistrez ceci sous compose.yaml :

services:
  web:
    build: .
    ports:
      - "127.0.0.1:8080:8000"
    develop:
      watch:
        - action: sync
          path: ./site
          target: /app/site
          initial_sync: true
          ignore:
            - "**/*.tmp"
        - action: sync+restart
          path: ./server.py
          target: /app/server.py
        - action: rebuild
          path: ./Dockerfile

Les trois chemins ont des rôles différents : chaque modification doit déclencher une seule action prévue. La règle HTML relie le dossier local site à /app/site. initial_sync vérifie ce contenu lorsqu’une nouvelle session Watch rejoint des conteneurs existants. Cela n’ajoute pas de rafraîchissement automatique du navigateur.

Le guide Watch de Docker précise que les sources sont relatives au répertoire du projet, tandis que les exclusions propres à une règle sont relatives au chemin surveillé. Ici, les fichiers temporaires sont exclus du contexte de build et de la règle HTML. Une modification ignorée ne déclenchera pas la mise à jour attendue.

Le port publié est 127.0.0.1:8080 sur l’hôte, relié au port 8000 du conteneur. Le serveur Python écoute sur toutes les interfaces du conteneur pour recevoir ce trafic. Notre guide des réseaux Compose explique les ports hôtes et les adresses des conteneurs.

Démarrer Watch et essayer chaque modification

Depuis le répertoire de l’exemple, lancez :

docker compose version
docker compose -p watch-demo config --quiet
docker compose -p watch-demo up --build --watch

config --quiet vérifie le modèle Compose. up --build --watch construit et démarre le service, puis surveille les modifications. Gardez ce terminal ouvert et visitez http://127.0.0.1:8080/ sur la même machine.

Effectuez une modification à la fois et notez le résultat réel :

  1. HTML : remplacez « First edit » par « Second edit » dans site/index.html, enregistrez et actualisez la page. Le nouveau titre doit apparaître après synchronisation, sans reconstruction de l’image.
  2. Python : dans server.py, remplacez la valeur first de l’en-tête X-Dev-Example par second. Après synchronisation et redémarrage, examinez une nouvelle réponse dans les outils réseau du navigateur. La valeur attendue est second.
  3. Image : ajoutez LABEL dev.example="second" au Dockerfile et enregistrez. Watch doit signaler une reconstruction et recréer le service. Vérifiez que la page est de nouveau accessible après l’opération.

Il s’agit de vérifications à effectuer, pas de résultats observés dans des conteneurs. Une erreur de syntaxe Python peut empêcher le service de démarrer après une modification ; consultez les journaux avant de changer les règles.

La commande distincte docker compose watch sépare sa sortie des journaux applicatifs. Son option --no-up saute la construction et le démarrage initiaux : utilisez-la uniquement si les services prévus fonctionnent déjà. La référence de la commande décrit aussi le nettoyage des images lors des reconstructions.

Diagnostiquer une modification invisible

Dans un autre terminal, depuis le même répertoire, lancez :

docker compose -p watch-demo ps
docker compose -p watch-demo logs --tail 30 web
docker compose -p watch-demo exec web id
docker compose -p watch-demo exec web sh -c "test -w /app/site && test -w /app/server.py"

La dernière commande réussit uniquement si l’utilisateur du conteneur peut écrire dans les deux cibles. Elle vérifie les droits, pas le traitement d’un événement Watch. Notre guide Docker logs explique comment examiner la sortie applicative.

  • Aucun événement : vérifiez que la session fonctionne, que le fichier se trouve dans une source configurée et qu’aucune règle ne l’exclut.
  • Erreur de copie : examinez les cibles, les propriétaires et les exécutables nécessaires. L’image en cours d’exécution peut différer du Dockerfile modifié.
  • Fichier copié, comportement inchangé : vérifiez si l’application relit les fichiers, dispose d’un mécanisme de rechargement ou nécessite un redémarrage. Enregistrer un module Python ne remplace pas le code déjà chargé en mémoire.
  • Fichiers inattendus : recherchez un bind mount sur la même cible. La documentation des bind mounts explique comment un montage masque les fichiers déjà présents dans l’image.

Choisissez un mécanisme clair pour chaque chemin de développement. Un bind mount peut convenir à l’accès direct aux fichiers de l’hôte ; Watch ajoute des actions explicites. Aucun des deux ne sauvegarde vos données applicatives. Notre guide du stockage Docker traite séparément leur persistance.

Terminer la session

Arrêtez la session attachée up --watch avec Ctrl+C. Lorsque vous avez terminé avec ce projet de test, lancez :

docker compose -p watch-demo down

compose down supprime les conteneurs des services et le réseau du projet. Les fichiers sources sur l’hôte restent votre copie de travail. En production, construisez et testez une image de version définie plutôt que de dépendre d’une session Watch de développement.

Votre prochaine charge de travail sur Hivenet.

Choisissez une charge de travail d’IA, de calcul ou de stockage à tester sur Hivenet, ou échangez avec notre équipe sur sa mise en production.