How to Configure Development Containers with Docker
VSCode Dev Containers for React + FastAPI with Docker Compose and hot reloading

This was an enourmous source of frustration to set up so I had to document the process.
I was working on a full-stack project when I ran into the classic "it works on my machine" problem.
I couldn't get the development environment set up correctly, and I were spending hours troubleshooting dependency conflicts instead of actually building features.
This guide shows you exactly how to configure development containers that eliminate environment issues and get your entire team productive within minutes instead of days.
I'll demonstrate these concepts using React and FastAPI as examples, but the configuration principles apply to any technology stack - the power lies in mastering the development container setup itself.
What Development Containers Actually Solve
Development containers represent a fundamental shift from traditional development setups. Instead of installing languages, frameworks, and tools directly on your local machine, you develop inside Docker containers that encapsulate everything your project needs. The key insight is that these containers feel exactly like local development while providing perfect consistency across your entire team.
The breakthrough moment came when I realized development containers aren't just about consistency - they're about eliminating the entire category of environment-related problems. No more "works on my machine," no more spending half a day setting up a new developer's environment, and no more conflicts between different projects requiring different versions of the same tools.
Why I Use React + FastAPI to Demonstrate
I'm using a React frontend with Vite and a Python FastAPI backend to demonstrate development container configuration. This isn't about these specific technologies - it's about showing how to configure development containers for any multi-service application.
Why this combination works perfectly as a demonstration:
- Cross-language complexity: JavaScript/Node.js and Python represent the exact kind of environment management nightmare that development containers solve
- Real configuration challenges: Different package managers, runtime versions, and development tools - perfect for showcasing comprehensive
.devcontainer
setup - Universal patterns: The configuration techniques you'll learn apply to Vue + Django, Angular + .NET, or any other technology combination
The real focus is mastering .devcontainer
configuration. Once you understand how to structure these files properly, you can configure development containers for any technology stack. The React and FastAPI code is just the canvas - the development container configuration is the masterpiece.
What makes development containers powerful:
- Your entire development environment runs in Docker while maintaining local development performance
- Every team member works in identical environments regardless of their operating system
- New developers become productive immediately without complex setup procedures
- Full VSCode functionality works seamlessly inside containers with IntelliSense, debugging, and extensions
- Hot reloading and auto-refresh work exactly as expected in traditional development
Prerequisites and Setup
Before diving into the implementation, you'll need three essential tools. The setup process is straightforward, but getting these components working together correctly is crucial for the entire workflow.
Required installations:
- Docker Desktop - Download and install from docker.com
- VSCode - Get the latest version from code.visualstudio.com
- Dev Containers Extension - Install this directly in VSCode by searching for "Dev Containers" in the extensions marketplace
The Dev Containers extension is what makes the magic happen. Once installed, VSCode can connect to and work inside Docker containers as if they were local development environments.
Project Structure: The .devcontainer
Configuration is Everything
The development container structure I've settled on supports both independent service development and coordinated multi-service workflows. The most critical part of this entire setup is getting the .devcontainer
configurations right - these files are what transform ordinary Docker containers into fully-featured development environments that rival local development.
project-root/
├── .devcontainer/
│ ├── frontend/
│ │ └── devcontainer.json # Frontend container config
│ └── backend/
│ └── devcontainer.json # Backend container config
├── frontend/
│ ├── Dockerfile.dev # Frontend dev environment
│ ├── package.json
│ └── src/
├── backend/
│ ├── Dockerfile.dev # Backend dev environment
│ ├── requirements.txt
│ └── main.py
└── compose.dev.yml # Development orchestration
The .devcontainer
directory is the heart of this entire system. These VSCode-specific configurations define exactly how each service behaves when opened in a development container - which extensions get installed, what settings are applied, which ports are forwarded, and how the development environment is initialized. Master these configurations, and you've mastered development containers.
Each service gets its own devcontainer.json
file with carefully chosen extensions, settings, and development tools. The development Dockerfiles support these configurations by providing the base environment, but the .devcontainer
files are what create the actual development experience.
Frontend Development Container Setup
Setting up the frontend development container focuses on creating an optimal React development environment with comprehensive tooling and hot reloading. The goal is to match or exceed the experience of local development while providing perfect consistency across the team.
Frontend Development Dockerfile
Create frontend/Dockerfile.dev
:
# Development environment with Node.js FROM node:20-slim # Set working directory WORKDIR /app # Install git for VSCode and development tools RUN apt-get update && apt-get install -y \ git \ curl \ && rm -rf /var/lib/apt/lists/* # Copy package files COPY package*.json ./ # Install dependencies RUN npm install --legacy-peer-deps # Copy source code COPY . . # Expose Vite dev server port EXPOSE 5173 # Start Vite dev server with host binding for Docker CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5173"]
This Dockerfile creates a development environment that includes system tools like Git (essential for VSCode functionality) and configures Vite with host binding so the development server is accessible from outside the container. The --host 0.0.0.0
configuration is crucial - without it, you won't be able to access your development server from your host machine.
Frontend Dev Container Configuration
Create .devcontainer/frontend/devcontainer.json
:
{
"name": "Frontend Dev Container",
"dockerComposeFile": ["../../compose.dev.yml"],
"service": "frontend",
"shutdownAction": "none",
"workspaceFolder": "/workspace/frontend",
"customizations": {
"vscode": {
"extensions": [
// Essential React/TypeScript extensions
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss",
"dsznajder.es7-react-js-snippets",
// Development productivity
"formulahendry.auto-rename-tag",
"christian-kohler.npm-intellisense",
"christian-kohler.path-intellisense",
// Git and Docker support
"eamodio.gitlens",
"ms-azuretools.vscode-docker",
// Code quality
"ms-vscode.vscode-typescript-next"
],
"settings": {
// Editor configuration
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": true
},
// JavaScript/TypeScript settings
"javascript.updateImportsOnFileMove.enabled": "always",
"typescript.updateImportsOnFileMove.enabled": "always",
// Terminal configuration
"terminal.integrated.defaultProfile.linux": "bash",
// File associations
"files.associations": {
"*.css": "tailwindcss"
}
}
}
},
// Development setup
"postCreateCommand": "npm install && npm run dev",
"forwardPorts": [5173],
"portsAttributes": {
"5173": {
"label": "Vite Dev Server",
"onAutoForward": "notify"
}
},
// Use node user for security
"remoteUser": "node",
// Development features
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
}
}
This configuration transforms a basic Docker container into a fully-featured development environment. The extension list includes everything you need for productive React development, while the settings ensure code formatting and linting happen automatically. The postCreateCommand
runs when the container is created, automatically installing dependencies and starting the development server.
Backend Development Container Setup
The backend setup creates a comprehensive Python development environment optimized for FastAPI development with debugging capabilities, testing infrastructure, and code quality tools integrated directly into the container.
Backend Development Dockerfile
Create backend/Dockerfile.dev
:
# Development environment with Python FROM python:3.11-slim # Set working directory WORKDIR /app # Install system dependencies for development RUN apt-get update && apt-get install -y \ sqlite3 \ git \ curl \ build-essential \ && rm -rf /var/lib/apt/lists/* # Copy requirements COPY requirements.txt . # Install Python dependencies with development tools RUN pip install --no-cache-dir -r requirements.txt \ && pip install --no-cache-dir \ pytest \ pytest-asyncio \ black \ flake8 \ mypy \ isort # Create data directory RUN mkdir -p /app/data && chmod 777 /app/data # Copy application code COPY . . # Expose port 8000 for FastAPI EXPOSE 8000 # Set environment variables ENV PYTHONUNBUFFERED=1 ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONPATH=/app # Run with Uvicorn in development mode with auto-reload CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload", "--log-level", "debug"]
This Dockerfile includes all the development tools you need for professional Python development: pytest for testing, Black for formatting, Flake8 for linting, and MyPy for type checking. The environment variables optimize Python for development with unbuffered output and no bytecode generation. The --reload
flag in the CMD ensures the server automatically restarts when you change code.
Backend Dev Container Configuration
Create .devcontainer/backend/devcontainer.json
:
{
"name": "Backend Dev Container",
"dockerComposeFile": ["../../compose.dev.yml"],
"service": "backend",
"shutdownAction": "none",
"workspaceFolder": "/workspace/backend",
"customizations": {
"vscode": {
"extensions": [
// Python development essentials
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.black-formatter",
"ms-python.isort",
"ms-python.flake8",
"ms-python.mypy-type-checker",
// Documentation and productivity
"njpwerner.autodocstring",
"kevinrose.vsc-python-indent",
// FastAPI specific
"tamasfe.even-better-toml",
// Development tools
"eamodio.gitlens",
"ms-azuretools.vscode-docker",
// Code quality
"charliermarsh.ruff",
"ms-python.pytest"
],
"settings": {
// Python interpreter
"python.defaultInterpreterPath": "/usr/local/bin/python",
// Linting configuration
"python.linting.enabled": true,
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true,
"python.linting.mypyEnabled": true,
// Formatting configuration
"editor.formatOnSave": true,
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
// Testing configuration
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.pytestArgs": ["tests"],
// IntelliSense settings
"python.analysis.typeCheckingMode": "basic",
"python.analysis.autoImportCompletions": true
}
}
},
// Development setup
"postCreateCommand": "pip install -r requirements.txt && python -c 'import sqlite3; sqlite3.connect(\"/app/data/app.db\").close(); print(\"Database initialized\")'",
"forwardPorts": [8000],
"portsAttributes": {
"8000": {
"label": "FastAPI Server",
"onAutoForward": "notify"
}
},
// Run as root for development flexibility
"remoteUser": "root",
// Development features
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
}
}
This configuration creates a sophisticated Python development environment with comprehensive tooling integration. The Python extension stack provides IntelliSense, debugging, and code navigation, while the formatting and linting tools maintain code quality automatically. The testing configuration enables running pytest directly from VSCode's interface.
Development Orchestration with Docker Compose
The development compose configuration orchestrates both containers into a cohesive development environment that supports rapid iteration, hot reloading, and seamless service communication.
Development Compose Configuration
Create compose.dev.yml
:
services:
# Frontend development service
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
ports:
- "5173:5173" # Vite dev server
- "24678:24678" # Vite HMR port
networks:
- app-network
volumes:
# Mount source code for hot reloading
- .:/workspace:cached
# Use named volume for node_modules to avoid conflicts
- frontend-node-modules:/workspace/frontend/node_modules
# Cache for faster rebuilds
- frontend-cache:/workspace/frontend/.vite
environment:
# Point to backend dev server
- VITE_API_URL=http://localhost:8000
- NODE_ENV=development
stdin_open: true
tty: true
# Backend development service
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev
ports:
- "8000:8000" # FastAPI dev server
- "5678:5678" # Python debugger port
networks:
- app-network
volumes:
# Mount source code for hot reloading
- .:/workspace:cached
# Persist backend data
- backend-data:/app/data
# Python cache for faster imports
- backend-cache:/root/.cache/pip
environment:
- DATABASE_URL=sqlite:///app/data/app.db
- ENVIRONMENT=development
- DEBUG=true
- PYTHONPATH=/workspace/backend
stdin_open: true
tty: true
# Named volumes for performance and persistence
volumes:
frontend-node-modules:
driver: local
frontend-cache:
driver: local
backend-data:
driver: local
backend-cache:
driver: local
# Network for service communication
networks:
app-network:
driver: bridge
This compose file balances performance, functionality, and developer experience. The volume mounting strategy uses :cached
for better performance on macOS, while named volumes for dependencies prevent conflicts and improve build performance. The network configuration enables service-to-service communication using container names.
The Development Workflow
Once everything is set up, the development workflow becomes incredibly smooth. Here's how I work with development containers on a daily basis.
Starting Your Development Environment
# Start all services
docker compose -f compose.dev.yml up -d
# Or start with logs visible for troubleshooting
docker compose -f compose.dev.yml up
Connecting VSCode to Containers
The fastest way to connect is through VSCode's command palette:
- Open VSCode in your project root
- Press
Ctrl+Shift+P
(orCmd+Shift+P
on Mac) - Type "Dev Containers: Reopen in Container"
- Select either "Frontend Dev Container" or "Backend Dev Container"
VSCode automatically handles container building, starting, and connection. All configured extensions install automatically, and you get a fully configured development environment instantly.
Multi-Service Development
For full-stack development, I typically work with both containers simultaneously:
- Open two VSCode windows
- Connect one to the frontend container - Access files at
/workspace/frontend
- Connect the other to the backend container - Access files at
/workspace/backend
This setup gives you:
- Frontend hot reload at
http://localhost:5173
- Backend auto-reload at
http://localhost:8000
- Seamless API communication between services
- Independent debugging for each service
Daily Development Commands
Frontend development:
# Install new packages
docker compose -f compose.dev.yml exec frontend npm install axios
# Run tests
docker compose -f compose.dev.yml exec frontend npm run test
# Access container shell
docker compose -f compose.dev.yml exec frontend bash
Backend development:
# Install new packages
docker compose -f compose.dev.yml exec backend pip install sqlalchemy
# Run tests
docker compose -f compose.dev.yml exec backend pytest
# Access container shell
docker compose -f compose.dev.yml exec backend bash
Debugging and Development Features
Frontend Debugging
The frontend development experience matches local development exactly:
- React DevTools work seamlessly with containerized development
- Hot Module Replacement provides instant feedback without page reloads
- VSCode debugging with breakpoints works directly in containerized JavaScript
- Browser DevTools show API calls to the containerized backend normally
Backend Debugging
Backend debugging is equally comprehensive:
- VSCode Python debugger attaches to Python running in the container
- Interactive debugging with breakpoint support and variable inspection
- FastAPI documentation at
http://localhost:8000/docs
for API testing - Real-time logs using
docker compose logs -f backend
Troubleshooting Common Issues
Container Won't Start
# Check logs for specific errors
docker compose -f compose.dev.yml logs frontend
docker compose -f compose.dev.yml logs backend
# Rebuild containers from scratch
docker compose -f compose.dev.yml build --no-cache
Hot Reload Not Working
For frontend hot reload issues, add polling to your vite.config.js
:
export default {
server: {
host: '0.0.0.0',
port: 5173,
watch: {
usePolling: true // Required for file watching in containers
}
}
}
VSCode Can't Connect
# Verify containers are running
docker compose -f compose.dev.yml ps
# Restart Dev Containers extension in VSCode
# Ctrl+Shift+P → "Developer: Reload With Extensions Disabled"
# Then re-enable the Dev Containers extension
Performance Issues
Create a .dockerignore
file to exclude unnecessary files from the build context:
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
coverage
.nyc_output
Performance Optimization Tips
Container performance:
- Use named volumes for
node_modules
and caches to avoid performance penalties - Mount only necessary directories for hot reloading
- Use
.dockerignore
to reduce build context size - Keep containers running during development to avoid startup overhead
VSCode optimization:
- Install only necessary extensions to reduce resource usage
- Use workspace-specific settings to avoid configuration conflicts
- Configure file watching properly for container environments
This development container setup has transformed how my team develops full-stack applications. We eliminated environment setup time, reduced onboarding from days to minutes, and completely solved the "works on my machine" problem. The initial setup investment pays for itself immediately through improved team productivity and eliminated debugging time.
Let me know in the comments if you have questions about implementing development containers for your team, and subscribe for more practical development guides.
Thanks, Matija