Skip to main content
  1. Posts/

Stir Trek 2025 and Multiple Dev Containers

Chris Ayers
Author
Chris Ayers
I am a father, nerd, gamer, and speaker.
Stir Trek 2025

This month at Stir Trek 2025, I presented on Dev Containers and GitHub Codespaces, demonstrating how these tools streamline both local and cloud-based development workflows. The session covered the essentials of creating portable development environments, customizing containers with features and extensions, and launching Codespaces directly from your repository. A lively Q&A followed, with attendees asking about strategies for running and working with multiple containers. Below, I’ve distilled those discussions and provided a deeper dive into shared container configurations across multiple projects-including folder structures, Docker Compose setups, VS Code workflows, and advanced tips you can apply in your own work.

Recap: Dev Containers and Codespaces
#

Dev Containers

Dev Containers are Docker-based environments enriched with development-specific tooling, settings, and startup tasks as defined in a devcontainer.json file. They enable you to use a container as a full-featured development environment-isolating dependencies, standardizing tool versions, and enabling reproducible setups locally or remotely (Dev Containers).

GitHub Codespaces builds on Dev Containers by providing cloud-hosted environments that spin up in seconds with configurable CPU, memory, and storage. Codespaces leverages the same open specification as Dev Containers, making your devcontainer.json a first-class citizen whether you connect via VS Code, IntelliJ, or directly in the browser (GitHub Docs).

Q&A: Running Multiple Containers
#

Can I connect to multiple containers at once?
#

By default, VS Code allows only one container per window, but you can open additional windows and attach each to a different container to work on multiple services in parallel (Visual Studio Code).

What about using a single window for multiple containers?
#

Docker Compose

If you use Docker Compose, define multiple services in your docker-compose.yml and create separate devcontainer.json configurations for each service-each referencing the common Compose file. VS Code will then list each configuration in its Dev Container picker, letting you reopen the current window to connect to a different service without duplicating your Compose setup (Dev Containers).

How do I configure separate containers for multiple projects?
#

To maintain isolation and clarity, place each container configuration in its own subdirectory under .devcontainer, following the pattern .devcontainer/<project>/devcontainer.json. Tools supporting the spec recognize this layout and list all found configurations in the Codespaces or VS Code Dev Container dropdown (containers.dev, GitHub Docs).

Shared .devcontainer Folder Structure
#

Centralize your container configurations in a single root directory:

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-a-node-js/
├─ project-b-node-js/
├─ project-c-python/
└─ project-d-go-lang/

This layout lets all projects share a single Compose definition and environment variables-reducing duplication and easing updates (containers.dev).

Common Docker Compose File
#

In .devcontainer/docker-compose.yml, define every project service alongside shared dependencies:

services:
  project-a-node-js:
    image: mcr.microsoft.com/devcontainers/base:latest
    volumes:
      - ..:/workspaces:cached
    ports:
      - "8001:8000"
    command: sleep infinity

  project-b-node-js:
    image: mcr.microsoft.com/devcontainers/base:latest
    volumes:
      - ..:/workspaces:cached
    ports:
      - "8002:8000"
    depends_on:
      - postgres

  project-c-python:
    image: mcr.microsoft.com/devcontainers/base:latest
    volumes:
      - ..:/workspaces:cached
    ports:
      - "8003:8000"
    depends_on:
      - postgres

  project-d-go-lang:
    image: mcr.microsoft.com/devcontainers/base:latest
    volumes:
      - ..:/workspaces:cached
    ports:
      - "8004:8000"

  postgres:
    image: postgres:latest
    volumes:
      - postgres-data:/var/lib/postgresql/data

volumes:
  postgres-data:

Each service listens on port 8000 internally and is mapped to a unique host port (8001-8004) to prevent collisions (Visual Studio Code).

Workspace Mounts & Folder Mapping
#

Mounting the root-level folder into /workspaces in each container gives uniform access to all projects. In each devcontainer.json, point workspaceFolder at the specific subdirectory:

"workspaceFolder": "/workspaces/project-b-node-js"

This ensures your editor is scoped appropriately when connected (Dev Containers).

Per-Project devcontainer.json
#

Each project’s configuration references the shared Compose file and specifies its service:

{
  "name": "Project B Dev Container",
  "dockerComposeFile": ["../docker-compose.yml"],
  "service": "project-b-node-js",
  "workspaceFolder": "/workspaces/project-b-node-js",
  "shutdownAction": "none"
}

Using "shutdownAction": "none" keeps all containers running when you close one window, so you don’t inadvertently tear down shared services (Dev Containers).

Building & Switching Between Containers
#

  1. Open the root folder (dev-container/) in VS Code.
  2. Reopen in Container: Run Dev Containers: Reopen in Container and select your desired project.
  3. Switch Container: Later, use Dev Containers: Switch Container to hop to another project without restarting the Docker stack (Visual Studio Code, GitHub Docs).

Advanced Multi-Project Strategies
#

Environment-Specific Overrides
#

Layer additional Compose files for environment-specific tweaks:

docker-compose -f docker-compose.yml \
  -f docker-compose.override.yml \
  -f docker-compose.dev.yml up -d

Overrides can redefine images, ports, mounts, or feature flags per environment (Visual Studio Code).

Isolated Networks & Namespaces
#

Define separate Docker networks to segment traffic:

networks:
  dev-a: {}
  dev-b: {}

services:
  project-a-node-js:
    networks: [dev-a]
  project-b-node-js:
    networks: [dev-b]
  postgres:
    networks: [dev-a, dev-b]

This prevents unintended inter-service communication between project environments (Visual Studio Code).

GitHub Codespaces Integration
#

Codespaces recognizes the same .devcontainer layout:

  • Configuration Dropdown: Multiple devcontainer.json files under .devcontainer/ are automatically listed when creating a Codespace (GitHub Docs).
  • Port Forwarding: Host-mapped ports (8001-8004) surface as forwarded ports in the Codespaces UI.
  • Pre-builds & Secrets: Enable pre-builds in devcontainer.json and leverage repository or organization secrets instead of a local .env file (The GitHub Blog).

Lifecycle Customization
#

Use Dev Container lifecycle hooks per project to automate setup:

"postCreateCommand": "cd /workspaces/project-a-node-js && npm ci",
"postStartCommand": "npm run migrate",
"initializeCommand": "git submodule update --init"

These commands ensure each container is fully prepared for development immediately (Dev Containers).

Troubleshooting Tips
#

  • Stuck at “Rebuilding container…”: Clear the Docker build cache or raise VS Code’s Docker logging level.
  • Ports not forwarding: Verify forwardPorts in devcontainer.json or check Codespaces port settings.
  • Volume performance issues: On macOS/Windows, consider isolating cache directories (e.g., node_modules) in named volumes to speed up I/O (Some Natalie’s corner of the internet, pamela fox’s blog).

Conclusion
#

By centralizing Dev Container configurations and sharing a unified docker-compose.yml, you eliminate duplication, streamline dependency management, and enable seamless switching between multiple projects-both locally and in GitHub Codespaces. This pattern scales from a handful of services to extensive microservice landscapes, delivering consistent, reproducible developer environments across your entire workspace.

Related

Copilot or Rubber Ducky?

·1623 words·8 mins
Rubber Duck Debugging in the Age of AI # Ever found yourself explaining your code to a little rubber duck perched on your desk? If so, you’re in good company. This quirky practice, known as rubber duck debugging, has helped countless developers articulate problems and discover solutions. The idea is simple: by forcing yourself to explain your code, line by line, to an inanimate duck, you often stumble upon the bug or insight you needed. It’s like having a silent pair programmer who patiently listens as you work through the logic. But what if your rubber duck could talk back and offer suggestions? Modern AI coding assistants (like GitHub Copilot) are becoming the new interactive rubber ducks, and that means a lot for debugging, brainstorming ideas, and reviewing code.

Writing Regex with Copilot

·754 words·4 mins
I had a real world example today that I wanted to share about copilot helping me with a coding problem. A friend reached out asking if I knew regex. Its been a bit, but I was happy to help because I thought we could figure it out.

Containerizing .NET - Part 2 - Considerations

·1976 words·10 mins
This is part 2 of the Containerizing .NET series. You can read the series of articles here: Containerizing .NET: Part 1 - A Guide to Containerizing .NET Applications Containerizing .NET: Part 2 - Considerations Considerations # Welcome to the second installment in our series on containerizing .NET applications. Building on the foundation laid in our first article-where we introduced Dockerfiles and the dotnet publish command-this piece delves into pivotal considerations for transitioning .NET applications into containers. As containers become a cornerstone of the ecosystem, understanding these factors is critical for developers aiming to enhance application deployment in containerized environments.

Dev Containers - Part 1

·1724 words·9 mins
This article is part of the Festive Tech Calendar 2023. For more articles in the series by other authors, visit https://festivetechcalendar.com/. Dev Containers can revolutionize the way we approach development environments, offering a fast, consistent setup across different projects. As a developer who uses Dev Containers in VS Code for various projects, I’ve experienced firsthand the benefits of having an environment that’s ready to go as soon as I clone a project.