I have containers managed by separate Compose projects. A container from one project should only be able to communicate with a container from the other project when both are attached to the same user-defined bridge network. However, it seems that containers on unrelated bridge networks can still reach services from the other project through their published host ports. Is this expected behavior, and what is the correct way to keep these services isolated while allowing only the intended cross-project communication?
3 Answers
User-defined bridge networks are isolated from one another. The part bypassing that isolation is the published port: a Compose entry such as `ports: - "8080:8080"` binds the service to the host, often on `0.0.0.0`. Another container can then connect through the host address and published port, which leaves and re-enters through the host instead of using the bridge directly. Remove `ports:` from services that should be internal-only. Create one shared user-defined network, mark it as `external: true` in both Compose files, and have the containers connect using the service name and container port. Only publish ports for services that genuinely need host or LAN access.
For services that should have no outside route, you can also define the relevant network with `internal: true`. A typical setup is to keep the database and API on a private internal network, then attach the API to a separate shared external network if it needs to communicate with a service managed by another Compose project. `external: true` only means the network is created outside that Compose file; it does not mean the network is exposed to the internet.
You can either place the services in the same Compose project so the network is created automatically, or create a shared network yourself with `docker network create` and reference it from both projects. The key distinction is that sharing a bridge network controls direct container-to-container access, while publishing a port exposes a path through the host that can be reached independently of the container's network membership.

Manually changing firewall rules usually isn't the right first fix here. Docker is creating the host port mapping because it was requested in Compose, so removing the published port and using a shared private network addresses the cause directly.