| name | docker-development-env |
|---|---|
| description | Set up development environments with Docker including hot reload, dev containers, bind mounts, and database containers. |
| standards-version | 1.7.0 |
Use this skill when:
- Setting up a local development stack with Docker
- Configuring hot reload / live code reload in containers
- Creating dev vs production Dockerfiles or multi-stage builds
- Running database containers (Postgres, MySQL, Redis, MongoDB) for development
- Setting up VS Code or Cursor devcontainers
- Managing environment variables across dev and prod
- Debugging applications running inside containers
- Creating docker-compose files for full-stack development
- Application language/framework (Node.js, Python, Go, etc.)
- Services needed (database, cache, message queue, etc.)
- IDE (VS Code, Cursor, or other)
- Host OS (Windows, macOS, Linux)
- Whether hot reload is needed
- Create a dev Dockerfile - use a multi-stage Dockerfile with a dev target that includes dev dependencies and debugging tools.
- Set up docker-compose for development - define all services (app, database, cache) with dev-friendly settings.
- Configure bind mounts - mount source code from the host into the container for live editing.
- Enable hot reload - configure the framework's file watcher to work with bind-mounted volumes.
- Add database containers - run Postgres, MySQL, Redis, or MongoDB with persistent named volumes.
- Set up environment variables - use
.envfiles for local dev, never commit secrets. - Configure devcontainer - optionally set up
.devcontainer/devcontainer.jsonfor a full IDE-in-container experience.
- Docker Compose for development
- Bind mounts
- Dev containers specification
- VS Code devcontainers
- Multi-stage builds
User: Set up a Node.js dev environment with hot reload and Postgres.
Agent: Here's a multi-stage Dockerfile with a dev target:
# Dockerfile
FROM node:20-alpine AS base
WORKDIR /app
COPY package.json package-lock.json ./
FROM base AS dev
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]
FROM base AS prod
RUN npm ci --omit=dev
COPY . .
RUN npm run build
USER node
CMD ["node", "dist/index.js"]And a Compose file for the full dev stack:
# docker-compose.yml
services:
app:
build:
context: .
target: dev
ports:
- "3000:3000"
- "9229:9229" # Node.js debugger
volumes:
- .:/app
- /app/node_modules # anonymous volume prevents overwriting
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://devuser:devpass@db:5432/devdb
depends_on:
db:
condition: service_healthy
db:
image: postgres:16-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: devuser
POSTGRES_PASSWORD: devpass
POSTGRES_DB: devdb
volumes:
- pgdata:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U devuser -d devdb"]
interval: 5s
timeout: 3s
retries: 5
volumes:
pgdata:The anonymous volume /app/node_modules prevents the host bind mount from overwriting the container's node_modules. This is critical because native modules compiled inside the container (Linux) won't work on the host (Windows/macOS) and vice versa.
User: How do I add Redis and MongoDB to that stack?
Agent:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redisdata:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
mongo:
image: mongo:7
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: devuser
MONGO_INITDB_ROOT_PASSWORD: devpass
volumes:
- mongodata:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 5s
timeout: 3s
retries: 5
volumes:
pgdata:
redisdata:
mongodata:Update your app environment variables:
environment:
- REDIS_URL=redis://redis:6379
- MONGO_URL=mongodb://devuser:devpass@mongo:27017/devdb?authSource=adminUser: How do I set up a devcontainer for Cursor/VS Code?
Agent: Create .devcontainer/devcontainer.json:
{
"name": "My Project Dev",
"dockerComposeFile": "../docker-compose.yml",
"service": "app",
"workspaceFolder": "/app",
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"ms-azuretools.vscode-docker"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
},
"forwardPorts": [3000, 5432, 6379],
"postCreateCommand": "npm install",
"remoteUser": "node"
}Or use a standalone Dockerfile-based devcontainer:
{
"name": "Node.js Dev",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"forwardPorts": [3000],
"postCreateCommand": "npm install",
"remoteUser": "node"
}With its Dockerfile at .devcontainer/Dockerfile:
FROM mcr.microsoft.com/devcontainers/javascript-node:20
RUN npm install -g npm@latestUser: How do I handle environment variables across dev and prod?
Agent: Use a .env file for local development, never committed to git:
# .env (add to .gitignore)
DATABASE_URL=postgresql://devuser:devpass@db:5432/devdb
REDIS_URL=redis://redis:6379
JWT_SECRET=dev-secret-not-for-production
LOG_LEVEL=debugReference it in Compose:
services:
app:
env_file:
- .envProvide a template for other developers:
# .env.example (committed to git)
DATABASE_URL=postgresql://user:password@db:5432/mydb
REDIS_URL=redis://redis:6379
JWT_SECRET=change-me
LOG_LEVEL=infoFor production, inject secrets via your orchestrator (Kubernetes secrets, ECS task definitions, Docker Swarm secrets) - never bake them into images.
User: How do I debug a Node.js app inside a container?
Agent: Expose the debug port and attach your IDE:
app:
command: ["node", "--inspect=0.0.0.0:9229", "src/index.js"]
ports:
- "3000:3000"
- "9229:9229"In VS Code / Cursor, add a launch configuration:
{
"type": "node",
"request": "attach",
"name": "Docker: Attach",
"port": 9229,
"address": "localhost",
"localRoot": "${workspaceFolder}",
"remoteRoot": "/app",
"restart": true
}For Python with debugpy:
app:
command: ["python", "-m", "debugpy", "--listen", "0.0.0.0:5678", "--wait-for-client", "app.py"]
ports:
- "5678:5678"| Tool | Purpose |
|---|---|
docker_listContainers |
Check which dev services are running and their status |
docker_containerLogs |
Read logs from dev containers to debug application errors |
docker_inspectContainer |
Check bind mounts, environment variables, and port mappings |
docker_listVolumes |
Verify named volumes for database persistence |
docker_listNetworks |
Check that dev services can communicate on the same network |
docker_listImages |
See local dev images and their sizes |
docker_diskUsage |
Monitor disk usage from dev images and volumes |
Verifying dev environment setup:
1. docker_listContainers - confirm all services are running (app, db, redis)
2. docker_containerLogs on each - check for startup errors
3. docker_inspectContainer on app - verify:
- Mounts include the source code bind mount
- Environment variables are set correctly
- Ports are mapped (3000, 9229 for debug)
4. docker_listNetworks - confirm services share a network
5. docker_listVolumes - confirm database volumes exist for persistence
Debugging a service that won't start:
1. docker_listContainers (all=true) - find the crashed container
2. docker_containerLogs - read the error output
3. docker_inspectContainer - check State.ExitCode and depends_on health
4. docker_inspectContainer on dependency (db) - verify it's healthy
- node_modules from host overwriting container - a bind mount of
.:/appreplaces the container'snode_moduleswith the host's. Use an anonymous volume (/app/node_modules) to preserve the container's copy. - File watchers not detecting changes - on Docker Desktop (macOS/Windows), filesystem events may be delayed or missing. Use polling mode in your framework's watcher (e.g.,
CHOKIDAR_USEPOLLING=truefor webpack,WATCHPACK_POLLING=truefor Next.js). - Database data lost on rebuild - use named volumes (
pgdata:/var/lib/postgresql/data), not bind mounts to random directories. Named volumes survivedocker compose down(but notdocker compose down -v). - Port collisions with host services - if Postgres is running on the host and in Docker, both can't bind to 5432. Stop the host service or remap:
5433:5432. - Stale containers after Compose file changes - run
docker compose up --build --force-recreateafter changing the Compose file or Dockerfile. - Slow bind mount performance on macOS - Docker Desktop for macOS has slower file I/O on bind mounts. Use the VirtioFS file sharing backend (default in newer versions) or move heavy I/O (like
node_modules) to named volumes. - Missing .env file - Compose silently ignores a missing
.envfile referenced byenv_file. This causes undefined variables at runtime with no error during startup. - Running dev tools in production images - keep dev dependencies (debuggers, test frameworks, hot reload) in a dev stage. The production stage should only include runtime dependencies.
- Hardcoded localhost in app config - inside a container,
localhostrefers to the container itself, not the host. Use Docker DNS names (db,redis) from the Compose service names. - Not using health checks on dependencies - without
healthcheckanddepends_on.condition: service_healthy, your app may start before the database is ready.