On an empty base image, a binary that ran fine on the build host exits immediately — why?
answer
- not as self-contained as it looks
- what gets resolved at start-up
- the build host supplied them silently
- dynamic dependencies with nothing to resolve them
- link statically or match the library family
basics
~20 sThe artifact is linked dynamically against system libraries the build host supplied and the empty base does not. Nothing resolves its dependencies at start-up, so it dies before the first line of its own code runs.
solid answer
~50 sA dynamically linked artifact is not self-contained: it names the libraries it needs and something resolves them from the filesystem each time it starts. On the build host those libraries were simply there, so nobody noticed the dependency. An empty base ships none of them, and a minimal base ships a small set that may come from a different library implementation family than the one the artifact was compiled against — same names, incompatible contents. Either way the failure lands before your code runs, which is why there is no application log to read. The three fixes are: link the artifact statically so nothing is resolved at run time; copy the exact libraries into the image; or choose a base whose library family matches the toolchain that produced the artifact. The cheap guard is to start the final image in the pipeline before publishing it.
code
pseudocode · 20 lineson container exiting immediately after start:
needed = dynamic dependencies declared by the artifact
present = system libraries inside the final image
missing = needed - present
if missing is not empty:
# the empty or minimal base supplies none of these
fix = copy each library in missing into the image
or rebuild the artifact statically linked
else if needed is not empty:
# every name is present, so the names are not the problem
fix = rebuild against the library implementation family
that this base ships, or link statically
else:
# nothing is resolved at start-up, so look elsewhere
fix = check the declared start command: a shell-style
line needs a shell this image does not havego deeper
Know that a dynamically linked program needs its libraries present in the image, and that the build host had them so nobody noticed. An immediate exit with no application log points there first.
Explain resolution at start-up and both shapes of the failure — libraries absent, and libraries present from a different implementation family — plus the three fixes and what each one costs you afterwards.
Demonstrate the guard: list the artifact's dynamic dependencies, diff against the base, and start the final image in the pipeline. Say clearly that the fix is a rebuild, never a change applied to a running container.
The trade-off you own is whether the estate links statically and takes rebuild cost on every library fix, or runs on a matched base family and takes coupling between build toolchain and base instead.
## Why a binary needs the image to supply anything An executable can be built two ways. **Statically linked**, everything it needs is inside the file, and it will start on an empty filesystem. **Dynamically linked**, the file names the shared libraries it requires and defers them: every time it starts, something walks that list, finds each library on the filesystem, maps it in, and only then transfers control to the program's own entry point. That second arrangement is the default in most toolchains, because it saves space and lets libraries be patched without rebuilding every program that uses them. The cost is that the executable is **a contract with its surroundings**, and moving it into an image changes the surroundings completely. On the build host, the surroundings were a full userland with the libraries already present. Inside an empty base there is nothing, so the resolution step fails and the process dies **before its first line runs**. This is why there is no application log, no start-up banner, and often no useful message at all — the failure is upstream of everything the program would have printed. ## The two shapes of the failure | Shape | What you see | Why it happens | What fixes it | |---|---|---|---| | Libraries absent | Immediate exit, complaint about a missing loader or shared object | The empty base supplies nothing | Static link, or copy each named library in | | Libraries present but wrong | Immediate exit even though a library with that name exists | The base's library implementation family differs from the one the artifact was compiled against | Rebuild against the target base's family, or link statically | The second shape is the nastier one, because a check that only asks *is there a library with this name* answers yes. Library names are shared across implementations; their contents are not. ## Ruling it in before you build 1. **Ask the artifact what it needs.** Every toolchain can list an executable's dynamic dependencies. That list is the requirement set for the final image. 2. **Compare it with what the base supplies.** The difference is exactly what you must copy in — and if the difference is empty but it still will not start, you are in the second shape above. 3. **Start the final image in the pipeline.** Not the build stage, not the base: the image you are about to publish, exercised through one real code path. 4. **Do it before anything is pulled onto hundreds of hosts**, because the symptom on a fleet is an instant restart loop with nothing to read. ## The three fixes and what each costs - **Link statically.** Nothing is resolved at run time, so the empty base is genuinely viable. The cost is that a library fix now means rebuilding your artifact rather than replacing a file, and not every toolchain or dependency supports it. - **Copy the libraries in.** Straightforward and precise, and it works with any base. The cost is ownership: files you copied by hand have no publisher's stream behind them, so keeping them current is now yours. - **Match the base to the toolchain.** Build and run against the same library family, which removes the second shape entirely. The cost is that the base is now pinned to a build decision, and changing either means revalidating both. There is a fourth option people reach for and should not: installing the missing library inside the running container. On a minimal or empty base there is no package manager to do it with, and even where there is, the change dies with the container and never reaches the next replica. An image is fixed once built; the fix is always a rebuild. ## Why the same class of bug bites data files too A dynamically linked library is only the loudest member of a family of **run-time dependencies the base used to supply invisibly**. The others fail later and more quietly: - A trust store the client reads when it opens an encrypted connection. - Timezone and locale data read the first time an instant is formatted. - Configuration files some processes read rather than asking the platform. - Character-set tables, which corrupt output rather than raising an error. The library case fails at start-up, which is a gift: you find it in seconds. The data-file cases fail on the first request that touches the code path, possibly days later. Both have the same cause — a dependency that was always there and was never declared — and the same guard: exercise the final image, not the build environment. ## What the interviewer is checking That you know an executable is not automatically self-contained; that you can name the two shapes and tell them apart; that your fix is a rebuild rather than something applied to a running container; and that you catch it in the pipeline rather than in production. None of that depends on which platform is running the container, which is precisely why it gets asked.
- The library is present under the expected name and the artifact still will not start. What now?Same name, different implementation. Library names are shared across implementation families, and an artifact compiled against one will not run against another. Rebuild the artifact against the family the base ships, or link it statically so the question stops mattering. Swapping to a base that matches the build toolchain is the same fix from the other end.
- How do you catch this before an image reaches a host?Start the final image in the pipeline and exercise one real code path, rather than testing in the build environment where the libraries happen to exist. Earlier and cheaper still, list the artifact's dynamic dependencies and diff them against the library set the base provides; anything in the difference must be copied in or linked away.
saying these in an interview costs you the question
- Assumes any compiled artifact carries everything it needs
- Blames the application code for an exit before start-up
- Thinks any minimal base is interchangeable with any other
- Plans to install the missing library inside the running container
- Checks only that a library name exists, not which implementation