Skip to content

Raw TCP connections can reach a different Compose project's container despite correct DNS #2672

Description

@nesl247

Describe the bug

On OrbStack 2.2.3, a raw TCP connection from one Docker Compose project can reach a PostgreSQL container belonging to a different Compose project even though DNS resolves to the correct destination IP.

This is not merely stale DNS:

  • getent ahostsv4 returns the intended project-specific primary and replica IPs.
  • The database connection succeeds.
  • select inet_server_addr() reports an address belonging to another container or to the intended project's replica instead of its primary.

This is dangerous because the projects use compatible PostgreSQL credentials and the same database name. A misrouted connection can therefore succeed and allow application reads, fixtures, or migrations to operate against the wrong project's database instead of failing at connection time.

This affects both a long-running Compose service (docker compose exec) and fresh one-off containers (docker compose run --rm).

To Reproduce

The affected environment has multiple instances of the same Compose project. Each instance has:

  • its own default Compose network;
  • a shared external development network;
  • a PostgreSQL primary and replica attached to both networks;
  • project-specific .local domains declared as both default-network aliases and dev.orbstack.domains;
  • an application container attached to both networks.

Sanitized Compose shape:

name: example${WORKTREE:+-${WORKTREE}}

services:
  app:
    networks:
      - default
      - shared

  database:
    image: postgres:18.3
    networks:
      default:
        aliases:
          - ${WORKTREE}.database.example.local
      shared:
    labels:
      dev.orbstack.domains: ${WORKTREE}.database.example.local

  database-replica:
    image: postgres:18.3
    networks:
      default:
        aliases:
          - ${WORKTREE}.database-ro.example.local
      shared:
    labels:
      dev.orbstack.domains: ${WORKTREE}.database-ro.example.local

networks:
  shared:
    external: true

In the affected project, container inspection showed:

project-c primary:
  default network: 192.168.166.5
  shared network:  192.168.136.10

project-c replica:
  default network: 192.168.166.6
  shared network:  192.168.136.13

unrelated project replica:
  shared network:  192.168.136.14

From the long-running project-c application container:

client interfaces:
  192.168.166.3
  192.168.136.12

primary DNS:
  project-c.database.example.local -> 192.168.166.5

replica DNS:
  project-c.database-ro.example.local -> 192.168.166.6

explicit primary-only connection:
  select inet_server_addr() -> 192.168.136.14

explicit replica connection:
  select inet_server_addr() -> 192.168.136.13

The primary-only connection reached 192.168.136.14, which belongs to an unrelated Compose project's replica, despite the primary hostname resolving to 192.168.166.5.

Repeating from a fresh one-off project-c application container:

client interfaces:
  192.168.166.7
  192.168.136.16

primary DNS:
  project-c.database.example.local -> 192.168.166.5

replica DNS:
  project-c.database-ro.example.local -> 192.168.166.6

explicit primary-only connection:
  select inet_server_addr() -> 192.168.136.13

explicit replica connection:
  select inet_server_addr() -> 192.168.136.13

In that case the primary connection reached project-c's replica instead of its primary.

The primary measurement uses a separate primary-only connection and does not use automatic read-to-replica routing.

The exact wrong destination varies by client container/flow. This appears to depend on stale network state; I have not yet reduced the trigger to a clean two-container reproducer.

Expected behavior

A TCP connection to 192.168.166.5:5432 must reach the container that owns 192.168.166.5.

Docker networks and Compose projects must remain isolated. A connection must never be transparently redirected to another project's container, regardless of whether that container accepts the same credentials.

Diagnostic report (REQUIRED)

https://orbstack.dev/_admin/diag/orbstack-diagreport_2026-08-25T15-25-40.805167Z.zip

Screenshots and additional context

OrbStack:        2.2.3 (2020300)
OrbStack commit: c83556b0ef8f1ba9a33abbb194622b6b7a1c0307
macOS:           26.6.2 (25G83), arm64
Docker:          29.4.0
Docker Compose:  v5.1.2

This may be related to #1944, which concerned cross-network container communication, but this report additionally shows the correct DNS destination being transported to a different container.

It is distinct from #2576, fixed in 2.2.2. That issue involved one-off containers stealing HTTP custom-domain routing; this reproduces on 2.2.3 with raw PostgreSQL TCP connections and affects both long-running and one-off clients.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions