devops
Three container networks, so the database has one neighbour
Three container networks, so the database has one neighbour
By default Docker Compose puts every service on one network and they can all reach each other. For a handful of containers that's fine. Once the stack is a database, a connection pooler, a cache, an image service, an API, three static frontends, a reverse proxy and a tunnel client, "everything can talk to everything" is a decision you made by not making one.
The production topology here is three bridge networks:
front— the static SPA containers and the reverse proxy. Internal, no host ports.edge— the reverse proxy, the API, the cache, the tunnel client. This is where outside traffic lands and gets routed.data— the connection pooler, the database, the image service. Internal.
The API is the only service on both edge and data. Nothing on front can address the database at all — there is no route. A frontend container that somehow ran attacker code still can't open a socket to Postgres, because they share no network.
Why bother when the app-layer auth is already there
Because defence in depth means a single failure isn't total. If the image service has a parsing bug and gets popped, the question "what can it now reach" should have a short, boring answer — and with it sitting alone on data with the pooler and the database, the answer is legible off the topology diagram instead of requiring an audit of every service's outbound calls.
It also forces you to be explicit. Putting the image service on data meant deciding, on purpose, that the API bridges to it — rather than it drifting onto the same flat network as the public frontends because that's where docker compose puts things by default.
Pair it with per-container memory limits. A leaking or attacked container that can't exhaust the host's RAM is a degraded service; one that can is an outage for everything else on the box.