Spring Boot with Embedded Tomcat Pros and Cons
If you work with web services in Java, chances are you work with Spring Boot as well or at least is considering it.
When you build your web service using Spring Boot, the default setup is to generate a .jar file. The .jar comes with a web server embedded, and you just need to execute it to have the application up and running. The web servers can be Tomcat, Jetty, or Undertow . I’m going to focus on Tomcat.
It’s also possible to use Spring Boot and deploy as a .war file. That’s how I’m more accustomed to it. It feels more natural to me. It’s how I learned — one single Tomcat installation in the server, with multiple applications deployed as .war files.
In this article, I’m going to address the pros and cons of building a Spring Boot application with an embedded Tomcat. It’s the best way (in my view) if you are working with Microservices.
The pros are not an extensive list, but they are quite significant.
For me, this is the driving force. Isolation means that the applications are not dependent on a single Tomcat installation. If the embedded Tomcat in service A fails, it won’t impact service B or C as each one has its own Tomcat. Unless the services call each other (which it shouldn’t )— but that’s a different problem. With one single Tomcat, with services calling each other or not, if the Tomcat is shut down, all services are impacted.
Such isolation also means that using Docker may be overkill (don’t beat me just yet). I’ve never used Docker, to be honest, I don’t feel comfortable elaborating too much on this argument. I’m pretty sure it provides many benefits. Based on what I’ve read, it involves much more than just building an application with embedded Tomcat.
One key benefit of Docker is to eliminate the famous ‘it works on my machine’  problem. However, most of the time I faced such issue was because of the Tomcat or Java version or the lack of environment variables and arguments defined in the Tomcat configuration — with separate Tomcats, one for each application/service, and a default start/stop/restart script, this issue is resolved.
2. Easier to choose the Tomcat and Java version you want
Let’s say you’re building a new application and want to use the newest Tomcat and Java. It’s as easy as configuring the version in the pom.xml (assuming you’re using Maven)
If there is one Tomcat installation, with some legacy application already deployed, you won’t be able to deploy your new application with a greater Java version. You could just upgrade the Tomcat and Java in the server, but you can’t guarantee the legacy applications will work fine, that no clients will be impacted.
Also, in a big company, it may not be so straightforward to upgrade existing Tomcat and Java. You often need to get permission and go through some lengthy processes with IT.
1. Multiple Tomcats to manage and monitor
, This one can be addressed with utility scripts to start/stop/restart each service or all at once, and spring-boot-admin  to monitor the health and resources such as memory, threads and JVM in general. All visualized through a nice UI.
2. It’s not possible to change properties files only, without rebuilding.
It’s not ideal for changing properties files only and reload the application. But it’s a reality.
More often than not, I worked with applications where a (bad) common practice was to change a configuration/properties file and redeploy on Tomcat without rebuilding the entire application again. With a .war file, it’s easy — when the .war is deployed on Tomcat, the contents are extracted into a folder in web apps. Then you just need to open the folder, change the properties file, and reload the application in Tomcat manager.
Working with a .jar, force us always to rebuild the entire application— which it’s good. I see this con as a pro in disguise.
*Update: It was mentioned in the comments section that the property file could be external. Hence there would be no need to rebuild the .jar —That’s correct.
3. It breaks load balancer health check
As there are multiple Tomcats and port numbers, which service would be used for load balancer health check?
It’s common to configure the load balancer (LB) with the port being dynamic, appended to the end of the hostname — BUT load balancers also require at least one port to be fixed, to serve as a health check.
The LB also needs a fixed port number for the health check. It could be 8083 or 8084, or perhaps a third one: 8080, that only serves as a health check. To use the latter could work — however, it’s not the best solution.
The ideal solution is to use a Gateway. It not only provides a common port to serve as a health check, but it also resolves the issue of having to remember multiple port numbers and URLs.
The Gateway stays in front of all services having its own unique port. It becomes a single point of failure — that’s true! But it’s a simple application that only does redirection. There is no business logic; hence it should rarely have issues.
Netflix Zuul  is commonly used for the Gateway. Figure 1 demonstrates such architecture.
4. Multiple port numbers / URLs to manage and monitor
As each service has its own port number, it can become a hassle to manage and monitor all the different URLs. Imagine telling for each client: client A, use service B on port 8083; client B, use service D on port 8084, and so on. It also impacts load balancer configuration as already explained.
The solution is also to have a Gateway, as described in the previous section — con number 3.
5. No hot deployment out of the box
Hot deployment means having the application deployed without having to restart the server or reload the application manually.
When I use one Tomcat installation and .war file, I usually do hot deployment for any change. All I need to do is to copy the modified .war file to Tomcat webapps folder, and that’s it, the application is updated. This process can be automated by configuring Jenkins. It can check changes in Git and move the .war.
In the case of a .jar with embedded Tomcat, Jenkins job can be used. The job can check for changes in the repository, generate the .jar, transfer it to the right location in the server and execute it.
There is also a module called spring-boot-devtools that can do reloading from the local IDE to a remote server. It doesn’t work exactly how I want, but it may be enough, at least for the TEST environment. Enabling this for production is a security risk 
If you are working with Microservices, it’s preferred to build your services as a .jar with embedded Tomcat, in my opinion. Although there are many cons, each one can be mitigated relatively easily. On the other hand, if you’re not interested in Microservices if your services and applications require to invoke each other, meaning, they’re not isolated anyway — then it’s better to have a single Tomcat installation, it’s easier to maintain.
In case you have Docker, I believe both options: .jar with its own embedded Tomcat or .war deployed in Tomcat installation inside the container, are possible. But you would get good isolation either way.
 Spring Boot Overview https://spring.io/projects/spring-boot
 Why Docker https://www.docker.com/why-docker
 spring-boot-admin https://github.com/codecentric/spring-boot-admin
 Zuul https://github.com/Netflix/zuul
 20.5 Remote Applications https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/html/using-boot-devtools.html