skip to content

questions

4

Why must a container on a managed platform bind the injected PORT on 0.0.0.0?

level: juniorimportance: must knowfreq 78%

answer

  1. The image does not choose this
  2. It arrives in the environment at start
  3. One interface is private to the container
  4. EXPOSE is metadata, not a bind
  5. Read PORT, listen on 0.0.0.0

basics

~20 s

The platform chooses the port, passes it in as an environment variable, and then connects to the container's IP on that port. An app that hardcodes a different port, or listens only on 127.0.0.1, is unreachable from outside its own network namespace.

solid answer

~50 s

A managed container platform does not ask the image which port it wants; it picks one, injects it as an environment variable (conventionally `PORT`), and sends traffic to the container's address on that port. Two independent mistakes break this. The first is hardcoding the port, so the process listens on 8080 while the platform is dialling 43117 and every request is refused. The second is binding the loopback address: inside the container's own network namespace, `127.0.0.1` is reachable only by processes in that same container, so the platform's connection never arrives even though the process is up. `EXPOSE` does not help with either -- it is metadata, not a bind address and not a publish. The image must read the port from the environment at startup and bind `0.0.0.0` so it accepts connections on the container's real interface.

code

dockerfile · 6 lines
dockerfile
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY build/libs/thumbnailer.jar /app/thumbnailer.jar
ENV PORT=8080
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "exec java -jar /app/thumbnailer.jar --server.port=${PORT} --server.address=0.0.0.0"]

go deeper

for a junior

Be ready to say where the port comes from on a managed platform and to write the two lines that use it: read PORT from the environment, bind 0.0.0.0. Know that EXPOSE is documentation and publishes nothing.

for a middle

Explain the mechanics: each container has its own network namespace, so its 127.0.0.1 is private to it, while 0.0.0.0 covers the routable interface the platform dials. Explain why exec-form ENTRYPOINT does not expand ${PORT}.

for a senior

Show how you diagnose it under time pressure -- confirm the process is up, then confirm what address and port it bound from inside the container -- and how you keep the image portable so the same artifact runs locally and on the platform with no rebuild.

for a principal

Own the convention across a fleet: one variable name, one base image or starter that already honours it, and a build-time or startup check that fails loudly rather than shipping an image that binds the wrong address and only fails once traffic is pointed at it.

### What the platform is actually doing A managed container platform runs your image as a container and puts a router or load balancer in front of it. That router has to know one thing: which TCP port inside the container to connect to. Rather than reading it out of the image, essentially every such platform inverts the relationship -- it allocates the port itself and tells the container, at start time, through an environment variable. The near-universal name for that variable is `PORT`. The image's side of the contract is therefore: read `PORT` at startup, listen on it, and accept connections on the container's routable address. The reason platforms do it this way is that they are packing many containers onto shared hosts and reserving ports out of a pool; letting each image dictate its own port would produce collisions the platform cannot resolve. It also means the same image runs unchanged in every environment, which is the whole point of shipping an image as the deployable unit. ### Mistake one: the hardcoded port A Spring Boot photo-thumbnail service with `server.port: 8080` baked into `application.yml` starts perfectly, logs "Tomcat started on port 8080", and is then declared unhealthy by the platform, which was connecting to 43117. Nothing crashed -- the two sides simply never met. The fix is to let the injected value win: Spring Boot's relaxed binding already maps the environment variable `SERVER_PORT` onto `server.port`, so a platform that injects `PORT` needs that value forwarded, either by starting the JVM with `--server.port=${PORT}` or by setting `server.port: ${PORT:8080}` so the environment overrides a sane local default. ### Mistake two: the loopback bind This one is subtler because the process really is listening. Every container gets its own network namespace with its own loopback interface and its own `eth0`. Binding `127.0.0.1` binds that container's private loopback, which is not the same loopback as the host's and is not reachable from any other network namespace. A process in the container can curl itself and succeed; the platform's router cannot reach it at all. Binding `0.0.0.0` (or `::` for IPv6) means "every interface in this namespace", which includes the container's routable address, and that is what the platform needs. Frameworks differ in their defaults -- Spring Boot binds all interfaces unless you set `server.address`, while several development servers in other ecosystems default to localhost -- so this bites hardest when a team moves a dev-mode entrypoint into an image. The same failure shows up locally, which is how you can rehearse it. Publishing a port with `docker run -p 39001:43117` sets up forwarding from the host into the container's address on 43117; if the process bound only the container's loopback, the connection is accepted by the forwarder and then fails, and you see a connection error rather than a 404. Nothing about `-p` changes what the process bound. ### Why EXPOSE does not save you `EXPOSE 8080` in a Dockerfile is documentation recorded in the image's config. It publishes nothing, opens nothing, and cannot influence which address or port the process inside chooses. Its only mechanical effect is that `docker run -P` (capital P) publishes every exposed port to an ephemeral host port, and that some tooling reads it as a hint. Treating `EXPOSE` as the declaration of "the port my app serves on" is one of the most common junior misreadings of the Dockerfile. ### Writing it correctly Three properties make an image portable here. Read the port from the environment with a default, so the image also runs with a plain `docker run` and no variables set. Bind `0.0.0.0` explicitly rather than relying on a framework default you did not choose. And make sure the variable actually reaches the process: exec-form `ENTRYPOINT ["java", "-jar", "app.jar", "--server.port=${PORT}"]` does **not** expand `${PORT}`, because exec form does not invoke a shell -- the literal string `${PORT}` is passed as an argument. Either use a shell wrapper that `exec`s the real process (so it still becomes PID 1 and still receives signals), or let the framework read the environment variable itself and pass no port argument at all. ### How to diagnose it in ten seconds Get a shell in the container and look at what is listening. If the listening address is `127.0.0.1:43117` you have the bind bug; if it is `0.0.0.0:8080` when the platform injected 43117 you have the hardcoded-port bug; if nothing is listening at all the process failed to start and neither of these is your problem yet.

  • If EXPOSE does not publish a port, what does it do at all?
    It records the port in the image's configuration as documentation. Its one mechanical effect is that `docker run -P` publishes every exposed port to a randomly chosen host port. It does not open a firewall, does not affect `-p`, and cannot change which address or port the process inside binds.
  • Your ENTRYPOINT is exec form and contains --server.port=${PORT}. Why does the app start on the literal wrong port?
    Exec form does not run a shell, so no variable expansion happens; the process receives the eight characters `${PORT}` as an argument. Either wrap it in `sh -c` and `exec` the real process so it still becomes PID 1 and receives signals, or drop the argument and let the framework read the environment variable itself.
  • The platform injects PORT but your framework only reads its own variable name. What is the cleanest fix?
    Map it at startup rather than teaching the platform a new name: pass `--server.port=${PORT}` from a small shell wrapper, or set the config value to `${PORT:8080}` so the injected variable wins and a bare `docker run` still works. Do not hardcode the platform's current allocation into the image.

The platform is a switchboard that assigns you an extension when you arrive and then rings it. Answering on the extension you had at your last job, or picking up an intercom that only reaches your own room, both look like "nobody is answering" from the switchboard's side.

saying these in an interview costs you the question

  • Thinks EXPOSE publishes the port to the host
  • Hardcodes the port and asks the platform to match it
  • Binds 127.0.0.1 and says the app is listening fine
  • Believes -p can reach a loopback-only bind
  • Uses ${PORT} inside exec-form ENTRYPOINT expecting expansion
  • Assumes every framework defaults to all interfaces

context

open as a page

Why should a containerized service write its logs to stdout instead of a file inside the container?

level: middleimportance: should knowfreq 62%

basics

~20 s

The engine captures the main process's stdout and stderr and hands that stream to the platform's collector; that is also what docker logs shows. A file written inside the container is invisible to that path, per-replica, and discarded when the container is replaced.

open as a page

On a managed container platform, why does a service that caches files on the container filesystem break?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Writes land in the container's writable layer, which is private to that container and thrown away when it is replaced. Every replica keeps its own cache, every deploy empties it, and it grows until the platform's ephemeral-storage limit kills the container.

open as a page

Does a Dockerfile HEALTHCHECK instruction control whether a managed platform sends traffic to your container?

level: middleimportance: nice to knowfreq 31%

basics

~20 s

No. HEALTHCHECK is image metadata that the Docker engine acts on: it runs the command inside the container and records a health status you can read with docker inspect. Managed platforms and Kubernetes ignore it and poll an endpoint they are configured with themselves.

open as a page