Port forward with DevContainer and Docker Compose fails
The Problem
You need multiple containers for your development setup and use a docker-compose.yml
file to declare the requirements for your services.
After starting the setup you see the port for one service in the VSCode ports tab but none of the ports from the other service containers.
If you have no time for background information, jump directly to the working solution.
Example Setup
Let's define a simple example setup to explore the problem space. Accessing services in development differs from the requirements in production.
We have a typical node application with two other required services with a docker-compose.yml
setup:
1services:2 service_a:3 build:4 context: ../service_a/5 dockerfile: Dockerfile6 volumes:7 - ../service_a:/workspace:cached8 service_b:9 build:10 context: ../service_b/11 dockerfile: Dockerfile12 volumes:13 - ../service_b:/workspace:cached14 working_dir: /workspace15 db:16 image: postgres:latest17 restart: unless-stopped18 volumes:19 - postgres-data:/var/lib/postgresql/data20 environment:21 POSTGRES_PASSWORD: postgres22 POSTGRES_USER: postgres23 POSTGRES_DB: postgres24volumes:25 postgres-data:
VSCode will allow us to add a devcontainer.json
setup based on this file which will look like this:
1// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:2// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/javascript-node-postgres3// Update the VARIANT arg in docker-compose.yml to pick a Node.js version4{5 "name": "Node.js & PostgreSQL",6 "dockerComposeFile": "docker-compose.yml",7 "service": "service_a",8 "workspaceFolder": "/workspace",910 // Configure tool-specific properties.11 "customizations": {12 // Configure properties specific to VS Code.13 "vscode": {14 // Add the IDs of extensions you want installed when the container is created.15 "extensions": [16 "dbaeumer.vscode-eslint"17 ]18 }19 },2021 // Use 'forwardPorts' to make a list of ports inside the container available locally.22 // This can be used to network with other containers or with the host.23 // "forwardPorts": [3000, 5432],2425 // Use 'postCreateCommand' to run commands after the container is created.26 // "postCreateCommand": "yarn install",2728 // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.29 "remoteUser": "node"30}
As part of the assisted setup we will be asked for the main service which will be added as "service": "service_a",
to the config.
Let's run this setup with DevContainer: Rebuild and Reopen in Container
and look at the options we get.
- we see only the files of the
service_a
in the explorer panel of VS Code:[VS Code - Remote Container started] - starting the node app
node index.js
will make VS Code detect the port and offers the option to preview the port in the browser:[node app started / browser preview dialog] - switching to the
PORTS
tab will show the currently forwarded ports:[VS Code forwarded Ports tab]
How is everthing related
Based on the current setup:
- VS Code is only installed in the main container which is
service_a
- all ports are only defined with
EXPOSE xxx
in the docker files of the services - all ports are unbound
- only the ports from the main container are auto-detected and for devcontainer port forwarding
- Port from other containers need to be defined with
"host:port"
indevcontainer.json
portFoward
directive.
How can this be fixed?
IMPORTANT: host names are only valid with character a-z0-9 and the -
dash. All other character e.g. _
underline create unvalid hostname ! Check you docker-compose.yml
service names which become hostnames to only contain vaild characters!
(A) devcontainer.json / portFoward
This solution is currently (Nov 2022) not supported by Github Codespaces!
- fix the service names in
docker-compose.yml
. Changeservice_a
toservicea
andservice_b
toserviceb
. - in
devcontainer.json
uncomment theportFoward
directive and change to:
Use the format1"forwardPorts": [2 3000,3 "serviceb:3000",4 "db:5432"5],"host:port"
to specify ports on none main containers. - Optional you can enrich the display of the ports with better lables or define additional settings - e.g. UPC:1"portsAttributes": {2 "3000": {3 "label": "Service A (Main)"4 },5 "serviceb:3000": {6 "label": "Service B"7 },8 "db:5432": {9 "label": "Database Postgress"10 }11},
Diagram of the port mapping:
The green ports are defined by VS Code which tries to do a 1:1 from the ports of the containers but will increase the port if containers use the same port and will use a different port if the port is used by an other service on the local computer.
You find all relevant code for the example in my Github repository
VS Code with all fowarded ports, database access and previews of both services:

(B) Ports mapping
This solution does not work with Github Codespaces and any setup where docker is not installed on the local computer!
Use a ports:
mapping in the docker-compose.yml
file to make the required ports available on the host system e.g.:
1db:2 ports:3 - "5432:5432"
This does work with Docker Desktop (MacOS, Windows, Linux) where VS Code and Docker run on the same computer but will fail on setups where docker runs on a different host or in the cloud (vscode.dev). Mapping is not visible in the VS Code ports tab.
(C) IP sharing
WARNING: The IP sharing setup cannot handle container which expose their service on the same port. This needs a fallback to docker compose
Use the IP address of one container db
for different containers with the very bad documented option network_mode: service:[service name]
,
which needs to be added to all containers network_mode: service:db
except for the service db
:
1services:2 service_a:3 build:4 context: ../service_a/5 dockerfile: Dockerfile6 volumes:7 - ../service_a:/workspace:cached8 network_mode: service:db9 service_b:10 build:11 context: ../service_b/12 dockerfile: Dockerfile13 volumes:14 - ../service_b:/workspace:cached15 working_dir: /workspace16 network_mode: service:db17 db:18 image: postgres:latest19 restart: unless-stopped20 volumes:21 - postgres-data:/var/lib/postgresql/data22 environment:23 POSTGRES_PASSWORD: postgres24 POSTGRES_USER: postgres25 POSTGRES_DB: postgres26volumes:27 postgres-data:
to make the port always forwarded (optional) add this to your devcontainer.json
:
1"forwardPorts": [3001,3002,5432],
Tipp: you can specify the exact attributes of a port and add a description with the parameter portsAttributes
.
1"portsAttributes": {2 "3001": {"label": "Service A (Main)"},3 "3002": {"label": "Service B"},4 "5432": {"label": "Database Postgress"}5},
Diagram of the port mapping:
Github repository (branch c-ip-sharing-example
) with example Code & Setup
VS Code with all fowarded ports, database access and previews of both services:

(D) Other options
- TCP Port forwarding - e.g. adding the following script in
devcontainer.json
topostCreate
:1socat TCP4-LISTEN:<PORT-TO-FORWARD>,reuseaddr,fork TCP:<SERVICE-NAME>:<PORT-TO-FORWARD> &2socat TCP4-LISTEN:3306,reuseaddr,fork TCP:cs-mysql:3306 & - start a container with a http proxy e.g. Tinyproxy and wire up all existing secondary container. This will allow to simulate a production environment with all related production domains.
- use a revers proxy as ingress - e.g. Traefik
Did it work for you - do you have a setup which is not covered?