How to Install and Configure Apache Tomcat on Windows Server 2022

Apache Tomcat is the de-facto Java Servlet container for deploying Java web applications on Windows Server. It supports the Jakarta EE (formerly Java EE) Servlet and JSP specifications and is widely used to host Spring Boot applications, REST APIs, and legacy J2EE WAR files. This guide covers downloading Tomcat 10, installing it as a Windows service, configuring HTTP and HTTPS connectors, securing the management apps, deploying WAR files, configuring JVM options, understanding Tomcat logging, and using IIS as a front-end reverse proxy via Application Request Routing (ARR).

Prerequisites

Before installing Tomcat, ensure that:

  • Java (OpenJDK 17 or 21 LTS) is installed and JAVA_HOME is set. See the Java installation article in this series.
  • Port 8080 (or your chosen connector port) is not already in use.
  • Windows Firewall rules will be configured to allow inbound traffic on the Tomcat port.
# Verify Java is available and JAVA_HOME is set
java -version
echo $env:JAVA_HOME

# Check if port 8080 is in use
netstat -ano | findstr ":8080"

Downloading Apache Tomcat 10

Tomcat 10.x implements the Jakarta Servlet 5.0 and 6.0 specifications. Note that Tomcat 10 uses the jakarta.* namespace (not javax.*) — legacy applications compiled against javax.servlet must be migrated using the Jakarta Migration Tool or remain on Tomcat 9.x.

# Download Tomcat 10.1.x Windows x64 installer (Service Installer .exe)
$tomcatVersion = "10.1.24"
$baseUrl = "https://dlcdn.apache.org/tomcat/tomcat-10/v$tomcatVersion/bin"

# Option A: Windows installer (.exe) — installs as a service automatically
Invoke-WebRequest -Uri "$baseUrl/apache-tomcat-$tomcatVersion.exe" `
    -OutFile "C:Installersapache-tomcat-$tomcatVersion.exe"

# Option B: Zip bundle — manual install, full control
Invoke-WebRequest -Uri "$baseUrl/apache-tomcat-$tomcatVersion-windows-x64.zip" `
    -OutFile "C:Installersapache-tomcat-$tomcatVersion-windows-x64.zip"

Installing Tomcat from ZIP (Recommended for Production)

Installing from the ZIP bundle gives you full control over directory layout, multiple instance support via CATALINA_BASE, and easier upgrades. Extract and prepare the installation:

# Extract the zip
Expand-Archive -Path "C:Installersapache-tomcat-10.1.24-windows-x64.zip" `
    -DestinationPath "C:Java"

# Rename for a clean path
Rename-Item "C:Javaapache-tomcat-10.1.24" "C:Javatomcat10"

# Set CATALINA_HOME
[System.Environment]::SetEnvironmentVariable("CATALINA_HOME", "C:Javatomcat10", "Machine")

# Set CATALINA_BASE (for single instance, same as CATALINA_HOME)
[System.Environment]::SetEnvironmentVariable("CATALINA_BASE", "C:Javatomcat10", "Machine")

# Test startup (interactive mode first, before installing as service)
$env:CATALINA_HOME = "C:Javatomcat10"
$env:JAVA_HOME = "C:Javajdk-21"
& "C:Javatomcat10binstartup.bat"

Open a browser and navigate to http://localhost:8080 — you should see the Tomcat welcome page. Stop the interactive instance before proceeding with service installation:

& "C:Javatomcat10binshutdown.bat"

Installing Tomcat as a Windows Service

Tomcat includes service.bat and the Tomcat10 native service wrapper (Tomcat10.exe / Tomcat10w.exe) for Windows service installation. Run the service installer from an elevated command prompt:

cd C:Javatomcat10bin

# Install as Windows service named "Tomcat10"
service.bat install Tomcat10

# Alternatively, use the Tomcat10.exe directly for more control:
Tomcat10.exe //IS//Tomcat10 `
    --DisplayName="Apache Tomcat 10.1" `
    --Description="Apache Tomcat 10.1 Servlet Container" `
    --Classpath="C:Javatomcat10binbootstrap.jar;C:Javatomcat10bintomcat-juli.jar" `
    --Jvm="%JAVA_HOME%binserverjvm.dll" `
    --StartMode=jvm --StopMode=jvm `
    --StartClass=org.apache.catalina.startup.Bootstrap `
    --StopClass=org.apache.catalina.startup.Bootstrap `
    --StartParams=start --StopParams=stop `
    --JvmMs=512 --JvmMx=2048 `
    --LogPath="C:Javatomcat10logs" `
    --StdOutput=auto --StdError=auto

Manage the service using PowerShell or Services Manager:

# Start, stop, restart, check status
Start-Service Tomcat10
Stop-Service Tomcat10
Restart-Service Tomcat10
Get-Service Tomcat10

# Set to start automatically
Set-Service Tomcat10 -StartupType Automatic

# Set the service to run as a specific service account
$cred = Get-Credential
sc.exe config Tomcat10 obj= "DOMAINsvc-tomcat" password= "ServicePass123!"

Configuring server.xml — HTTP and HTTPS Connectors

The primary Tomcat configuration file is %CATALINA_HOME%confserver.xml. This file defines the TCP port listeners (connectors), virtual hosts, and engine configuration. A default Tomcat installation uses port 8080 for HTTP. For production, add an HTTPS connector and disable (or restrict) the HTTP connector.



  
  

  

    
    

    
    

    
      
      
    
  

Create a self-signed certificate for testing (use a proper CA-signed certificate for production):

# Generate a self-signed keystore using Java keytool
& "$env:JAVA_HOMEbinkeytool.exe" -genkeypair `
    -alias tomcat `
    -keyalg RSA `
    -keysize 2048 `
    -validity 365 `
    -keystore "C:Javatomcat10confkeystore.jks" `
    -storepass changeit `
    -keypass changeit `
    -dname "CN=myserver.example.com, OU=IT, O=MyOrg, L=City, ST=State, C=US"

Securing the Manager and Host-Manager Apps

Tomcat ships with two web-based administration apps: Manager (for deploying/undeploying WAR files) and Host Manager (for managing virtual hosts). Both are disabled by default for remote access — they must be explicitly authorized in conf/tomcat-users.xml. Never expose these apps directly to the internet.




  
  
  
  

  

  
  

By default, the Manager app only allows connections from localhost. To allow access from a specific IP range (e.g., your management network), edit webapps/manager/META-INF/context.xml:


  
  
  

Deploying WAR Files

WAR (Web Application Archive) files are the standard deployment unit for Java web applications. Drop the WAR into %CATALINA_HOME%webapps and Tomcat will auto-deploy it when autoDeploy="true" is set in server.xml:

# Copy WAR to webapps directory (Tomcat auto-deploys when running)
Copy-Item -Path "C:Buildmyapp.war" -Destination "C:Javatomcat10webappsmyapp.war"

# Verify it was unpacked (check for the myapp directory)
Get-ChildItem "C:Javatomcat10webapps"

# Deploy via Manager API (does not require restart)
Invoke-WebRequest `
    -Uri "http://localhost:8080/manager/text/deploy?path=/myapp&war=file:C:/Build/myapp.war" `
    -Credential (Get-Credential deploy-user)

# Undeploy
Invoke-WebRequest `
    -Uri "http://localhost:8080/manager/text/undeploy?path=/myapp" `
    -Credential (Get-Credential deploy-user)

JVM Options: setenv.bat

The recommended way to configure JVM options in Tomcat is via binsetenv.bat (or setenv.sh on Linux). Create this file if it does not exist — Tomcat’s catalina.bat automatically calls it at startup:

REM C:Javatomcat10binsetenv.bat

SET "CATALINA_OPTS=-server -Xms1g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
SET "CATALINA_OPTS=%CATALINA_OPTS% -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=C:Javatomcat10logs"
SET "CATALINA_OPTS=%CATALINA_OPTS% -Dspring.profiles.active=production"
SET "CATALINA_OPTS=%CATALINA_OPTS% -Djava.net.preferIPv4Stack=true"
SET "CATALINA_OPTS=%CATALINA_OPTS% -Dfile.encoding=UTF-8"

For the Windows service, set JVM options via the Tomcat Service Manager GUI (Tomcat10w.exe //ES//Tomcat10) or from the command line:

C:Javatomcat10binTomcat10.exe //US//Tomcat10 `
    --JvmMs=1024 `
    --JvmMx=4096 `
    ++JvmOptions="-XX:+UseG1GC" `
    ++JvmOptions="-Dspring.profiles.active=production"

Tomcat Logging

Tomcat uses its own logging framework (JULI — Java Util Logging Implementation) by default. Log files are stored in %CATALINA_HOME%logs:

  • catalina.YYYY-MM-DD.log — Tomcat engine log (equivalent to catalina.out on Linux)
  • localhost.YYYY-MM-DD.log — Per-host log
  • localhost_access_log.YYYY-MM-DD.txt — HTTP access log
  • manager.YYYY-MM-DD.log — Manager application log
# View today's Tomcat log
Get-Content "C:Javatomcat10logscatalina.$(Get-Date -Format 'yyyy-MM-dd').log" -Tail 100 -Wait

# Search for ERROR entries
Select-String -Path "C:Javatomcat10logscatalina.*.log" -Pattern "SEVERE|Exception"

# Clean logs older than 30 days
Get-ChildItem "C:Javatomcat10logs" -Filter "*.log" |
    Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
    Remove-Item -Force

IIS as Front-End with ARR Reverse Proxy

For production deployments, it is common to place IIS in front of Tomcat. IIS handles TLS termination, static file serving, and load balancing, while Tomcat handles dynamic Java content. Install Application Request Routing (ARR) and URL Rewrite in IIS:

# Install ARR and URL Rewrite via Web Platform Installer or direct download
# ARR: https://www.iis.net/downloads/microsoft/application-request-routing
# URL Rewrite: https://www.iis.net/downloads/microsoft/url-rewrite

# Enable proxy in ARR (required)
$arrProxy = Get-WebConfiguration system.webServer/proxy
Set-WebConfiguration system.webServer/proxy -Value @{enabled=$true}

Configure a URL Rewrite reverse proxy rule in the IIS site’s web.config:



  
    
      
        
          
          
            
          
          
        
      
    
    
      
        
      
    
  

Open the Windows Firewall to allow Tomcat on port 8080 only from localhost (since IIS is the public face):

# Allow Tomcat only from localhost (block external access to 8080)
New-NetFirewallRule -DisplayName "Tomcat 8080 Localhost Only" `
    -Direction Inbound -Protocol TCP -LocalPort 8080 `
    -RemoteAddress 127.0.0.1 -Action Allow

# Allow IIS on 443 from anywhere
New-NetFirewallRule -DisplayName "IIS HTTPS 443" `
    -Direction Inbound -Protocol TCP -LocalPort 443 `
    -Action Allow