Dev Container — einheitliche Entwicklungsumgebung für Mac / Windows / Linux
Mit VS Code Dev Containers OS-Unterschiede eliminieren. Eine Docker-basierte, reproduzierbare Entwicklungsumgebung, die durch eine einzelne devcontainer.json definiert wird, die das gesamte Team teilt.
„Bei mir funktioniert es" beginnt meistens mit OS-Unterschieden. Mac hat Homebrew und Apple Silicon, Windows hat PowerShell und UTF-16, Linux-Maschinen haben systemd. Selbst wenn alle dieselbe Node-Version und dieselben Pakete installieren, entstehen subtile Unterschiede.
Dev Containers (VS Code Dev Containers + der Standard devcontainer.json) kapseln OS-Unterschiede in einem Docker-Container. Das gesamte Team arbeitet in derselben Linux-Umgebung — nur die IDE läuft auf dem Host. Diese Anleitung richtet einen einzelnen Dev Container ein, der auf Mac-, Windows- und Linux-Hosts identisch funktioniert.
TL;DR
- Eine Docker-Laufzeitumgebung installieren (Mac: OrbStack/Docker Desktop, Win: Docker Desktop/WSL Docker, Linux: docker)
- VS Code + die Dev Containers-Erweiterung (
ms-vscode-remote.remote-containers) - Eine
.devcontainer/devcontainer.jsonim Projektstamm ablegen Cmd/Ctrl+Shift+P→ Dev Containers: Reopen in Container — die IDE startet innerhalb des Containers neu- Teammitglied: klonen → diesen Befehl einmalig ausführen → identische Umgebung
Voraussetzungen
- VS Code 1.95+
- Docker-Laufzeitumgebung — OS-spezifische Anleitungen: mac/docker-setup · windows/docker-wsl2
- Projekt unter Git (damit devcontainer.json geteilt werden kann)
1. Warum Dev Containers
Probleme, die es löst
- „Bei mir funktioniert es" — innerhalb des Containers sind OS und Toolchain identisch
- Onboarding-Geschwindigkeit — neue Mitarbeiter lesen keine 30-zeilige README; sie klicken einmal
- Versionskonflikte — Projekt A mit Node 18, B mit Node 22 — ohne Host-mise/nvm sauber isoliert
- OS-spezifische Build-Probleme — Apple-Silicon-ARM-Probleme, Windows-Long-Path-Probleme usw. werden umgangen
- CI entspricht lokal — dasselbe Basisimage wie im GitHub-Actions-Workflow verwenden, und die Lücke verschwindet
Wo Dev Containers schwach sind
- GUI-App-Entwicklung — Mac-native oder Windows-native UI-Builds gehören nicht in einen Container
- GPU-intensive Workloads — GPU-Durchleitung ist teilweise möglich, aber auf dem Host stabiler
- Schwachbrüstige Rechner — Docker selbst verbraucht 1–2 GB zusätzlichen RAM (bei 16 GB kein Problem)
2. Minimale devcontainer.json
.devcontainer/devcontainer.json:
{
"name": "My Project Dev",
"image": "mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"postCreateCommand": "npm install",
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
},
"forwardPorts": [3000, 5173],
"remoteUser": "node"
}Wichtige Felder:
| Feld | Funktion |
|---|---|
image | Docker-Basisimage (Node / Python / Go / Rust usw.) |
features | Zusatztools (git, gh, docker-in-docker, awscli — aus einem Katalog auswählbar) |
postCreateCommand | Läuft einmalig nach der Container-Erstellung (Abhängigkeiten installieren usw.) |
customizations.vscode.extensions | VS Code-Erweiterungen, die automatisch im Container installiert werden |
forwardPorts | An den Host weitergeleitete Ports |
remoteUser | Benutzer innerhalb des Containers (nicht-root für Sicherheit empfohlen) |
3. Erster Start
VS Code-Befehlspalette (Cmd/Ctrl+Shift+P) → Dev Containers: Reopen in Container.
- Erster Start: ~1–3 Minuten (Image-Download +
postCreateCommand) - Unten links in VS Code wird
Dev Container: My Project Devangezeigt
# Terminal im Container (VS Code Terminal)
uname -a
# Linux 1234abcd 6.10.x #1 SMP Debian ...
which node
# /usr/local/share/nvm/versions/node/v22.x/bin/nodeUnabhängig vom Host-OS dieselbe Linux-Umgebung.
4. Basisimage auswählen
Das image: in devcontainer.json. Offizieller Katalog:
| Sprache / Stack | Image |
|---|---|
| Node.js | mcr.microsoft.com/devcontainers/javascript-node:1-{18,20,22}-bookworm |
| Python | mcr.microsoft.com/devcontainers/python:1-{3.11,3.12,3.13}-bookworm |
| Go | mcr.microsoft.com/devcontainers/go:1-{1.22,1.23} |
| Rust | mcr.microsoft.com/devcontainers/rust:1-bookworm |
| Java | mcr.microsoft.com/devcontainers/java:1-{17,21}-bookworm |
| .NET | mcr.microsoft.com/devcontainers/dotnet:1-9.0-bookworm |
| Universal (mehrsprachig) | mcr.microsoft.com/devcontainers/universal:2-linux |
Empfehlung: Einzelsprachiges Projekt → das zugehörige Sprachimage. Mehrsprachig → ein Basisimage + Features.
Alternativ kann ein eigenes Dockerfile verwendet werden:
{
"name": "My Custom Dev",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
}
}5. features — Tool mit einer Zeile hinzufügen
Der features-Block verwendet Module aus dem offiziellen Katalog (https://containers.dev/features). Eine Zeile und das Tool ist installiert.
Häufige Auswahl:
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}, // docker innerhalb des Containers ausführen
"ghcr.io/devcontainers/features/aws-cli:1": {},
"ghcr.io/devcontainers/features/terraform:1": {},
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {}
}6. Docker-in-Docker — Container im Container
Wenn docker run innerhalb des Dev Containers benötigt wird (docker-compose-Tests, Build-Verifikation):
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "latest",
"dockerDashComposeVersion": "v2"
}
}Alternativ den Docker-Socket des Hosts einbinden (leichter, aber geringere Sicherheit):
"mounts": [
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
]7. Host ↔ Container-Datei-Mounts
Der Arbeitsbereich wird automatisch unter /workspaces/{project} eingehängt.
Weitere Mounts:
"mounts": [
"source=${localEnv:HOME}/.aws,target=/home/node/.aws,type=bind,readonly",
"source=${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind"
]Für macOS-Mount-Performance siehe docker-setup §5.2 (VirtioFS empfohlen).
8. Umgebungsvariablen und Geheimnisse
Statische Umgebungsvariablen
"containerEnv": {
"NODE_ENV": "development",
"LOG_LEVEL": "debug"
}Geheimnisse (niemals committen)
.env-Datei einbinden oder über localEnv weiterleiten:
"containerEnv": {
"GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}",
"OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}"
}Host-Shell-Umgebungsvariablen werden in den Container injiziert. .env in .gitignore behalten.
9. Multi-Container (docker-compose-Integration)
Für Abhängigkeiten wie DB / Redis:
.devcontainer/docker-compose.yml:
services:
app:
image: mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm
volumes:
- ..:/workspaces/myapp
command: sleep infinity
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: dev
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:.devcontainer/devcontainer.json:
{
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/myapp"
}VS Code verbindet sich mit dem app-Container; db wird automatisch gestartet.
10. Gleiche Umgebung wie CI — devcontainer-cli
devcontainer.json in CI wiederverwenden:
npm install -g @devcontainers/cli
# Container bauen und Befehl ausführen
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . npm testGitHub Actions:
# .github/workflows/test.yml
- name: Test in dev container
uses: devcontainers/ci@v0.3
with:
runCmd: npm testCI grün = lokal dasselbe Ergebnis.
11. Überprüfung
# Host
docker --version
# Im Container (VS Code Terminal)
uname -a # Linux ...
which node # /usr/local/share/...
echo $NODE_ENV # development (containerEnv hat gewirkt)
node --version # die Node-Version des Containers
git --version # über features installiertAlle fünf antworten im Container = Einrichtung abgeschlossen.
12. Fehlerbehebung
„Failed to connect" / Container startet nicht
- Läuft die Docker-Laufzeitumgebung? (Docker Desktop in der Menüleiste,
orb status,colima status) docker psauf Port-/Namenskollision prüfen- VS Code → Befehlspalette → „Dev Containers: Rebuild Without Cache"
Builds dauern sehr lange
- Erster Build lädt das Basisimage (mehrere GB) — das ist normal
- Folgende Builds nutzen den Cache.
postCreateCommandwird nicht gecacht (läuft bei jedem Rebuild) onCreateCommand(nur einmal) vonpostCreateCommand(bei jedem Rebuild) unterscheiden
Zugriff auf Dateien verweigert
- Container-UID und Host-UID stimmen nicht überein (besonders auf Linux-Hosts)
remoteUserplusupdateRemoteUserUIDsetzen:"remoteUser": "vscode", "updateRemoteUserUID": true
forwardPorts funktioniert nicht
- Bindet die App im Container an
0.0.0.0oder::(nicht127.0.0.1)? - Beispiel: Next.js benötigt
next dev -H 0.0.0.0
Host Git-Konfiguration wird nicht übernommen
.gitconfig-Auto-Mount ist optional — explizit inmountshinzufügen- Oder dem
features-git vertrauen, das einegitconfig-Kopie verwendet
IDE ist langsam bei großen node_modules
node_modulesaus dem VS-Code-Datei-Watcher ausschließen (files.watcherExclude)- Oder
node_modulesin ein Container-exklusives Volume statt einen Host-Mount legen:"mounts": [ "source=${localWorkspaceFolderBasename}-node-modules,target=/workspaces/${localWorkspaceFolderBasename}/node_modules,type=volume" ]
13. Was als Nächstes
- Mac Docker — /mac/docker-setup (Laufzeitvergleich)
- Windows Docker WSL2 — /windows/docker-wsl2
- Remote-Entwicklung — /multi-os/remote-development — einen entfernten Rechner als Entwicklungsumgebung nutzen
- Dotfiles-Sync — /mac/dotfiles — Dotfiles via chezmoi auch im Dev Container anwenden
Referenzen
Changelog
- 2026-05-16: Erster Entwurf. devcontainer.json-Grundlagen · Basisimage-Auswahl · features · docker-compose-Integration · CI-Integration · sechs Fehlerbehebungsfälle.