devAlice
← Multi-OS

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

  1. Eine Docker-Laufzeitumgebung installieren (Mac: OrbStack/Docker Desktop, Win: Docker Desktop/WSL Docker, Linux: docker)
  2. VS Code + die Dev Containers-Erweiterung (ms-vscode-remote.remote-containers)
  3. Eine .devcontainer/devcontainer.json im Projektstamm ablegen
  4. Cmd/Ctrl+Shift+PDev Containers: Reopen in Container — die IDE startet innerhalb des Containers neu
  5. 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:

FeldFunktion
imageDocker-Basisimage (Node / Python / Go / Rust usw.)
featuresZusatztools (git, gh, docker-in-docker, awscli — aus einem Katalog auswählbar)
postCreateCommandLäuft einmalig nach der Container-Erstellung (Abhängigkeiten installieren usw.)
customizations.vscode.extensionsVS Code-Erweiterungen, die automatisch im Container installiert werden
forwardPortsAn den Host weitergeleitete Ports
remoteUserBenutzer 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 Dev angezeigt
# 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/node

Unabhängig vom Host-OS dieselbe Linux-Umgebung.


4. Basisimage auswählen

Das image: in devcontainer.json. Offizieller Katalog:

Sprache / StackImage
Node.jsmcr.microsoft.com/devcontainers/javascript-node:1-{18,20,22}-bookworm
Pythonmcr.microsoft.com/devcontainers/python:1-{3.11,3.12,3.13}-bookworm
Gomcr.microsoft.com/devcontainers/go:1-{1.22,1.23}
Rustmcr.microsoft.com/devcontainers/rust:1-bookworm
Javamcr.microsoft.com/devcontainers/java:1-{17,21}-bookworm
.NETmcr.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 test

GitHub Actions:

# .github/workflows/test.yml
- name: Test in dev container
  uses: devcontainers/ci@v0.3
  with:
    runCmd: npm test

CI 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 installiert

Alle 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 ps auf 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. postCreateCommand wird nicht gecacht (läuft bei jedem Rebuild)
  • onCreateCommand (nur einmal) von postCreateCommand (bei jedem Rebuild) unterscheiden

Zugriff auf Dateien verweigert

  • Container-UID und Host-UID stimmen nicht überein (besonders auf Linux-Hosts)
  • remoteUser plus updateRemoteUserUID setzen:
    "remoteUser": "vscode",
    "updateRemoteUserUID": true

forwardPorts funktioniert nicht

  • Bindet die App im Container an 0.0.0.0 oder :: (nicht 127.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 in mounts hinzufügen
  • Oder dem features-git vertrauen, das eine gitconfig-Kopie verwendet

IDE ist langsam bei großen node_modules

  • node_modules aus dem VS-Code-Datei-Watcher ausschließen (files.watcherExclude)
  • Oder node_modules in 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


Referenzen

Changelog

  • 2026-05-16: Erster Entwurf. devcontainer.json-Grundlagen · Basisimage-Auswahl · features · docker-compose-Integration · CI-Integration · sechs Fehlerbehebungsfälle.