Welcome to the fifth guide in the Dev Container series:
- Dev Containers - Why You Need Them
- Part 1: Quick Start - Basic Setup and Usage
- Part 2: Image, Features, Workspace, Environment Variables
- Part 3: Full Stack Dev - Docker Compose & Database
- Part 4: Remote Dev - Develop on a Remote Docker Host
- Part 5: Multiple Projects & Shared Container Configuration
To get started, you can clone my demo project using the following command:
git clone -b part-5-shared-configure-for-multiple-projects https://github.com/graezykev/dev-container.git
Introduction
In Part 3, we learned about using Docker Compose in Dev Containers to build containers for Node.js
applications and databases.
Currently, you can only connect to one container per Visual Studio Code window. But what if you have multiple projects using different tech stacks like Node.js
, Python
, Go
, etc., and need to create Dev Containers for each?
One option is to place a .devcontainer
folder under each project:
.
└── path
└── to
├── project-a-node-js
│ └── .devcontainer
│ ├── docker-compose.yml
│ ├── ...
│ └── devcontainer.json
├── project-b-node-js
│ └── .devcontainer
│ ├── ...
│ └── devcontainer.json
├── project-c-python
│ └── .devcontainer
│ ├── ...
│ └── devcontainer.json
├── project-d-go-lang
│ └── .devcontainer
│ ├── ...
│ └── devcontainer.json
└── project-...
If these applications need to share the same database, you must ensure they all use the same database container in their docker-compose.yml
and the same volume:
services:
app-name-...
...
postgres:
image: postgres:latest
...
...
volumes:
postgres-data:
However, this can result in overlapping configurations across multiple projects, making maintenance tedious. A better approach is to share a common docker-compose.yml
:
.
└── path
└── to
└── dev-container
│
├── .devcontainer
│ │
│ ├── .env
│ ├── docker-compose.yml
│ ├── ...
│ │
│ ├── project-a-node-js
│ │ └── devcontainer.json
│ │
│ ├── project-b-node-js
│ │ └── devcontainer.json
│ │
│ ├── project-c-python
│ │ └── devcontainer.json
│ │
│ ├── project-d-go-lang
│ │ └── devcontainer.json
│ │
│ └── project-e-...
│ └── devcontainer.json
│
├── project-a-node-js
│ └── index.js
│
├── project-b-node-js
│
├── project-c-python
│ └── hello.py
│
├── project-d-go-lang
└── project-e-...
All projects and the .devcontainer
folder share a common root-level folder, with each project having its own configuration folder under .devcontainer
.
This setup allows you to define multiple Dev Containers (and a container for the database) in a common docker-compose.yml
, and create a devcontainer.json
for each project to reference the shared docker-compose.yml
. This approach also helps manage each project's features and lifecycle scripts, avoiding configuration conflicts.
Common Docker Compose File
First, create a common docker-compose.yml
inside the root-level .devcontainer
:
services:
project-a-node-js:
image: graezykev/dev-container-base-image:latest
volumes:
- ..:/workspaces:cached
ports:
- 8001:8000
depends_on:
- postgres
command: /bin/zsh -c "while sleep 1000; do :; done"
project-b-node-js:
...
volumes:
- ..:/workspaces:cached
ports:
- 8002:8000
project-c-python:
...
project-d-go-lang:
...
project-e-...
postgres:
...
volumes:
postgres-data:
See the complete file in my demo.
All projects share the same database container postgres
.
We have learned most of the concepts in Part 3 but there are a few things we need to pay attention to.
Ports
Notice the port mappings: 8001:8000
, 8002:8000
, etc. Each project uses port 8000
within its own Dev Container, mapped to different ports on the host machine. This setup avoids port conflicts and allows access to each project's server via distinct ports (e.g., 8001
, 8002
, etc.).
See demo preveiw below.
Volumes & Workspace
Using ..:/workspaces
for the volumes
sections mounts the entire root-level folder (dev-container
) on the host machine to /workspaces
in the containers.
In each devcontainer.json
, specify the project folder within the workspace:
"workspaceFolder": "/workspaces/project-b-node-js"
Service
In each devcontainer.json
, reference the Docker Compose file docker-compose.yml
and specify the service name:
{
"name": "Dev Container",
"dockerComposeFile": [
"../docker-compose.yml"
],
"service": "project-a-node-js",
"shutdownAction": "none",
"workspaceFolder": "/workspaces/project-a-node-js"
}
{
"service": "project-b-node-js",
"workspaceFolder": "/workspaces/project-b-node-js"
}
{
"service": "project-c-python",
"workspaceFolder": "/workspaces/project-c-python"
}
The "shutdownAction": "none"
option will leave the containers running when VS Code closes -- which prevents you from accidentally shutting down both containers by closing one window (or switching containers).
Build and Switch Dev Containers
In VS Code, open the root-level folder (dev-container
in my demo). Run Dev Containers: Reopen in Container
from the Command Palette and select the project to build.
This triggers the Dev Container build for the selected project.
To switch between projects, use Dev Containers: Switch Container
from the Command Palette and select the desired project.
After each Dev Container is built, we can use this Switch Container
command to switch between projects, the current VS Code window will reload each time and connect to the selected Dev Container.
Demo Preview
In my demo, I have three projects: two Node.js
projects and one Python
project, each starts an HTTP server with a web page.
When visited, the projects write "visiting records" to the shared database and display all records on the page.
And remember the port mapping we mentioned before?
Top comments (8)
Hi, nice article series.
One question, when being in the Dev Container the Git Repo is not being recognized. Currently I have two projects in one Repo. When I enter the container for the first project I don't see the changes I made and most importantly I cannot commit on the CLI of the Container. Furthermore how to push to github then?
Hi, thanks for reading and sorry for taking so long to get back to you.
First, if you want to push to Github, configure your GitHub Credentials in the Dev Container(s).
Second, if Git is installed in the Dev Container, are there any specific error/warning outputs when you run
git status
in the container?Take my projects for example, I have 3 projects in one Repo on the host machine:
And they're mounted to the Dev Container's
/workspaces
dir:The issue I faced was that when I ran
git status
in the container, the Git repo was not recognised correctly until I fixed it by following the tips.I'm not a Git expert and you may not have the same issue with mine, but if you could share the configurations of your Dev Container — especially the
image
andvolumes
sections of your project — we can better figure out what happened.exactly what I was looking for thank you.
One more question though. If I run my container on my local machine the HMR (Hot Module Replacement) of Angular won't work
I doubt it's due to the port mapping inconsistency.
HMR should be using web socket protocol, so I suggest you check the
ws://
connection and its port, which must also be mapped.And one more question...
How would it look like if I had to use https for development? Putting the ssl certificate into the Repo is a bad practice. Generating it with every container build would be annoying because the user has to add the certificate to Trusted Certificates on windows
Good question that I never thought of.
I disagree with generating SSL certs with every container either.
I only have a general idea right now that, say, we might have multiple containers in a local (host) machine, and the host machine is kind of like a reverse proxy, so the SSL cert should be deployed in the host machine.
I just wanted to say thank you for this excellent series on dev containers. Great work!
Thanks!