Choosing between dev containers vs docker compose is less about which tool is “better” and more about which layer of the local development workflow you need to standardize. Docker Compose is strong at running multi-container applications; VS Code Dev Containers are strong at turning a container into a repeatable, editor-integrated development environment.
For many teams, the practical answer is not either/or. The strongest setup often uses Docker Compose to orchestrate services and VS Code Dev Containers to standardize the developer workspace, tools, extensions, terminals, and debugging experience.
Why Local Development Environments Are Hard to Standardize
Local development becomes difficult to standardize when a project has more than one runtime, more than one service, or more than one developer machine.
The source data highlights a common microservices scenario: multiple teams working across several services, such as a frontend, backend API, database, message queues, and other dependent microservices. Developers may need to work on multiple services at the same time and see how changes affect dependent systems without waiting for a shared staging environment.
Traditional local setup usually means every developer follows README files and installs dependencies directly on their machine. That works for small projects, but it breaks down as complexity grows.
Common problems include:
- Port Management: Multiple services need different ports, and it becomes hard to remember what
localhost:3000,localhost:3001, orlocalhost:3010represents. - Dependency Management: Different projects may need different versions of Node.js, Go, Ruby, databases, or command-line tools.
- Machine Differences: Operating systems, installed packages, shell configuration, and environment variables can vary across developers.
- Frontend Origin Issues: Different local ports can create CORS, Web Storage API, cookie, and origin-related problems.
- Onboarding Friction: New developers may need to follow several different README files, each with its own assumptions and setup steps.
The core local development problem is not just “how do we run the app?” It is “how do we give every developer the same app, tools, editor behavior, ports, dependencies, and setup path?”
This is where the dev containers vs docker compose comparison matters. Docker Compose helps define and run the application stack. Dev Containers help define and run the developer’s working environment.
What VS Code Dev Containers Do Best
VS Code Dev Containers let developers use a Docker container as a full-featured development environment. According to the VS Code documentation, the Dev Containers extension allows you to open any folder or repository inside a container while still using VS Code’s full feature set.
The key configuration file is devcontainer.json, usually stored at:
.devcontainer/devcontainer.json
or:
.devcontainer.json
at the project root.
This file tells VS Code how to access or create the development container and what to do after connecting.
Deep VS Code Integration
The biggest advantage of Dev Containers over plain Docker Compose is IDE integration.
Dev Containers can configure:
- Extensions: Automatically install VS Code extensions inside the container.
- Terminal: Run the integrated terminal inside the container.
- Debugging: Use VS Code debugging tools against the containerized runtime.
- IntelliSense: Provide code navigation, auto-completion, and syntax support based on the container environment.
- Workspace Settings: Store project-specific editor settings, tasks, and launch configurations.
A basic devcontainer.json can point to a prebuilt image:
{
"image": "mcr.microsoft.com/devcontainers/typescript-node:0-18"
}
The VS Code documentation also shows how to add extensions and forward ports:
{
"image": "mcr.microsoft.com/devcontainers/typescript-node",
"customizations": {
"vscode": {
"extensions": ["streetsidesoftware.code-spell-checker"]
}
},
"forwardPorts": [3000]
}
With that configuration, VS Code can reopen the project inside a Node.js and TypeScript development container, forward port 3000, and install the Code Spell Checker extension. The source data also notes that the base TypeScript/Node image includes the ESLint extension automatically.
Repeatable Development Tooling
Dev Containers are especially useful when the development environment needs tools that are not part of the production container.
For example, a development container may include:
- Debuggers
- Linters
- Build systems
- Editor extensions
- CLI tools
- Development-only dependencies
- Post-create scripts
The VS Code documentation explains that a dev container can be used either to run the application or to provide separate tools, libraries, and runtimes needed for working with a codebase.
Version-Controlled Environments
Because devcontainer.json lives in the repository, the development environment becomes version-controlled.
That means changes to tooling, extensions, forwarded ports, or setup commands can be reviewed and shared just like application code.
A common example is using postCreateCommand to run setup after the container is created:
{
"postCreateCommand": "bash scripts/install-dependencies.sh"
}
The VS Code documentation notes that postCreateCommand runs after the container is created, which makes it suitable for commands that need access to mounted workspace files, such as package installation scripts in the source tree.
For tools that need an interactive shell, the docs show:
{
"postCreateCommand": "bash -i -c 'nvm install --lts'"
}
Important warning: the command needs to exit, or the container will not start. Starting a long-running application process inside
postCreateCommandis a common mistake.
What Docker Compose Does Best
Docker Compose is designed to describe and run multi-container applications. The Cloudomation source describes Docker Compose as a way to reference several containers and define how they interact with each other.
A Compose file can define:
- Frontend containers
- Backend containers
- Database containers
- Networks
- Port mappings
- Secrets
- Storage volumes
- Image references
- Build instructions using Dockerfiles
Where Dev Containers focus on the developer’s environment, Docker Compose focuses on the application topology.
Multi-Service Application Orchestration
Docker Compose is the natural fit when a local app needs multiple services to work together.
The source data gives examples such as:
- A frontend service
- A backend service
- A database
- Other microservices communicating through REST, gRPC, or Kafka
- Service-specific dependencies
- Message queues
A Docker Compose file can define how those services connect, which ports are exposed, which volumes are mounted, and which images are built.
Networks, Ports, and Volumes
Compose is especially useful for local dependency management because it can put services on the same network and attach persistent volumes.
The microservices source shows a Compose setup that creates an attachable network so that Dev Containers can connect to the same network:
networks:
default:
attachable: true
name: shared-network
That same source uses Traefik as a routing layer to expose multiple services under one local entry point. For example, services can be routed as:
localhost:3000/svc1
localhost:3000/svc2
instead of each service needing an unrelated localhost port.
The sample Compose configuration includes:
services:
traefik:
image: traefik:v2.11.0
ports:
- 3000:80
- 5555:8080
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- /var/run/docker.sock:/var/run/docker.sock
command: --configFile=/etc/traefik/traefik.yml
networks:
- default
postgres-db:
profiles:
- postgres-db
- all
image: postgres:16.2
networks:
- default
This is the kind of multi-service coordination Docker Compose is built for.
Selective Service Startup with Profiles
The same microservices setup uses Compose profiles to run selected services.
For example:
COMPOSE_PROFILES=postgres-db,svc1
A Makefile target can then start the selected services:
dev:
docker compose up -d
This is useful when a developer does not need the full system running. They can start only the database and one service, or start a grouped profile where a database and API always run together.
Key Differences in Workflow and Configuration
The practical difference between dev containers vs docker compose is the level of abstraction.
Docker Compose defines how containers work together. Dev Containers define how a developer works inside or with those containers.
| Area | Docker Compose | VS Code Dev Containers |
|---|---|---|
| Primary Purpose | Orchestrates multi-container applications | Creates a full-featured development environment |
| Main Config File | compose.yml or docker-compose.yml |
devcontainer.json |
| Best At | Services, networks, ports, volumes, dependencies | Editor integration, extensions, terminals, debugging, tooling |
| Scope | Application-level environment | IDE and tooling-level environment |
| Extensions | Manual editor setup | Automatic VS Code extension installation |
| Debugging | Possible, but requires configuration | Integrated with VS Code workflows |
| Onboarding | Requires Docker Compose commands and project knowledge | Can be as simple as opening in VS Code and using “Reopen in Container” |
| Multi-Container Support | Core feature | Can reuse or extend Docker Compose |
| Development Scripts | Service lifecycle commands | postCreateCommand, postStartCommand, lifecycle hooks |
| Production Similarity | Often closer to deployment topology | Often adds development-only tools on top |
Dockerfile vs Compose vs Dev Container
The Cloudomation source makes a useful distinction:
- Dockerfile: Describes how to build one container image.
- Docker Compose: Describes how multiple containers work together.
- Dev Container: Adds a development-environment layer on top of images, Dockerfiles, or Compose files.
A Dockerfile can specify an operating system, packages, files, and shell commands. Docker Compose can define a frontend, backend, database, network, ports, secrets, and volumes. A Dev Container can reference those assets while also configuring IDE plugins, source code mapping, and post-installation scripts.
Dev Containers do not replace Docker Compose for multi-container orchestration. They add development workflow configuration on top of container infrastructure.
When to Use Both Together
In many real-world projects, the best answer to dev containers vs docker compose is to use both.
Use Docker Compose to run the supporting services and application stack. Use Dev Containers to give each developer the same workspace, editor extensions, terminal context, and development tools.
A Common Combined Pattern
A source from the Docker community discussion describes a practical approach:
- Use a Dockerfile that builds the application as if it could be sent toward production.
- Use Docker Compose to run the application and related services.
- Point the dev container to the Compose setup.
- Add development-only configuration through the dev container file.
- Use bind mounts so code remains on the host even if the dev container is removed.
This pattern keeps the production-like container setup separate from development conveniences.
Example Combined Architecture
A microservices setup from the source data uses:
- A central repository with a Makefile and Docker Compose configuration.
- Service repositories with their own Dockerfiles and Dev Container configurations.
- Traefik for routing traffic to services.
- Compose profiles for selecting which services to run.
- An attachable shared network so Dev Containers can join the same network as Compose services.
A simplified service definition from the source looks like this:
services:
svc1:
hostname: svc1
profiles:
- svc1
- all
image: svc1:latest
env_file:
- svc1/.env.example
- path: svc1/.env.local
required: false
networks:
- default
build:
context: ./svc1
dockerfile: Dockerfile
args:
- API_KEY=${API_KEY:-your_default_api_key}
- LOG_LEVEL=${LOG_LEVEL:-info}
labels:
- traefik.enable=true
- traefik.http.services.svc1.loadbalancer.server.port=5173
- traefik.http.routers.svc1.rule=(Host(`localhost`) || Host(`host.docker.internal`)) && PathPrefix(`/svc1`)
This shows the separation clearly:
- Compose handles service image, build context, environment files, network, profiles, and Traefik routing.
- Dev Containers can then attach developer tooling to the service workflow.
When the Combined Approach Makes Sense
Use both when your project has:
- Multiple Services: Frontend, backend, database, and supporting services.
- Team-Based Development: Several developers need the same setup.
- Editor Requirements: Extensions, debugging, and terminal behavior should be consistent.
- Service Dependencies: Developers need local databases, APIs, or queues.
- Cross-Service Work: Developers frequently move between repositories or services.
Performance and Resource Considerations
The provided sources do not include benchmark numbers for Dev Containers or Docker Compose, so any performance recommendation should be treated as workflow guidance rather than measured performance data.
That said, the sources do identify configuration choices that affect rebuild time, startup behavior, and resource usage.
Dockerfile Is Better for Persistent Tool Installation
The VS Code documentation explains that manually installed software inside a running dev container is lost when the container is rebuilt.
To avoid that, use either:
- A Dockerfile
postCreateCommand- Dev Container Features
The docs state that a custom Dockerfile benefits from Docker’s build cache and can result in faster rebuilds than postCreateCommand.
A Dockerfile example from the VS Code documentation:
FROM mcr.microsoft.com/devcontainers/javascript-node:0-18
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install git
Use a Dockerfile for tools and packages that do not depend on workspace files.
Use postCreateCommand for commands that require the source tree to be mounted, such as:
{
"postCreateCommand": "bash scripts/install-dependencies.sh"
}
Avoid Starting Too Much Locally
The microservices source recommends using Compose profiles to run selected services. This matters because large local stacks can become difficult to manage if every developer starts every service every time.
A practical Makefile pattern from the source:
dev:
docker compose up -d
down:
COMPOSE_PROFILES=all docker compose down
And an environment profile example:
COMPOSE_PROFILES=postgres-db,svc1
This lets developers run only the services needed for their current task.
Rebuild Intentionally
The VS Code documentation notes that when you edit files in .devcontainer, you need to rebuild for changes to take effect.
Relevant commands include:
- Dev Containers: Rebuild Container
- Rebuild without cache when cache-related issues occur, as mentioned in the community discussion.
A common rebuild issue is installing tools manually in the integrated terminal and expecting them to survive rebuilds. They will not unless captured in a Dockerfile, Feature, or lifecycle command.
Team Onboarding and Cross-Platform Support
Dev Containers are especially strong for onboarding because they move environment setup from human instructions into repository configuration.
Instead of asking a new developer to install runtimes, extensions, tools, and dependencies manually, a repository can include:
devcontainer.json- Dockerfile
- Docker Compose configuration
- Post-create scripts
- Forwarded ports
- VS Code extensions
- Workspace settings
The source data describes onboarding with Dev Containers as opening the project in VS Code and choosing “Reopen in Container.”
What Still Needs to Be Installed
Dev Containers do not remove every prerequisite.
The Cloudomation source explicitly notes requirements such as:
- Docker must be installed on the developer machine.
- The IDE must support and be configured to run Docker commands.
- The repository must contain properly configured container files.
So Dev Containers simplify project setup, but they do not eliminate the need for a working container runtime and compatible editor tooling.
Cross-Platform Value
The main cross-platform benefit comes from isolating tools and runtimes inside Linux containers rather than installing them directly on each developer’s machine.
This helps reduce differences caused by:
- Operating system differences
- Host-installed package versions
- Local shell configuration
- Runtime version conflicts
- Project-specific dependency conflicts
Dev Containers also support project isolation: each project can have its own toolchain, libraries, and runtime without polluting the host machine.
Common Mistakes to Avoid
1. Treating Dev Containers as a Docker Compose Replacement
Dev Containers and Docker Compose solve different problems.
Docker Compose defines multi-container application behavior. Dev Containers define a development environment and IDE workflow.
If your app has a database, backend, frontend, and supporting services, Compose is still the right tool for defining how those containers interact.
2. Putting Long-Running Commands in postCreateCommand
The VS Code documentation warns that postCreateCommand must exit. If you put an application start command there and it keeps running, the container will not start correctly.
Use postCreateCommand for setup tasks, not persistent processes.
3. Manually Installing Tools and Expecting Them to Persist
Installing packages manually inside the terminal is useful for experimentation, but those changes are lost after rebuild unless captured in configuration.
Better options include:
- Dockerfile: Best for persistent packages and tools independent of workspace files.
- Dev Container Features: Useful for predefined tools and languages.
postCreateCommand: Useful for source-tree-dependent setup.
4. Duplicating Port Configuration Without a Plan
Port mappings can be specified in Docker Compose and Dev Container files. The Cloudomation source notes that if a Dev Container references Docker Compose, port mappings can live in the Compose file.
Avoid scattering port decisions across multiple files without a clear convention.
5. Overcomplicating the Configuration Stack
The Cloudomation source warns that Dockerfile, Compose, Dev Container, and related standards can create several layers of complexity. Configuration spread across many files can make failures harder to diagnose.
Keep each file focused:
| File | Responsibility |
|---|---|
| Dockerfile | Build one container image |
| Compose file | Define multi-container application behavior |
| devcontainer.json | Define developer workspace, editor integration, and lifecycle commands |
| Makefile or scripts | Provide simple commands for common workflows |
6. Ignoring Environment File Behavior During Builds
The microservices source notes that build-time environment handling can be tricky because there is no easy way to directly access env files during first launch. The sample setup uses build args with defaults:
args:
- API_KEY=${API_KEY:-your_default_api_key}
- LOG_LEVEL=${LOG_LEVEL:-info}
It also introduces a Makefile build command that loads service-specific env files before rebuilding.
Recommended Setup by Project Type
The right choice depends on project complexity, team size, and how much of the workflow needs to be standardized.
| Project Type | Recommended Setup | Why |
|---|---|---|
| Single-service app with simple dependencies | Dev Container or Dockerfile | A dev container can standardize runtime, tools, extensions, and ports without a full Compose stack |
| Single app plus database | Docker Compose + optional Dev Container | Compose can run the app and database; Dev Container can standardize editor tooling |
| Microservices system | Docker Compose + Dev Containers | Compose handles service orchestration; Dev Containers handle per-service developer environments |
| Team with frequent onboarding | Dev Containers strongly recommended | devcontainer.json can automate extensions, tools, ports, and setup scripts |
| Production-like local stack | Dockerfile + Docker Compose, with Dev Container layered on top | Keeps application container behavior close to deployment while adding development-only tooling |
| Developers using VS Code heavily | Dev Containers + Compose when needed | Dev Containers provide deep VS Code integration, including extensions, terminal, debugging, and IntelliSense |
| Projects with many optional services | Docker Compose profiles + Dev Containers | Profiles allow selected service startup; Dev Containers preserve consistent development tooling |
For a Small Node.js or TypeScript Project
A simple Dev Container may be enough:
{
"image": "mcr.microsoft.com/devcontainers/typescript-node",
"customizations": {
"vscode": {
"extensions": ["streetsidesoftware.code-spell-checker"]
}
},
"forwardPorts": [3000]
}
This is useful when the main goal is repeatable tooling, editor behavior, and runtime isolation.
For a Backend with a Database
Use Docker Compose for the database and application service. Add a Dev Container if developers need standardized editor extensions, terminal tools, or debugging.
Compose should own the network and service definitions. The Dev Container can reuse or attach to that setup.
For Microservices
Use a central Compose setup with:
- Profiles for selective startup
- Shared attachable network
- Routing layer such as Traefik, if you need path-based local routing
- Per-service Dockerfiles
- Per-service Dev Container configurations
The source data’s microservices example uses Traefik with:
localhost:3000for routed serviceslocalhost:5555for the Traefik dashboardshared-networkas an attachable Docker network
This pattern helps avoid port conflicts and lets Dev Containers connect into the same local service network.
Bottom Line
The dev containers vs docker compose decision is best understood as a layering decision.
Docker Compose is the better fit for defining and running multi-container applications: services, networks, ports, volumes, databases, and service dependencies. VS Code Dev Containers are the better fit for standardizing the developer workspace: editor extensions, terminal environment, debugging, forwarded ports, tools, setup scripts, and project isolation.
For many teams, the strongest local workflow uses both: Docker Compose for the application stack and Dev Containers for the repeatable development experience. This keeps service orchestration separate from IDE and tooling concerns while giving developers a faster, more consistent setup path.
FAQ
Is a Dev Container the same as Docker Compose?
No. Docker Compose describes how multiple containers work together, such as a frontend, backend, and database. A Dev Container describes a development environment and can reference an image, Dockerfile, or Docker Compose setup.
When should I use Dev Containers instead of Docker Compose?
Use Dev Containers when your main problem is standardizing the developer experience: VS Code extensions, terminal tools, debugging, forwarded ports, setup commands, and runtime isolation. They are especially useful for team onboarding and project-specific toolchains.
When should I use Docker Compose instead of Dev Containers?
Use Docker Compose when your main problem is running multiple services together. It is designed for defining networks, volumes, ports, databases, application services, and service dependencies.
Can VS Code Dev Containers use Docker Compose?
Yes. The VS Code documentation states that Dev Containers can configure multiple containers through Docker Compose and can reuse or extend an existing Docker Compose setup.
Do Dev Containers remove the need to install Docker?
No. The source data notes that Docker still needs to be installed and the IDE must be configured correctly so it can run Docker commands.
What is the best setup for microservices?
For microservices, the source data supports using Docker Compose plus Dev Containers. Compose can orchestrate services with shared networks and profiles, while Dev Containers provide consistent per-service development environments with IDE integration.










