skip to content

Why does a WireMock stub set that works under java -jar return 404s in the wiremock/wiremock container?

level: seniorimportance: must knowfreq 58%

answer

  1. one directory decides the whole set
  2. where the process thinks here is
  3. the image does not run in your checkout
  4. mappings/ must be a child of the root
  5. /home/wiremock is the image's root

basics

~20 s

WireMock's standalone launcher reads mappings and __files beneath one root directory. The wiremock/wiremock image sets that root to /home/wiremock. A bind mount landing anywhere else leaves the mapping set empty, so every scaffolding-inspection request goes unmatched and comes back 404.

solid answer

~40 s

Under `java -jar wiremock-standalone.jar` the root directory defaults to the working directory, so a checkout that already contains `mappings/` and `__files/` works with no flags at all. The `wiremock/wiremock` image does not run inside your checkout — its root is `/home/wiremock`, so those two directories have to arrive there as *children* of the mount target: `-v "$PWD/scaffolding-inspection-stubs:/home/wiremock"`. Three variants of one mistake produce the 404: the mount landed a level too deep, so WireMock looks at `/home/wiremock/mappings` while the files sit under `/home/wiremock/scaffolding-inspection-stubs/mappings`; the JSON was copied directly under the mount target instead of into `mappings/`; or a `--root-dir` argument appended after the image name moved the root somewhere empty. List `/home/wiremock/mappings` inside the running container before you touch a single matcher.

go deeper

for a junior

Be ready to say that WireMock standalone reads mappings/ and __files/ from a single root directory, and that the wiremock/wiremock image fixes that root at /home/wiremock rather than at your checkout.

for a middle

Explain how --root-dir and the image's root interact, and why arguments written after the image name reach the same launcher a java -jar invocation would have started.

for a senior

Show the diagnosis order out loud: prove the mapping set is empty by listing the directory inside the container before you touch a matcher, a base URL or a client timeout.

for a principal

Own the standard. One agreed stub-directory layout and one mount target across teams means a stub set moves between a laptop, a build agent and a container without anyone re-deriving the path.

## What WireMock's root directory actually is WireMock's standalone distribution is a shaded uber-jar — `wiremock-standalone` — that starts an HTTP server and builds its stub set from exactly one place: the **root directory**. Beneath that root it expects two subdirectories and looks nowhere else. - `mappings/` holds the JSON stub definitions the server will answer with. - `__files/` holds the body files those definitions reference by name. The root is chosen by `--root-dir`. When the flag is absent, WireMock roots itself in the process's current working directory. That single sentence explains the whole failure: nothing about your files changed between the laptop and the container, only what the process considers *here*. ## Why `java -jar` hides the problem On a developer machine you almost always launch the jar from the repository checkout that already contains `mappings/` and `__files/`. The default root and the checkout are the same directory, so the scaffolding-inspection stubs load and nobody ever thinks about `--root-dir`. The flag is invisible precisely because the default happened to be correct. The `wiremock/wiremock` image removes that coincidence. The container does not run inside your checkout — it roots the server at `/home/wiremock`, and that path holds none of your work until you put something there. An empty mapping set is not an error condition: WireMock starts happily, binds its port, and returns 404 to `GET /v1/scaffolds/SC-4471/inspections` because it genuinely has no stub for it. From outside, a healthy process with an empty stub set and a healthy process with a loaded one look identical. ## The three shapes of the same failure 1. **The mount landed one level too deep.** Mounting the checkout root produces `/home/wiremock/scaffolding-inspection-stubs/mappings`, while the server reads `/home/wiremock/mappings`. Every file is present inside the container and still invisible. 2. **The JSON sits directly under the mount target.** Copying `scaffold-inspections-list.json` straight into `/home/wiremock` puts it beside the directory WireMock reads rather than inside it. `mappings/` is not a convention you can drop. 3. **An argument moved the root.** Anything written after the image name is handed to the same launcher `java -jar` would have started, so a `--root-dir /tmp/stubs` left over from an experiment silently overrides the mount you just made. ## Diagnosing it in the order that costs least - **Prove the set is empty before you suspect matching.** List `/home/wiremock/mappings` inside the running container. An empty or missing directory ends the investigation there; a full one moves you to a genuinely different problem. - **Read the start-up output.** `--verbose` turns on WireMock's verbose logging to stdout, which is exactly where the container's logs already point. - **Check what the launcher was actually given.** Inspect the command the container is running: a forgotten `--root-dir` beats the mount every time. - **Confirm the request reached WireMock at all.** A host port that does not map to the server's port gives a connection failure, not a 404. If a 404 body came back from WireMock, the server received the request and had nothing to answer it with. - **Only then look at the mapping.** A stub that loaded and failed to match is a different class of problem with a different toolkit. ## Why 404 is the honest symptom The distinction worth demonstrating in an interview is between *nothing loaded* and *nothing matched*. Both surface as an unmatched request, so both look like a broken stub, and engineers reliably reach for the matcher first. The mechanical separator is the mapping set itself: if the server holds zero mappings, no matcher was ever consulted, and every minute spent loosening a URL pattern is wasted. That is why the first move is a directory listing rather than an edit. The same asymmetry explains why this failure survives code review. The mapping files are committed, reviewed and correct; the defect lives in the one line that says where the process should look, and that line usually sits in a pipeline definition nobody reads alongside the stubs. ## Keeping it from recurring - **Agree one layout.** Keep `mappings/` and `__files/` at the top of a dedicated stub directory in the repository, and always mount that directory — never one of its children. - **Mount the parent, publish the port, pass nothing else.** The fewer arguments after the image name, the fewer ways there are to move the root. - **Smoke-test the set, not the process.** Once the container is up, request one scaffolding-inspection path a mapping must answer and fail the job when it does not. Process liveness cannot tell you the set loaded; a request that a stub must answer can. - **Fail loudly on an empty set.** A build that quietly proceeds against a stub server holding no stubs produces test failures that read like application bugs, and the team spends a day inside the wrong repository. - **Keep the local and container launch in one script.** When the same file chooses both the root directory and the mount target, the two cannot drift apart.

  • The mount looks correct and the container still serves nothing — what do you look at next?
    Check that nothing after the image name moved the root: those arguments reach the same launcher, so a stray `--root-dir` wins over the mount. Then confirm the mapping JSON parses, because that is a start-up concern rather than a matching one. Finally, check the client is calling the published host port and not the container's internal one, which fails differently.
  • How would you make this failure impossible to miss in a pipeline?
    Follow the container start with one request against a scaffolding-inspection path a stub must answer, and fail the job on anything but the stubbed status. A stub server that started with an empty mapping set is indistinguishable from a working one at the process level, so the only trustworthy signal is a response that could only have come from a loaded mapping.

saying these in an interview costs you the question

  • Assuming the container serves files from the host working directory
  • Mounting the stub set anywhere and expecting WireMock to find it
  • Putting mapping JSON directly under the root instead of inside mappings/
  • Blaming request matching before checking that any mapping loaded at all
  • Believing the image watches the mount and reloads new files by itself