Reaching external services
Connect a containerized app to PostgreSQL, MySQL, or Redis running outside the container.
Some apps the agent clones expect a database or cache that runs outside the container — PostgreSQL, MySQL, Redis. SQLite needs none of this: it's a file on a mounted volume, not a network service, which is why SQLite-backed apps just work. The clients are already in the image (libpq, mysql2, hiredis), so connectivity — not a missing driver — is the only thing to get right.
The one rule that causes most failures
localhost and 127.0.0.1 mean the container itself — its own loopback. They never mean the host the container runs on.
So any cloned app whose config says redis://localhost:6379 or postgres://localhost:5432 fails in a container unless that service also runs inside the same container. Everything else here is just: what address do you use instead, and is the path open.
Two things to check every time
- Addressing
- Does the app's config point at something other than localhost?
- Reachability
- Is the listening service bound to an address reachable from outside its own loopback, and does its firewall allow the connection?
Case 1 — service on the same host
The service runs on your Mac and the container is local. Both checks apply at once: name the host correctly, and make sure the host service accepts the connection.
Addressing — how the container names the host
host.docker.internal — it resolves automatically..1, e.g. 192.168.64.1). Find it inside with ip route | grep default.--add-host host.docker.internal:host-gateway, or use the bridge gateway (commonly 172.17.0.1).Reachability — the host service must accept the connection
Correct addressing isn't enough — a service bound to localhost only will still refuse the container, because from the host's view the connection arrives from the VM or bridge IP, not loopback.
-
PostgreSQL: set
listen_addresses(e.g.'*') inpostgresql.conf, plus a matchingpg_hba.confline for the container subnet. -
Redis:
bindmust include a non-loopback address. If you open it, setrequirepass. - The host firewall must allow the container subnet.
Case 2 — service in another container
When the database or cache runs in its own container, address it by name — not by localhost, which only ever reaches the container's own loopback.
-
Same user-defined network: address by container or service name via built-in DNS, e.g.
redis://redis:6379. The internal port is used directly — published-pports are irrelevant container-to-container. This is the normal, portable case. - Different networks or runtimes: they can't see each other by name. Put them on a shared network, or fall back to host-IP routing (Case 1) via the host's published port.
- A sibling on a shared network needs no host-gateway tricks — the connection never leaves the container network.
Case 3 — remote or managed service
The simplest case. Use the real hostname or IP — postgres://user:[email protected]:5432/..., redis://cache.internal:6379 — and the container makes an ordinary outbound connection. No host-gateway tricks needed.
- The container has egress (the default), so outbound TCP works as usual.
- DNS resolves the hostname.
- The remote service's firewall must allow the container's egress IP — which after NAT is typically the host's IP, not the container's internal IP. This matters for IP allowlists on RDS or managed Redis.
- TLS is in place if the service requires it.
Quick reference
host.docker.internalHost service binds a non-loopback address.
ip route)Host service binds a non-loopback address.
host-gateway / bridge IPHost service binds a non-loopback address.
Both on a shared user-defined network.
Egress allowed; remote firewall allows the host's NAT IP.