Forwarding and securing ports in DevContainers
Port forwarding is what lets you open localhost:3000 on the host and reach a service inside the container — but the defaults can also auto-forward ports you didn’t intend, and in Codespaces a misconfigured port can become publicly reachable. This page forwards exactly the ports you want, labels them, and locks down visibility. It extends the Understanding the DevContainer Specification cluster.
Prerequisites
- A
devcontainer.jsonand a service that listens on a known port - VS Code Dev Containers extension or the devcontainer CLI
How-To Steps
-
Declare the ports you want forwarded explicitly rather than relying on auto-detection:
{ "image": "mcr.microsoft.com/devcontainers/javascript-node:20@sha256:7c3e...", "forwardPorts": [3000, 5432], "remoteUser": "vscode" } -
Annotate each port with a label and an auto-forward behaviour via
portsAttributes:{ "portsAttributes": { "3000": { "label": "web", "onAutoForward": "notify" }, "5432": { "label": "postgres", "onAutoForward": "silent" } } } -
Suppress unwanted auto-forwarding so stray listeners don’t get exposed:
{ "otherPortsAttributes": { "onAutoForward": "ignore" } } -
Keep services bound correctly. For host access bind
0.0.0.0; for container-internal-only services keep them on127.0.0.1and do not list them inforwardPorts. Verify what’s reachable:devcontainer exec --workspace-folder . ss -ltnp # see listening sockets curl -fsS http://localhost:3000/health # forwarded port works from host -
In Codespaces, confirm visibility is private (the default). Public visibility makes a port reachable by anyone with the URL — only set it deliberately.
Common Pitfalls
| Symptom | Root Cause | Remediation |
|---|---|---|
| Unexpected ports forwarded | Auto-forward detected a listener | Set otherPortsAttributes.onAutoForward to ignore |
| Forwarded port refuses connection | Service bound to 127.0.0.1 | Bind 0.0.0.0 for host-reachable services |
| Codespaces port publicly reachable | Visibility set to public | Reset port visibility to private |
| Port label missing/confusing | No portsAttributes entry | Add a label per forwarded port |
Conclusion
The invariant: forward only the ports you name, label them, and default everything else to ignore. Combine explicit forwardPorts with correct bind addresses and private Codespaces visibility, and you expose exactly the surface you intend — nothing more.
FAQ
What’s the difference between forwardPorts and appPort?
forwardPorts maps container ports to the host through the IDE’s forwarding and is the modern, spec-aligned approach. appPort publishes via Docker directly and bypasses the IDE’s visibility controls — prefer forwardPorts.
Why is a port auto-forwarded even though I didn’t list it?
The IDE detects new listening sockets and forwards them by default. Set otherPortsAttributes.onAutoForward to ignore to disable that behaviour for unlisted ports.
Related
- Understanding the DevContainer Specification — the parent cluster.
- Debugging Network & DNS Issues in Containers — when a forwarded port still won’t connect.
- devcontainer.json Property Reference — the
forwardPortsandportsAttributesproperties.