Spring Boot
Embedded Tomcat
WAR Files
Java
Deployment

Spring Boot How to add another WAR files to the embedded tomcat?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Spring Boot's embedded Tomcat is built to host the current application, not to behave like a full shared application server. If you need to expose another WAR inside the same JVM, the supported path is a manual Tomcat customization, but in most cases the better answer is to use an external Tomcat or convert the extra app into a dependency or service.

Understand The Constraint First

This topic trips people up because an external Tomcat server and Boot's embedded Tomcat are not the same operational model.

With an external Tomcat installation, dropping multiple WAR files into the deployment directory is normal. With embedded Tomcat, your application owns the server lifecycle. That means there is no general "deploy another WAR next to me" switch in Spring Boot.

So the design question comes first:

  • If you want multiple independent webapps, use an external Tomcat or separate services.
  • If you need one process and one server, you can manually register an additional web context.

That manual registration is possible, but it is infrastructure code, not a typical application setting.

Register An Additional Web Context

Embedded Tomcat can host another web application if you add a second context programmatically. The most reliable form is an exploded WAR directory rather than a raw .war file.

Here is a Spring Boot configuration that mounts an already-expanded legacy app at /legacy:

java
1import java.io.File;
2import org.apache.catalina.Context;
3import org.apache.catalina.startup.Tomcat;
4import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
5import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
6import org.springframework.boot.web.server.WebServer;
7import org.springframework.context.annotation.Bean;
8import org.springframework.context.annotation.Configuration;
9
10@Configuration
11public class TomcatConfig {
12
13    @Bean
14    public TomcatServletWebServerFactory servletContainer() {
15        return new TomcatServletWebServerFactory() {
16            @Override
17            protected WebServer getWebServer(Tomcat tomcat) {
18                File baseDir = new File("/opt/tomcat-apps/legacy-expanded");
19                Context legacyContext = tomcat.addWebapp("/legacy", baseDir.getAbsolutePath());
20                legacyContext.setParentClassLoader(getClass().getClassLoader());
21                return new TomcatWebServer(tomcat);
22            }
23        };
24    }
25}

This code tells Tomcat to serve the additional application under a separate context path. The important detail is the filesystem location: Tomcat needs access to the expanded web application content.

Why Exploded WARs Are Easier Here

You may technically start from a WAR file, but embedded deployment is simpler when the archive is unpacked ahead of time. Tomcat can then point directly at the directory containing WEB-INF, classes, and static assets.

If your build pipeline produces legacy.war, unpack it during deployment and mount the resulting directory. That avoids hiding archive extraction and file permissions inside application startup logic.

It also makes failures easier to diagnose. If the app does not start, you can inspect the expanded directory directly instead of debugging both extraction and deployment at once.

Prefer Alternatives When You Can

Even though the customization above works, it is usually not the best architecture.

A better option is often one of these:

  • Run both WARs on an external Tomcat.
  • Turn the shared code into a library and keep one Boot app.
  • Deploy the legacy WAR separately and connect through HTTP.

Why? Because multi-app embedded Tomcat setups complicate startup, logging, class loading, resource paths, and security boundaries. Those problems are manageable, but they are not free.

If the second WAR is a legacy admin console or a transitional module, hosting it externally is usually easier to maintain than forcing it into the embedded server lifecycle.

Watch Class Loading And Resources

The hardest part of this setup is rarely the addWebapp call itself. It is the interaction between the Boot application classpath and the legacy webapp.

Potential trouble spots include:

  • Conflicting servlet API expectations.
  • Duplicate libraries loaded from different places.
  • Assumptions about writable temp directories.
  • Resource lookups that worked on external Tomcat but fail in embedded mode.

Setting the parent class loader explicitly, as shown above, can help, but it does not remove all compatibility issues. You still need to test the deployed legacy app as if it were a separate product.

Common Pitfalls

The most common mistake is assuming Spring Boot has a property that magically enables multi-WAR hosting. It does not.

Another mistake is trying to mount a raw WAR path directly without confirming how the embedded Tomcat instance resolves and expands it. Using an exploded directory is much more predictable.

Developers also underestimate lifecycle concerns. If the extra application fails during context initialization, it can prevent the whole Boot server from starting. That means one legacy deployment issue can take down the primary application too.

Finally, do not ignore architecture signals. If you are frequently adding more webapps to embedded Tomcat, you are probably rebuilding a shared app server manually. At that point, an external Tomcat or separate service deployment is the cleaner solution.

Summary

  • Embedded Tomcat in Spring Boot is primarily for the current application, not general multi-WAR hosting.
  • If you must host another WAR, mount it as an additional Tomcat context programmatically.
  • Use an exploded WAR directory for a more reliable setup.
  • Expect class loading, lifecycle, and deployment complexity.
  • In most cases, external Tomcat or separate services are easier to operate.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.