DEV Community

Cover image for Dev Containers - Part 5: Multiple Projects & Shared Container Configuration
graezykev
graezykev

Posted on

Dev Containers - Part 5: Multiple Projects & Shared Container Configuration

Welcome to the fifth guide in the Dev Container series:

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
Enter fullscreen mode Exit fullscreen mode

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-...
Enter fullscreen mode Exit fullscreen mode

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:
Enter fullscreen mode Exit fullscreen mode

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-...
Enter fullscreen mode Exit fullscreen mode

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:  
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

set workspaceFolder for the right dev container

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"
}
Enter fullscreen mode Exit fullscreen mode
{
  "service": "project-b-node-js",
  "workspaceFolder": "/workspaces/project-b-node-js"
}
Enter fullscreen mode Exit fullscreen mode
{
  "service": "project-c-python",
  "workspaceFolder": "/workspaces/project-c-python"
}
Enter fullscreen mode Exit fullscreen mode

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.

choose dev container 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.

preview

And remember the port mapping we mentioned before?

visiting records

Top comments (8)

Collapse
 
tech1ead profile image
Fabian • Edited

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?

Collapse
 
graezykev profile image
graezykev • Edited

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:

/path/to/dev-container
│
├── .git
├── .devcontainer
├── README.md
├── project-a-node-js
├── project-b-node-js
└── project-c-python
Enter fullscreen mode Exit fullscreen mode

And they're mounted to the Dev Container's /workspaces dir:

/workspace
│
├── .git
├── .devcontainer
├── README.md
├── project-a-node-js
├── project-b-node-js
└── project-c-python
Enter fullscreen mode Exit fullscreen mode

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.

Image description

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 and volumes sections of your project — we can better figure out what happened.

Collapse
 
tech1ead profile image
Fabian

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

Thread Thread
 
graezykev profile image
graezykev

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.

Thread Thread
 
tech1ead profile image
Fabian

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

Thread Thread
 
graezykev profile image
graezykev

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.

Collapse
 
ds_devto profile image
DS

I just wanted to say thank you for this excellent series on dev containers. Great work!

Collapse
 
graezykev profile image
graezykev

Thanks!