Pour déployer avec Docker Compose sur un VPS, installez Docker Engine et le plugin Compose, placez un fichier compose.yaml et un fichier .env dans un répertoire de projet, lancez docker compose up -d, puis placez un reverse proxy comme Caddy devant pour le HTTPS. Les mises à jour se résument ensuite à docker compose pull && docker compose up -d.
Ce guide construit une stack modeste mais réaliste : une application web, une base de données Postgres et Caddy pour le HTTPS automatique, sur Ubuntu 24.04 ou Debian 12.
1. Préparer le serveur
Partez d'un serveur sur lequel les premières étapes ont été faites : mises à jour, utilisateur sudo, clés SSH et pare-feu. Pour cette stack, 2 GB de mémoire constituent un minimum raisonnable.
Faites pointer un enregistrement DNS vers le serveur avant de commencer, pour que le HTTPS fonctionne du premier coup :
app.example.com. A 203.0.113.10 ; adresse d'exemple
app.example.com. AAAA 2001:db8::10 ; adresse d'exemple
2. Installer Docker Engine et Compose
Utilisez le dépôt officiel de Docker ; les paquets des distributions sont souvent plus anciens.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
. /etc/os-release
sudo curl -fsSL "https://download.docker.com/linux/$ID/gpg" -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/$ID $VERSION_CODENAME stable" | \
sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
$ID vaut ubuntu ou debian : les mêmes lignes fonctionnent donc sur les deux. Vérifiez :
sudo docker run --rm hello-world
docker compose version
Autorisez votre utilisateur à lancer Docker sans sudo (déconnectez-vous puis reconnectez-vous ensuite) :
sudo usermod -aG docker $USER
Appartenir au groupe docker équivaut de fait à un accès root. N'y ajoutez que des utilisateurs de confiance.
Raccourci : l'application en un clic Docker de Wolkra installe Docker CE avec Compose pour vous.
3. Organiser le projet
mkdir -p ~/apps/myapp && cd ~/apps/myapp
Une organisation claire rend le serveur facile à comprendre, même des mois plus tard :
~/apps/myapp/
├── compose.yaml
├── .env # secrets, hors de git
└── Caddyfile
4. Écrire compose.yaml
services:
app:
image: ghcr.io/example/myapp:1.4.2
restart: unless-stopped
env_file: .env
environment:
DATABASE_URL: postgres://myapp:${POSTGRES_PASSWORD}@db:5432/myapp
depends_on:
db:
condition: service_healthy
expose:
- "3000"
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: myapp
POSTGRES_DB: myapp
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myapp"]
interval: 10s
retries: 5
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
volumes:
db-data:
caddy-data:
caddy-config:
Points à retenir :
- Figez les versions des images (
1.4.2,16) pour qu'un redémarrage ne télécharge jamais une mise à jour majeure inattendue. exposeet nonportspour l'application et la base de données. Elles ne sont joignables qu'à l'intérieur du réseau Compose. Seul Caddy publie des ports.- Les volumes nommés conservent les données lorsque les conteneurs sont recréés.
depends_onavec un health check attend que Postgres accepte les connexions.
Remplacez ghcr.io/example/myapp par votre propre image et votre propre port.
5. Stocker les secrets dans .env
cat > .env <<EOF
POSTGRES_PASSWORD=$(openssl rand -hex 24)
APP_SECRET=$(openssl rand -hex 32)
EOF
chmod 600 .env
Compose lit le fichier .env du répertoire du projet pour la substitution des ${...}, et env_file le transmet à l'application. Ajoutez .env à .gitignore si le répertoire est un dépôt git.
6. Ajouter le HTTPS avec Caddy
Créez le fichier Caddyfile :
app.example.com {
encode zstd gzip
reverse_proxy app:3000
}
Caddy obtient et renouvelle automatiquement un certificat dès que le DNS pointe vers le serveur et que les ports 80 et 443 sont ouverts. Pour plus d'options, consultez le guide du reverse proxy Caddy.
7. Démarrer la stack
docker compose up -d
docker compose ps
docker compose logs -f caddy
Lorsque Caddy affiche certificate obtained successfully dans ses journaux, ouvrez https://app.example.com.
8. Attention au pare-feu
Docker insère des règles iptables qui s'appliquent avant UFW. Un port publié avec ports: est joignable depuis Internet, même si UFW indique qu'il est bloqué. Trois façons de rester en sécurité :
- Ne publier que le reverse proxy (comme ci-dessus).
- Lier tout le reste à localhost :
"127.0.0.1:5432:5432". - Filtrer en amont du serveur avec un pare-feu cloud, que Docker ne peut pas contourner. Autorisez 80 et 443 depuis partout, et 22 uniquement depuis votre adresse.
9. Mettre à jour sans stress
Modifiez le tag de l'image dans compose.yaml, puis :
docker compose pull
docker compose up -d
docker image prune -f
Compose ne recrée que les services dont l'image ou la configuration a changé. Pour revenir en arrière, remettez l'ancien tag et relancez up -d. Prenez un instantané avant les grosses mises à jour, comme une nouvelle version majeure de Postgres, qui exige un dump puis une restauration.
10. Sauvegarder les données
Les volumes se trouvent sous /var/lib/docker/volumes. Pour une base de données, un dump logique est plus sûr qu'une copie des fichiers :
docker compose exec -T db pg_dump -U myapp myapp | gzip > ~/backups/myapp-$(date +%F).sql.gz
Lancez cette commande chaque nuit via cron et copiez les fichiers hors du serveur. Les sauvegardes au niveau du serveur aident aussi : sur Wolkra, les sauvegardes quotidiennes coûtent 20 % du prix du serveur et conservent 7 points de restauration. Le guide de stratégie de sauvegarde rassemble le tout.
11. Journaux et limites
Par défaut, les journaux Docker grossissent sans limite. Plafonnez-les globalement dans /etc/docker/daemon.json :
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}
Puis lancez sudo systemctl restart docker. Vous pouvez aussi limiter la mémoire par service avec mem_limit: 512m, pour qu'un conteneur ne puisse pas priver les autres de ressources.
12. Démarrer au boot
restart: unless-stopped, combiné au service Docker activé, garantit que la stack revient après un redémarrage. Testez-le une fois :
sudo reboot
# reconnectez-vous
docker compose -f ~/apps/myapp/compose.yaml ps
13. Déployer depuis la CI
Une fois la stack en marche, les déploiements peuvent être automatisés. Un job de CI construit et pousse une image avec un nouveau tag, puis se connecte au serveur en SSH et exécute :
cd ~/apps/myapp
sed -i 's|myapp:.*|myapp:1.4.3|' compose.yaml
docker compose pull app
docker compose up -d app
Donnez au job de CI sa propre clé SSH, limitée à ce serveur, comme décrit dans le guide des clés SSH.
Quand dépasser le serveur unique
Compose sur un seul serveur permet d'aller loin. Quand il vous faut davantage, les étapes suivantes habituelles sont :
- déplacer la base de données sur son propre serveur dans un réseau privé ;
- faire tourner deux serveurs d'application derrière un répartiteur de charge ;
- conserver les données sur un volume de stockage en bloc que vous pouvez déplacer d'un serveur à l'autre.
Pour gérer de nombreuses applications via une interface web, l'application en un clic Coolify repose sur la même base Docker. D'autres idées sur hébergement Docker.
Questions fréquentes
Docker Compose est-il suffisant pour la production ?
Pour un seul serveur, oui. De nombreuses applications petites et moyennes tournent sous Compose pendant des années. Ne passez à un orchestrateur que lorsque vous avez besoin de plusieurs hôtes, d'une bascule automatique ou de mises à jour progressives sur plusieurs machines.
Quelle taille de VPS faut-il pour Docker Compose ?
Additionnez ce qu'utilise chaque conteneur. Une application web avec Postgres et Redis est à l'aise avec 2 à 4 GB de mémoire ; gardez de la marge pour la construction des images si vous les buildez sur le serveur.
Pourquoi UFW ne bloque-t-il pas les ports de mes conteneurs ?
Docker ajoute ses propres règles iptables, qui s'appliquent avant UFW. Publiez les ports sur 127.0.0.1 et placez un reverse proxy devant, ou filtrez avec un pare-feu cloud.
Dois-je construire les images sur le serveur ?
Cela fonctionne pour les petits projets. Pour des déploiements reproductibles, construisez dans la CI, poussez vers un registre et contentez-vous de faire un pull sur le serveur.
Puis-je éviter d'installer Docker moi-même ?
Oui. Sur Wolkra, choisissez l'application en un clic Docker CE lors de la création d'un serveur : elle est livrée avec Docker et le plugin Compose installés.