Dev Containers crée un environnement de développement identique pour tous les membres de votre équipe, quel que soit leur système d'exploitation. Fini le "ça marche sur ma machine" : les dépendances, versions et configurations sont définies dans un fichier versionné. Ce guide couvre la configuration de base, Docker Compose, les features, et l'intégration CI/CD.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre ce que résout un Dev Container.
- Écrire un
devcontainer.json(image, features, extensions). - Ouvrir un projet dans le conteneur avec VS Code.
- Combiner avec Docker Compose pour les dépendances.
- Réutiliser l'environnement en CI et GitHub Codespaces.
Le problème que Dev Containers résout
Section intitulée « Le problème que Dev Containers résout »Sans environnement standardisé, chaque développeur a sa propre configuration :
| Problème | Conséquence |
|---|---|
| Versions différentes (Node, Python...) | Bugs impossibles à reproduire |
| Dépendances système manquantes | "Ça marche chez moi" |
| Configuration manuelle | Onboarding de plusieurs heures |
| Pollution du système local | Conflits entre projets |
Dev Containers résout ces problèmes avec Docker :
- Environnement défini en code : fichier
devcontainer.jsonversionné - Isolation complète : chaque projet a son container
- Portabilité : fonctionne sur Linux, macOS, Windows
- Onboarding instantané : clone le repo, ouvre VS Code, c'est prêt
Fonctionnement
Section intitulée « Fonctionnement »Quand vous ouvrez un projet avec Dev Containers :
- VS Code lit
.devcontainer/devcontainer.json - Docker crée le container avec l'image spécifiée
- VS Code Server s'installe dans le container
- Extensions et dépendances s'installent automatiquement
- Votre code est monté dans le container (volume)
Vous travaillez dans VS Code normalement, mais tout s'exécute dans le container : terminal, debug, LSP, extensions.
Prérequis
Section intitulée « Prérequis »-
Téléchargez depuis code.visualstudio.com.
-
Docker
Le moteur de containers qui exécute l'environnement. Installez Docker Desktop ou Docker Engine.
Fenêtre de terminal docker --version# Docker version 24.0.7 ou supérieur -
Extension Dev Containers
Installez l'extension Dev Containers (anciennement "Remote - Containers") :
Fenêtre de terminal code --install-extension ms-vscode-remote.remote-containersOu recherchez "Dev Containers" dans le Marketplace (ID :
ms-vscode-remote.remote-containers).
Configuration de base
Section intitulée « Configuration de base »Un Dev Container tient dans un seul fichier versionné, devcontainer.json, placé dans un dossier .devcontainer/ à la racine du dépôt. VS Code le détecte automatiquement à l'ouverture du projet et propose de rouvrir la fenêtre dans le conteneur. Le minimum viable se réduit à trois informations : l'image de base, la commande d'installation des dépendances et les extensions à embarquer.
Structure des fichiers
Section intitulée « Structure des fichiers »À la racine de votre projet, créez le dossier .devcontainer/ :
mon-projet/├── .devcontainer/│ └── devcontainer.json├── src/├── package.json└── ...Exemple minimal (Node.js / Astro)
Section intitulée « Exemple minimal (Node.js / Astro) »Notez le tag d'image précis (node:20-alpine3.19) plutôt qu'un node:latest : sans lui, deux développeurs qui construisent le conteneur à deux mois d'écart n'obtiennent pas la même version de Node, et la reproductibilité promise s'évapore.
{ "name": "Astro Dev Container", "image": "node:20-alpine3.19", "postCreateCommand": "npm ci", "customizations": { "vscode": { "extensions": [ "astro-build.astro-vscode", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode" ] } }, "forwardPorts": [4321]}Explication des champs :
| Champ | Description |
|---|---|
name | Nom affiché dans VS Code |
image | Image Docker à utiliser |
postCreateCommand | Commande exécutée après création (installation) |
customizations.vscode.extensions | Extensions à installer automatiquement |
forwardPorts | Ports exposés sur localhost |
Démarrer le container
Section intitulée « Démarrer le container »La première ouverture est la plus longue : VS Code télécharge l'image, construit le conteneur, y installe le serveur VS Code puis les extensions. Les suivantes réutilisent le conteneur existant et démarrent en quelques secondes.
- Ouvrez le projet dans VS Code
- Appuyez sur
F1→ Dev Containers: Reopen in Container - VS Code télécharge l'image, crée le container, installe les extensions
- Votre terminal s'exécute maintenant dans le container
Vérification :
# Dans le terminal VS Code (container)node --version# v20.x.x
cat /etc/os-release | grep PRETTY_NAME# PRETTY_NAME="Alpine Linux v3.19"Propriétés du devcontainer.json
Section intitulée « Propriétés du devcontainer.json »Au-delà de l'image et des extensions, la spécification expose des propriétés qui décident quand les commandes s'exécutent, sous quel utilisateur et avec quoi monté. Ce sont elles qui font la différence entre un conteneur qui dépanne et un conteneur que l'équipe garde. Les quatre familles ci-dessous couvrent la quasi-totalité des besoins réels.
Lifecycle commands
Section intitulée « Lifecycle commands »La spec définit plusieurs hooks exécutés à différents moments :
| Hook | Moment | Usage typique |
|---|---|---|
postCreateCommand | Après création du container | npm ci, installation dépendances |
postStartCommand | À chaque démarrage | Lancer un serveur de dev |
postAttachCommand | À chaque connexion VS Code | Messages de bienvenue |
initializeCommand | Avant création (sur l'hôte) | Clone de submodules |
Exemple avec séparation des responsabilités :
{ "postCreateCommand": "npm ci", "postStartCommand": "npm run dev -- --host"}Variables d'environnement
Section intitulée « Variables d'environnement »containerEnv inscrit les variables dans le conteneur lui-même, donc dans le fichier versionné : réservez-le aux valeurs de développement inoffensives. Un vrai secret n'a rien à faire là, puisqu'il partirait dans le dépôt Git avec le reste de la configuration.
{ "containerEnv": { "NODE_ENV": "development", "DATABASE_URL": "postgres://user:pass@db:5432/mydb" }}Utilisateur non-root
Section intitulée « Utilisateur non-root »Par défaut, le container peut s'exécuter en root. Pour un comportement plus proche de la production :
{ "remoteUser": "node"}L'utilisateur node existe dans les images officielles Node.js. Pour d'autres images, utilisez containerUser ou créez l'utilisateur dans un Dockerfile.
Monter des fichiers/dossiers
Section intitulée « Monter des fichiers/dossiers »Le montage ci-dessous est en lecture seule (readonly), ce qui empêche un processus du conteneur de modifier ou d'écraser vos clés privées. La variable ${localEnv:HOME} est résolue sur la machine hôte, pas dans le conteneur.
{ "mounts": [ "source=${localEnv:HOME}/.ssh,target=/home/node/.ssh,type=bind,readonly" ]}Cela monte vos clés SSH dans le container pour les opérations Git.
Features : ajouter des outils sans Dockerfile
Section intitulée « Features : ajouter des outils sans Dockerfile »Les features sont des modules réutilisables qui installent des outils dans votre container. Plus propre que de tout faire dans un Dockerfile.
{ "image": "mcr.microsoft.com/devcontainers/base:ubuntu", "features": { "ghcr.io/devcontainers/features/node:1": { "version": "20" }, "ghcr.io/devcontainers/features/docker-in-docker:2": {}, "ghcr.io/devcontainers/features/git:1": {} }}Features populaires :
| Feature | Description |
|---|---|
node | Node.js + npm |
python | Python + pip |
docker-in-docker | Docker dans le container |
kubectl-helm-minikube | Outils Kubernetes |
terraform | Terraform CLI |
aws-cli | AWS CLI |
Consultez le catalogue de features pour la liste complète.
Docker Compose : multi-services
Section intitulée « Docker Compose : multi-services »Quand votre projet nécessite plusieurs services (base de données, cache, API), utilisez Docker Compose.
docker-compose.yml
Section intitulée « docker-compose.yml »Le service de développement doit rester vivant sans rien faire : c'est le rôle du command: sleep infinity. Sans lui, le conteneur web s'arrêterait immédiatement et VS Code n'aurait plus rien à quoi se rattacher. Le volume nommé postgres-data conserve la base entre deux reconstructions.
services: web: image: node:20-alpine3.19@sha256:1cc9088b0fbcb2009a8fc2cb57916cd129cd5e32b3c75fb12bb24bac76917a96 ports: - "4321:4321" volumes: - ./:/workspace:cached command: sleep infinity
db: image: postgres:16@sha256:33f923b05f64ca54ac4401c01126a6b92afe839a0aa0a52bc5aeb5cc958e5f20 restart: unless-stopped volumes: - postgres-data:/var/lib/postgresql/data environment: POSTGRES_USER: myapp_user POSTGRES_PASSWORD: myapp_password POSTGRES_DB: myapp_db ports: - "5432:5432"
volumes: postgres-data:devcontainer.json pour Compose
Section intitulée « devcontainer.json pour Compose »En mode Compose, image disparaît au profit de dockerComposeFile et de service : VS Code délègue la création des conteneurs à Compose et se rattache au seul service désigné. Le workspaceFolder doit correspondre au chemin monté dans ce service, ici /workspace.
{ "name": "Mon App + Postgres", "dockerComposeFile": "../docker-compose.yml", "service": "web", "workspaceFolder": "/workspace", "postCreateCommand": "npm ci", "forwardPorts": [4321, 5432], "customizations": { "vscode": { "extensions": [ "astro-build.astro-vscode", "ms-azuretools.vscode-docker" ] } }}Champs spécifiques à Compose :
| Champ | Description |
|---|---|
dockerComposeFile | Chemin vers le docker-compose.yml |
service | Service principal (où VS Code se connecte) |
workspaceFolder | Dossier de travail dans le container |
Accéder à la base de données
Section intitulée « Accéder à la base de données »Depuis le service web, la base de données est accessible via le nom du service :
# Dans le terminal du containerpsql -h db -U myapp_user -d myapp_dbDepuis votre machine locale, utilisez localhost:5432 grâce à forwardPorts.
Dockerfile personnalisé
Section intitulée « Dockerfile personnalisé »Pour des besoins spécifiques, créez un Dockerfile :
.devcontainer/Dockerfile
Section intitulée « .devcontainer/Dockerfile »Ce Dockerfile crée un utilisateur non privilégié avec l'UID 1000, celui du premier compte sur la plupart des distributions Linux. La correspondance d'UID évite le problème classique des fichiers créés dans le conteneur qui apparaissent en root sur l'hôte.
FROM node:20-alpine3.19@sha256:1cc9088b0fbcb2009a8fc2cb57916cd129cd5e32b3c75fb12bb24bac76917a96
# Installer des outils supplémentairesRUN apk add --no-cache \ git \ openssh-client \ curl
# Créer un utilisateur non-rootRUN adduser -D -u 1000 developerUSER developer
WORKDIR /workspacedevcontainer.json avec Dockerfile
Section intitulée « devcontainer.json avec Dockerfile »Le champ build.context vaut ici .., soit la racine du dépôt et non le dossier .devcontainer/. C'est indispensable dès que le Dockerfile doit copier un fichier du projet, comme un package.json.
{ "name": "Custom Dev Container", "build": { "dockerfile": "Dockerfile", "context": ".." }, "remoteUser": "developer", "postCreateCommand": "npm ci", "forwardPorts": [4321]}Remote SSH + Dev Containers
Section intitulée « Remote SSH + Dev Containers »Vous pouvez combiner l'accès SSH distant avec Dev Containers : VS Code se connecte en SSH à un serveur, puis utilise Docker sur ce serveur.
-
Connectez-vous en SSH
F1→ Remote-SSH: Connect to Host → sélectionnez votre serveur -
Ouvrez le projet
Une fois connecté, ouvrez le dossier contenant
.devcontainer/ -
Ouvrez dans le container
F1→ Dev Containers: Reopen in Container
Avantages :
- Docker tourne sur le serveur (pas besoin de Docker local)
- Ressources du serveur (RAM, CPU, stockage rapide)
- Réseau du serveur (accès aux services internes)
Consultez la documentation officielle pour les configurations avancées.
Intégration CI/CD
Section intitulée « Intégration CI/CD »Le devcontainer.json peut servir de source de vérité pour votre pipeline CI.
Pattern recommandé
Section intitulée « Pattern recommandé »Le principe tient en une phrase : le Dev Container sert l'éditeur, le job CI tourne dans la même image de base. Tant que les deux partagent le tag exact, un test qui passe en local passe en CI pour les mêmes raisons, et un échec en CI se reproduit sur le poste du développeur.
GitHub Actions
Section intitulée « GitHub Actions »Le job ci-dessous s'exécute directement dans l'image grâce à la clé container. Trois réflexes de durcissement l'accompagnent : permissions: {} au niveau du workflow retire tous les droits par défaut du GITHUB_TOKEN, chaque job redemande le strict minimum, et persist-credentials: false empêche actions/checkout de laisser le jeton dans le .git/config où n'importe quelle dépendance pourrait le lire.
name: CI
on: [push, pull_request]
permissions: {}
jobs: test: runs-on: ubuntu-latest permissions: contents: read container: image: node:20-alpine3.19 # Même image que devcontainer.json
steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Install dependencies run: npm ci
- name: Run tests run: npm test
- name: Build run: npm run buildUtiliser devcontainer CLI en CI
Section intitulée « Utiliser devcontainer CLI en CI »Pour une cohérence totale, utilisez la CLI devcontainer :
permissions: {}
jobs: build: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Build dev container uses: devcontainers/ci@513af61f4de4f75d37e4438f184ba4358f0fc1ca # v0.3.1900000450 with: runCmd: npm ci && npm test && npm run buildCette approche utilise exactement la configuration de votre devcontainer.json. Les deux actions sont épinglées par SHA de commit, avec le tag en commentaire : un tag Git peut être redéplacé par son mainteneur vers un autre commit, un SHA non.
Performance
Section intitulée « Performance »La lenteur ressentie d'un Dev Container vient presque toujours du système de fichiers, pas du CPU. Chaque lecture d'un fichier monté depuis l'hôte traverse une couche de traduction sur macOS et Windows, ce qui se paie très cher sur les milliers de petits fichiers d'un node_modules. Deux réglages suffisent à récupérer l'essentiel : placer le code au bon endroit et sortir les dépendances du montage.
Placement du code
Section intitulée « Placement du code »Le tableau donne l'emplacement à privilégier selon l'hôte. Sur Windows, le point critique est de rester dans le système de fichiers WSL2 : un projet ouvert depuis /mnt/c/ traverse le pont vers NTFS à chaque accès et devient rapidement inutilisable.
| OS | Recommandation |
|---|---|
| Linux | Code sur le filesystem Linux, performances natives |
| macOS | Préférer le code dans le container (volume nommé) pour éviter les I/O lents |
| Windows | Stocker le code en WSL2 (\\wsl$\Ubuntu\...), pas sur /mnt/c/ |
Volume nommé pour les dépendances
Section intitulée « Volume nommé pour les dépendances »Évitez de remonter node_modules depuis l'hôte :
{ "mounts": [ "source=myproject-node_modules,target=/workspace/node_modules,type=volume" ]}Cela stocke node_modules dans un volume Docker plus rapide.
Dépannage
Section intitulée « Dépannage »La plupart des pannes se lisent dans le journal de création du conteneur, accessible par F1 puis Dev Containers: Show Container Log. Le tableau ci-dessous regroupe les symptômes récurrents ; deux d'entre eux, les fichiers appartenant à root et le port qui ne répond pas, proviennent d'un devcontainer.json mal structuré plutôt que d'un problème Docker.
| Symptôme | Cause probable | Solution |
|---|---|---|
| "Cannot connect to Docker" | Docker non démarré | Lancez Docker Desktop |
| Container lent à créer | Téléchargement de l'image | Normal la première fois, cache ensuite |
| Fichiers en root sur l'hôte | remoteUser non configuré | Ajouter "remoteUser": "node" |
| Extensions non installées | Mauvais format JSON | Vérifier customizations.vscode.extensions |
| Port non accessible | forwardPorts mal placé | Doit être au premier niveau, pas dans customizations |
| Erreur "image not found" | Typo dans le nom d'image | Vérifier l'orthographe (alpine pas apline) |
Reconstruire le container
Section intitulée « Reconstruire le container »Si quelque chose ne fonctionne pas après une modification :
F1 → Dev Containers: Rebuild Container
Pour repartir de zéro (sans cache Docker) :
F1 → Dev Containers: Rebuild Container Without Cache
FAQ : Dev Containers
Section intitulée « FAQ : Dev Containers »devcontainer.json :{
"image": "mcr.microsoft.com/devcontainers/python:3.12",
"customizations": {"vscode": {"extensions": ["ms-python.python"]}}
}
VS Code (ou un IDE compatible) ouvre le projet à l'intérieur du conteneur : outils, extensions et dépendances y sont préinstallés et identiques pour toute l'équipe. Fini le « ça marche sur ma machine ».devcontainer.json (dans .devcontainer/) décrit tout l'environnement :imageoubuild.dockerfile: la base.features: des outils préemballés à ajouter (Docker, Node, kubectl...).customizations.vscode.extensions: les extensions installées.forwardPorts,postCreateCommand: ports exposés, commandes d'initialisation.
- Docker Compose orchestre des services applicatifs (app, base de données, cache) pour exécuter une application.
- Dev Containers définit l'environnement de développement : l'outillage et les extensions IDE.
devcontainer.json peut pointer vers un docker-compose.yml (dockerComposeFile) pour développer dans un service donné, entouré de ses dépendances (une vraie base de données à côté).- la Dev Containers CLI (
devcontainer up,devcontainer exec) construit et lance un conteneur depuis un terminal ou en CI ; - GitHub Codespaces s'appuie directement sur
devcontainer.json; - d'autres IDE (JetBrains) l'implémentent.
À retenir
Section intitulée « À retenir »- Dev Containers = environnement en code,
devcontainer.jsondéfinit tout - Spec ouverte, Fonctionne avec VS Code, Codespaces, JetBrains, DevPod
- postCreateCommand pour l'installation, postStartCommand pour les process
- Features pour ajouter des outils sans Dockerfile
- Docker Compose pour les architectures multi-services
- forwardPorts au premier niveau, pas dans
customizations - Même image en CI, garantit la cohérence dev/production
- Remote SSH + DevContainers, Docker sur le serveur distant