Samedi : je relance mon Supabase staging dans Coolify (exited depuis quelques jours). Click Start → failed to pull "supabase/postgres:15.8.1.060": You have reached your unauthenticated pull rate limit.
Estimé 5 min. Réel : 1h30 pour comprendre trois mécanismes cumulés et poser un mirror Docker Hub local.
Piège 1 : Coolify prune les images « unused » même à 4% disque
force_docker_cleanup: true est le défaut, cron à minuit. En théorie il n’agit qu’à docker_cleanup_threshold: 80 (% disque), or je suis à 4%. En pratique : un service exited voit ses images « unused » → le cleanup nocturne les vire. Au compose up suivant, re-pull des ~15 images Supabase.
Piège 2 : Docker Hub anonyme = 100 pulls / 6h / IP
1 image = 1 manifest + N layers : 15 images dépassent vite. Et mon KS-5 OVH partage sûrement son IP de sortie → quota réel plus bas. Un docker login (200/6h) aide, mais l’auth globale au daemon est invasive : tous les services la partagent.
Piège 3 : registry:2 doit aussi être pullé depuis Docker Hub
Premier docker pull registry:2 → rate limit, le serpent se mord la queue. Fix : docker login ponctuel le temps de puller, puis docker logout. Le mirror gère son auth via env vars.
La solution : registry:2 en pull-through cache, bindé localhost
registry-mirror:
image: registry:2
environment:
REGISTRY_PROXY_REMOTEURL: "https://registry-1.docker.io"
REGISTRY_PROXY_USERNAME: "${DOCKERHUB_USERNAME}"
REGISTRY_PROXY_PASSWORD: "${DOCKERHUB_PAT}"
volumes: ["/data/registry-mirror:/var/lib/registry"]
ports: ["127.0.0.1:5000:5000"] # bind STRICT localhost
Le bind 127.0.0.1 est clé : pas exposé, pas de TLS, pas d’UFW, le daemon y accède via localhost (HTTP toléré). Dans /etc/docker/daemon.json : "registry-mirrors": ["http://localhost:5000"], puis systemctl restart docker (~10s ; sur srv-a coolify-poststart.service gère la race condition). Cache miss = via mirror authentifié, hits suivants = local instantané. Le cleanup peut prune quand il veut, le re-pull ne touche plus Docker Hub.
Lessons learned
force_docker_cleanup: trueprune les images des services exited même à 4% disque (image != volume retention).- Docker Hub : 100 pulls/6h/IP anonyme, 200 authentifié ; sur IP OVH mutualisée, quota réel plus bas.
docker loginglobal = invasif : préférerregistry-mirrors, auth viaREGISTRY_PROXY_*.- Bind STRICT
127.0.0.1:5000: pas de TLS, HTTP sur localhost, cleanup safe. - 1 mirror = 1 upstream ; pour ghcr.io/quay.io, autre registry (ou Harbor).
docker logoutaprès le pull deregistry:2: sinon credentials globaux qui survivent au reboot.