skip to content

You copy analytics.war into a standalone Tomcat's webapps/ directory. What context path does the application end up on, how would you instead serve it at the root path, and what does the Host attribute autoDeploy do while Tomcat is running?

level: juniorimportance: must knowfreq 68%

answer

  1. file name decides the path
  2. one reserved name means root
  3. hash characters carry meaning
  4. a scan thread watches appBase

basics

~20 s

Tomcat derives the context path from the WAR's file name, so analytics.war deploys at /analytics. Name the file ROOT.war to serve it at the root path. With autoDeploy enabled, Tomcat scans webapps/ while running and deploys or redeploys changed files.

solid answer

~40 s

Tomcat's `<Host>` has an `appBase`, `webapps` by default, and every WAR or directory there becomes a deployed context whose path comes from the file name. `analytics.war` gives `/analytics`; the reserved name `ROOT.war` (or a `ROOT` directory) gives the empty root path; a `#` in the name encodes a nested path, so `shop#eu.war` becomes `/shop/eu`; and a double `##` marks a version for parallel deployment, as in `shop##002.war`. You do not set the path yourself — a `path` attribute in the application's own `META-INF/context.xml` is ignored, which surprises people. `deployOnStartup` deploys what is present when Tomcat boots; `autoDeploy` keeps a background scan running so a newly copied or updated WAR is picked up without a restart. `unpackWARs` decides whether the archive is expanded into a directory first.

code

bash · 5 lines
bash
# The file name in appBase determines the context path
cp analytics.war "$CATALINA_BASE/webapps/"            # -> /analytics
cp portal.war    "$CATALINA_BASE/webapps/ROOT.war"    # -> /
cp eu.war        "$CATALINA_BASE/webapps/shop#eu.war" # -> /shop/eu
cp shop.war      "$CATALINA_BASE/webapps/shop##002.war" # -> /shop, version 002

go deeper

for a junior

Be able to say that the WAR's file name becomes the context path, that ROOT.war is the root application, and that copying the file into webapps/ is the deployment step.

for a middle

Explain the naming grammar precisely — the # and ## encodings, why a path attribute inside the app is ignored — and distinguish deployOnStartup, autoDeploy and unpackWARs by what each one actually triggers.

for a senior

Show that you know the failure modes: a WAR picked up mid-copy, an exploded directory silently recreated on redeploy, getRealPath returning null when WARs are not unpacked, and why many production installs disable autoDeploy entirely.

for a principal

Own the release story: whether hot redeploy into a long-lived JVM is allowed at all, how parallel deployment versions interact with session draining and rollback, and who is permitted to write into appBase in the first place.

## The pieces involved A standalone Tomcat serves applications out of an *application base directory*, configured as the `appBase` attribute of the `<Host>` element in `conf/server.xml`. The stock configuration is: ```xml <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> </Host> ``` A relative `appBase` is resolved against `$CATALINA_BASE`, the per-instance directory (which equals `$CATALINA_HOME` unless you split them). Anything Tomcat deploys becomes a **Context**: one web application, with its own class loader, its own JNDI naming environment, its own session manager, and one URL prefix — the *context path* — under which every one of its servlets is reachable. ## How the file name becomes the context path Tomcat does not ask the application what path it wants. It derives the path from the name of the WAR file or the directory: - `analytics.war` (or an `analytics/` directory) becomes context path `/analytics`. - `ROOT.war` or a `ROOT/` directory is the reserved spelling for the **empty** context path, i.e. the application answers `https://host/` directly. This is the standard way to put one application at the site root; nothing else does it. - `#` in the file name is decoded as `/`, so `shop#eu.war` deploys at `/shop/eu`. That is how you get a multi-segment path without nesting directories. - `##` introduces a **version** for parallel deployment: `shop##001.war` and `shop##002.war` are two versions of the same `/shop` context. Tomcat routes existing sessions to the old version and new sessions to the newest one, so you can retire the old WAR once its sessions drain. A common misconception is that you can write `path="/analytics"` in the application's `META-INF/context.xml`. Tomcat ignores it — the `path` attribute is only legal when a `<Context>` is nested directly in `server.xml`, which the documentation discourages because such a context cannot be reloaded without restarting Tomcat. ## deployOnStartup, autoDeploy, unpackWARs Three `<Host>` attributes govern the mechanics: - **`deployOnStartup`** (default `true`): at boot, Tomcat deploys everything it finds — context descriptors in `conf/<engine>/<host>/`, then directories, then WAR files. - **`autoDeploy`** (default `true`): a background thread rescans `appBase` periodically. Dropping a new WAR in deploys it; overwriting an existing WAR with a newer timestamp triggers an *undeploy and redeploy* of that context; deleting the WAR undeploys it. This is what makes `cp app.war webapps/` a deployment procedure, and it is also why a half-copied WAR can be picked up mid-copy — copy to a temporary name and rename, or use the Manager application's upload instead. - **`unpackWARs`** (default `true`): the archive is expanded into a directory next to it before it runs. Running unexpanded (`false`) saves the disk copy but makes `ServletContext.getRealPath()` return `null`, which breaks applications that expect a real file system path. ## What that means in practice ```bash cp analytics.war "$CATALINA_BASE/webapps/" # -> /analytics cp portal.war "$CATALINA_BASE/webapps/ROOT.war" # -> / ``` The copy is the whole deployment. Tomcat expands `analytics.war` into `webapps/analytics/`, creates the context, and starts it; failures appear in `logs/catalina.out` and in the context's own log, not on the console of the copy command. Because the exploded directory is *derived*, editing files inside it is temporary: the next redeploy deletes and recreates it. In production, teams often turn `autoDeploy` off and restart the JVM on each release instead, because repeated hot redeploys of the same JVM are the classic source of class-loader leaks. The naming rules stay the same either way. ## Related surfaces The Manager application (`/manager/html` and its text API) deploys, undeploys, reloads and lists contexts over HTTP using exactly the same naming rules, and the Host Manager application creates virtual hosts. Both are ordinary web applications shipped with Tomcat, guarded by roles in `conf/tomcat-users.xml`, and both are routinely removed from internet-facing installations.

  • What actually happens on disk when autoDeploy notices that an already-deployed WAR has a newer timestamp?
    Tomcat undeploys the running context, deletes the exploded directory under `appBase` and the context's directory under `work/`, then expands the new archive and starts a fresh context with a new class loader. Anything you edited inside the exploded directory is gone, and in-memory state such as sessions belonging to the old context is discarded unless it was persisted.
  • Why would you set unpackWARs to false, and what tends to break?
    Running straight from the archive avoids a second copy on disk and keeps the deployed unit byte-identical to what was built. The cost is that `ServletContext.getRealPath()` returns `null`, so any code that expects a real file — writing uploads next to the application, reading a template by absolute path, some older libraries scanning the file system — fails. Applications that only use `getResourceAsStream()` are unaffected.
  • What does the `##` version suffix buy you over simply overwriting the WAR?
    Parallel deployment. Both versions run at the same context path at once: requests carrying a session that belongs to the older version keep going there, while new sessions land on the highest version. You undeploy the old WAR when its sessions have drained, so a release does not throw active users out mid-session — at the cost of two copies of the application in one JVM.

The webapps directory works like a drop folder rather than a registry: the file's name is the whole deployment request, so renaming the file is how you choose the URL.

saying these in an interview costs you the question

  • Thinking the app declares its own path in context.xml
  • Believing you must restart Tomcat to deploy a WAR
  • Calling the root app index.war or default.war
  • Assuming edits to the exploded directory survive redeploy
  • Confusing autoDeploy with reloadable class watching

context