
Héberger plusieurs sites dans des containers Docker
L’objectif est d’auto héberger plusieurs sites web sur un VPS, chacun dans leur container Docker, avec un Nginx Reverse Proxy lui même dans un Docker.
Nous allons voir comment héberger plusieurs sites web avec Docker. Notre architecture finale ressemblera à ça :

Prérequis
- Un VPS auquel on peut accéder en SSH avec les droits root.
- Un nom de domaine sur lequel on peut modifier le DNS.
Outils recommandés
lazydocker
: un outil qui permet de monitorer Docker (les images, les networks, les containers en cours, …) et de faire des actions dessus de manière plus intuitive.
https://github.com/jesseduffield/lazydockermidnightcommander
: un outil qui permet facilement de gérer ses fichier et qui a la particularité de pouvoir se connecter en SSH à un autre serveur. Pratique pour migrer des serveurs.
https://doc.ubuntu-fr.org/midnight_commander
Etape 1 : Installer Docker / Docker compose
Docker sera la couche de virtualisation qui permettra de lancer des containers.
Pour l’installer :
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
On créer un réseau spécifique qui servira à regrouper plusieurs images dans le même sous-réseau.
docker network create --subnet=172.18.0.0/16 webserver_network
Etapes 2 : Nging-reverse-proxy
Modification du DNS
Chez notre registrar, on fait met en place une entrée DNS de type A
pour un domaine ou un sous-domaine qui pointe vers l’IP de notre serveur.
Par exemple cheatsheet.fr
.
Image Docker Nginx
Notre reverse proxy Nginx aura besoin d’enregistrer des informations. Pour éviter qu’elles soient perdues entre chaque redémarrage, on va faire en sorte qu’elles soient stockées sur notre machine.
Dans un répertoire nginx-proxy-manager
, on va créer un répertoire data
et un répertoire letsencrypt
qui seront liés au volume de l’image Docker.
Afin de lancer notre image Docker avec l’utilisateur www-data
du groupe www-data
, on va créer un fichier .env
qui contiendra l’identifiant de l’utilisateur et du groupe qu’on souhaitera utiliser dans la configuration :
PUID=33
PGID=33
Généralement, l’utilisateur www-data
est l’identifiant 33
. Pour s’en assurer, on peut taper la commande :
less /etc/passwd
On crée un fichier docker-compose.yml
avec le contenu suivant :
version: '3.8'
services:
app:
container_name: nginx-proxy-manager
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80' # Public HTTP Port
- '443:443' # Public HTTPS Port
- '81:81' # Admin Web Port
environment:
PUID: ${PUID}
PGID: ${PGID}
# Uncomment this if you want to change the location of
# the SQLite DB file within the container
# DB_SQLITE_FILE: "/data/database.sqlite"
# Uncomment this if IPv6 is not enabled on your host
# DISABLE_IPV6: 'true'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
default:
name: webserver_network
external: true
Explications :
container_name: nginx-proxy-manager
: le nom qu’on souhaite donner au conteneur, pour le retrouver plus facilement.
image: 'jc21/nginx-proxy-manager:latest'
: l’image Docker sur laquelle on va se baser. https://github.com/NginxProxyManager/nginx-proxy-manager
restart: unless-stopped
: si l’image s’arrête, il faut qu’elle redémarre automatiquement, sauf si on l’a arrêtée volontairement.
ports:
: on fait un mapping des ports 80 (http), 443 (https) et 81 (interface web de gestion de Nginx).
environment:
on déclare les 2 variables d’environnement qui servent à déterminer l’utilisateur et le groupe.
DB_SQLITE_FILE: "/data/database.sqlite"
: nom du fichier dans lequel on enregistre la base de données qui stocke la configuration de Nginx.
DISABLE_IPV6: 'true'
: à définir lorsque notre serveur ne gère pas l’IPv6.
volumes:
: on fait pointer le répertoire local ./data
vers le répertoire /data
du container, et le répertoire local ./letsencrypt
vers le répertoire /etc/letsencrypt
du container.
networks:
: permet de dire que par défaut on utilise le réseau webserver_network
qui est un réseau externe (celui créé plus haut).
On devrait avoir la structure suivante :
Workspaces/
└── nginx-proxy-manager/
├── .env
├── data/
├── docker-compose.yml
├── letsencrypt/
Démarrage du serveur
On démarre le serveur avec la commande :
docker compose up -d --build
Configuration du reverse-proxy
On peut accéder à l’interface de Nginx par son IP et le port 81.
Etape 3 : Nouveau site
Structure et container
- On crée un répertoire
Workspaces/mon_site
qui aura la structure suivante :
Workspaces/
└── mon_site/
├── .env
├── docker-compose.yml
├── Dockerfile
└── src/
└── index.php
- Le fichier
.env
contiendra toutes les variables d’environnements dont on aura besoin. Par exemple :
PORT=8080
DB_LOGIN=Mon identifiant
PORT
sera le port sur lequel l’instance Docker tournera.DB_LOGIN
sera une variable d’environnement sur le serveur.
- Le fichier
Dockerfile
aura le contenu suivant :
FROM php:8.2-apache as base
afin de lui dire que l’image de base est un container qui contient PHP 8.2 et Apache.
- Le fichier
docker-compose.yml
aura le contenu suivant :
version: "3.9"
services:
php:
container_name: mon_site_php
image: php
restart: always
#depends_on:
# - db
build:
context: .
dockerfile: Dockerfile
target: base
volumes:
- ./src:/var/www/html
environment:
- DB_LOGIN=${DB_LOGIN}
ports:
- "127.0.0.1:${PORT}:80"
networks:
- webserver_network
- internal_network
networks:
webserver_network:
name: webserver_network
external: true
internal_network:
name: mysite_network
driver: bridge
L’élément volume
permet de faire le mapping sur le répertoire src
local vers /var/www/html
distant.
L’élément environment
permet de créer des variables d’environnements sur l’environnement distant.
L’élément ports
permet de dire que le port local pointe sur le port 80
distant.
L’élément networks
permet de lier l’image à un réseau interne et un réseau externe.
Ajouter une base de données
Pour utiliser une base de données de type MySQL, on peut rajouter dans docker-compose.yml
:
db:
container_name: database_db
image: mysql:8.3
restart: unless-stopped
user: "${UID}:${GID}"
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
#healthcheck:
# test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
# interval: 5s
# timeout: 10s
# retries: 10
# start_period: 5s
volumes:
- ./db_data:/var/lib/mysql
networks:
- internal_network
Pour utiliser une base de donnée de type PosgreSQL, on peut rajouter dans docker-compose.yml
:
db:
container_name: mydatabase_db
image: postgres
restart: unless-stopped
# set shared memory limit when using docker-compose
shm_size: 128mb
user: "${UID}:${GID}"
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
#healthcheck:
# test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}" ]
# interval: 5s
# timeout: 10s
# retries: 10
# start_period: 5s
volumes:
- ./db_data:/var/lib/postgresql/data
networks:
- internal_network
Pour utiliser une base de données de type MariaDB, on peut rajouter dans docker-compose.yml
:
db:
container_name: database_db
image: mariadb:latest
restart: unless-stopped
environment:
PUID: ${UID}
PGID: ${GID}
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
MARIADB_DATABASE: ${MARIADB_DATABASE}
MARIADB_USER: ${MARIADB_USER}
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
volumes:
- ./db_data:/var/lib/mysql
networks:
- internal_network
Ajouter une interface d’administration pour la base de données
Si on veut un PHPMyAdmin :
phpmyadmin:
container_name: famille-parfaite_myadmin
depends_on:
- db
image: phpmyadmin:latest
ports:
- 8080:80
restart: unless-stopped
environment:
PMA_HOST: db
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
UPLOAD_LIMIT: 1024M
MAX_EXECUTION_TIME: 10800
networks:
- internal_network
Si on veut un Adminer :
adminer:
image: adminer
restart: unless-stopped
environment:
ADMINER_DEFAULT_SERVER: db
depends_on:
- db
ports:
- 8080:8080
networks:
- internal_network
Contenu du site
- Le fichier
index.php
contient par exemple :
<?php
echo "Hello " . getenv("DB_LOGIN") . "!";
?>
- En se mettant dans le répertoire
mon_site
on démarre le container.
sudo docker compose up --build
Etape 4 : Redirections Nginx-reverse-proxy
Maintenant que les sites sont opérationnels, il faut faire en sorte que le nom de domaine redirige sur la bonne instance Docker.
Pour cela, on s’authentifie sur l’instance Nginx-reverse-proxy en y accédant par son IP et le port 81.
Par exemple : http://123.45.67.89:81
A la première connexion, il faut renseigner une adresse email et un mot de passe fort.
Ensuite, il faut ajouter un « host ». Comme on ne connait pas forcément l’IP de notre container Docker dans le réseau « websote_network », on peut utiliser à la place le « hostname » que l’on a défini dans le fichier docker-compose.yml
.

Il est possible de générer un certificat SSL avec Let’sEncrypt dans l’onglet « SSL ».
Fichiers d’exemple
Voici les archives complètes de Nginx et des exemples de sites :