Inside a container, a worker connects to localhost to reach a database in another container - why does that fail?
answer
- laptop habit, not a host process
- each container gets its own view
- loopback is per view
- localhost means this container only
- address the neighbour by name
basics
~20 sEach container normally gets its own network view: its own interface, its own address and its own loopback. So localhost inside a container means that container itself, and nothing is listening there. Reach the other container by its address or its name.
solid answer
~40 sA containerised process is not just another process on the host's network. The platform normally builds each container a private network view holding one virtual interface, one address drawn from a workload range, its own loopback and its own port space. `localhost` and `127.0.0.1` resolve inside that view, so a call to `localhost:7000` is a call to *this* container's loopback, where only what this container bound is listening. The database sits on a different loopback in a different view, so the call is refused straight away rather than routed anywhere. The fix is to address the neighbour properly: the name the platform resolves to it, or its own address. The trap is pure local-machine habit - on a laptop every process shares one loopback, so `localhost` happened to work for everything.
go deeper
Recall that a container normally has its own address and its own loopback, so localhost inside it means that container. Reach a neighbour by the name that resolves to it, not by localhost.
Explain what the private view holds - one interface, one address, one loopback, one port space - and why the call is refused locally rather than routed to the host or to a neighbour.
Recognise this as an incident symptom: configuration carried over from a developer machine still says localhost, and the failure looks like a dead dependency. Be able to separate refused-here from unreachable-there.
State the trade-off plainly: per-workload views buy free port choice, a portable workload identity and one place to apply rules, at the cost of invalidating every local-machine assumption in inherited configuration.
## What a container is given, network-wise When a platform starts a container it normally does not run the process directly on the host's networking. It builds that container a **private network view** and runs the process inside it. The view is a small but complete version of a machine's networking, and it usually contains: - one **virtual interface** - one end of a virtual interface pair, with the other end left on the host so traffic has somewhere to go; - one **address** on that interface, taken from a range the platform manages for workloads; - its own **loopback** interface, the one reachable as `localhost` and `127.0.0.1`; - its own **port space**, its own routing table, and its own idea of what is listening. Nothing in that list is shared with the container beside it. Network-wise, two containers on one host behave like two machines that happen to sit in the same box. ## Why localhost is the first habit to break On a developer machine every process shares one loopback. The application, the database and the cache all bind there, and `localhost:<port>` reaches any of them, because the machine has exactly one loopback and one port space. Move those same processes into containers and that single loopback splits into one per view. `localhost` stops meaning *this machine* and starts meaning **this view**. The worker's call is delivered to the worker's own loopback, where the only listeners are the ones the worker itself bound. The database is listening on a different loopback inside a different view, and the packet never goes near it. That is why configuration copied straight off a laptop fails on its first run in a container, and why the failure reads as a dependency outage: nothing is down, nothing is wrong on the database side, and the address looks correct. ## Reading the failure | The call | Where it is delivered | Result | |---|---|---| | `localhost:7000` from the worker | the worker's own loopback | refused - nothing is bound there | | the database container's address, port 7000 | the database container's interface | connects, if a route and the rules permit | | the host's address, port 7000 | the host's own network view | reaches only what the host itself has listening; making a container's port answer there is a separate mechanism | The difference between **refused** and **timed out** is worth internalising. A refusal is an answer: the local stack checked its own port space, found nothing bound, and said so immediately. A timeout means the packet left and nobody replied - a wrong address, a missing route, or a rule that dropped it. A `localhost` mistake produces the first, almost instantly, which is a strong hint that the process is talking to itself. ## Reaching the other container Three shapes, in rough order of how often they are the right one: 1. **Use the name the platform resolves.** Platforms hand workloads names that resolve to an address, so a caller never needs to know where anything landed. How that name keeps working while replicas are replaced is its own subject. 2. **Use the neighbour's address.** This works, but the address is assigned by the platform and changes when the container is replaced, so it belongs in a debugging session rather than in a configuration file. 3. **Put both containers in one shared network view.** They then genuinely share a loopback and `localhost` works again - at the price of one shared port space between them. What never works is keeping `localhost` and hoping. No platform rewrites a loopback call into a call to a neighbour, because it cannot know which neighbour was meant. ## Why the model is built this way A per-container view costs one broken habit and buys three things: - **Free port choice.** Every workload binds whatever port it likes, because the number is scoped to its own view rather than to the machine. - **A workload-shaped identity.** The unit that has an address is the workload, not the host, so a caller's target does not change meaning when the workload lands on a different host. - **A boundary worth writing rules about.** Traffic in and out of a view is a place where the platform can apply permission rules, because there is exactly one door. The escape hatches exist on purpose. A container can be placed in the **host's own network view**, in which case `localhost` really does mean the host and port choice starts colliding with everything else on the machine. Several containers can be placed in **one shared view**, which is exactly how a helper reaches an endpoint that is bound to loopback. Both are deliberate choices, and both trade the isolation described above for a specific convenience.
- The call fails instantly with a refusal rather than hanging - what does that tell you?That something answered. The container's own stack looked in its own port space, found nothing bound on that port, and refused immediately. A hang or timeout would mean the packet left and got no reply, which points at a wrong address, a missing route or a rule dropping it. An instant refusal on a loopback address usually means the process is talking to itself.
- Does a container always get a network view of its own?No. A platform can run a container in the host's own network view, where it shares the host's interfaces, addresses and port space - `localhost` then does mean the host, and port choice collides with everything else on the machine. Platforms can also place several containers in one shared view deliberately, so that they see one loopback between them.
- Why is hardcoding the neighbour's address a bad fix even though it works once?Because the address belongs to that container instance, not to the workload. When the container is replaced the address usually changes, and the configuration now points at something that no longer exists. A name the platform resolves survives replacement; an address written into a config file does not.
Two offices in one building each run their own internal phone system. Dialling extension 100 from one office reaches that office's own front desk, never the neighbour's - the number means 'here', not 'this building'.
saying these in an interview costs you the question
- Thinks localhost inside a container reaches services on the host
- Believes every container on one host shares a single loopback
- Reads the refusal as the other container being down
- Blames a firewall rule for a call that never left the container
- Expects the platform to redirect a loopback call to a neighbour