Home/Server & Website Managed Operations/Standard Workflow and Common Pitfalls for Deploying Java Projects with Tomcat + JDK
Server & Website Managed Operations

Standard Workflow and Common Pitfalls for Deploying Java Projects with Tomcat + JDK

Deploying a Java project involves a few more steps than deploying a PHP site. The main differences are: applications are packaged as WAR files for deployment, and Tomcat itself has several default configurations that need adjustment.

The following guide follows the order of “Install JDK → Install Tomcat → Deploy Application → Adjust Configurations”, including common issues at each stage.

Step 1: Install JDK

# Check if already installed
java -version
which java

# Install via package manager (recommended for easier management)
yum install -y java-11-openjdk java-11-openjdk-devel

# Confirm JAVA_HOME
echo $JAVA_HOME
# Or find the installation path
ls /usr/lib/jvm/

Version Must Match the Application

Issue Manifestation
JDK version too high Old projects may fail to compile or run
JDK version too low New projects using newer syntax features cannot run
# Manage multiple versions
alternatives --config java
alternatives --config javac

Confirm the required JDK version with developers before installing. Assuming “the latest is best” often leads to compatibility issues.

Step 2: Install Tomcat

# Download and extract (using official binary package as an example)
wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.xx/bin/apache-tomcat-9.0.xx.tar.gz
tar -xzf apache-tomcat-9.0.xx.tar.gz -C /opt/
ln -s /opt/apache-tomcat-9.0.xx /opt/tomcat

# Run with a dedicated user (do not use root)
useradd -r -s /sbin/nologin tomcat
chown -R tomcat:tomcat /opt/tomcat
# Configure environment variables
cat > /etc/profile.d/tomcat.sh <<'EOF'
export CATALINA_HOME=/opt/tomcat
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
EOF
source /etc/profile.d/tomcat.sh
# Start and verify
sudo -u tomcat /opt/tomcat/bin/startup.sh
curl -I http://127.0.0.1:8080/
ss -lntp | grep 8080

Step 3: Deploy Application

# Method 1: Place WAR file in webapps (auto-extract and deploy)
cp app.war /opt/tomcat/webapps/
# Tomcat will automatically extract and deploy it

# Method 2: Extract and place directory
unzip app.war -d /opt/tomcat/webapps/app/

# View deployment logs
tail -f /opt/tomcat/logs/catalina.out

ROOT Deployment

# To map the application directly to the root path /
# Rename the WAR to ROOT.war, or extract to webapps/ROOT/
cp app.war /opt/tomcat/webapps/ROOT.war

Step 4: Necessary Configuration Adjustments

① Port

<!-- conf/server.xml -->
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

In production environments, port 8080 is usually not exposed directly. Instead, use Nginx as a reverse proxy:

location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

② Memory Settings (Important)

# bin/setenv.sh (create if it doesn't exist)
cat > /opt/tomcat/bin/setenv.sh <<'EOF'
export JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC"
EOF
chmod +x /opt/tomcat/bin/setenv.sh

It is recommended to set -Xms and -Xmx to the same value to avoid overhead from repeated memory expansion during runtime. Ensure the upper limit leaves sufficient margin based on machine memory (do not exceed 70% of available memory).

③ Disable External Access to Management Interfaces

# In production, it is recommended to delete or restrict management apps
ls /opt/tomcat/webapps/
# If manager and host-manager are not needed, delete them
rm -rf /opt/tomcat/webapps/manager /opt/tomcat/webapps/host-manager

If the default management interface is exposed and uses weak passwords, it is a common entry point for intrusions.

④ Disable Directory Listing

<!-- conf/web.xml -->
<init-param>
    <param-name>listings</param-name>
    <param-value>false</param-value>
</init-param>

⑤ Configure as System Service

cat > /etc/systemd/system/tomcat.service <<'EOF'
[Unit]
Description=Tomcat
After=network.target

[Service]
Type=forking
User=tomcat
Environment=CATALINA_HOME=/opt/tomcat
Environment=JAVA_HOME=/usr/lib/jvm/java-11-openjdk
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now tomcat
systemctl status tomcat

Troubleshooting

# ① Check logs (most important)
tail -100 /opt/tomcat/logs/catalina.out
tail -100 /opt/tomcat/logs/localhost.date.log

# ② Check ports
ss -lntp | grep 8080

# ③ Local access test (distinguish between Tomcat issues and proxy issues)
curl -I http://127.0.0.1:8080/app/

# ④ Check processes and memory
ps aux | grep tomcat
jps -l                  # View Java processes

# ⑤ Common causes of startup failure
#   - Port occupied
#   - JAVA_HOME not configured
#   - Memory parameters exceed available machine memory

Decision Tree

Deploy Java Project
├─ Confirm JDK version with developers first (don't assume 'latest')
├─ Run Tomcat with a dedicated user (not root)
├─ Deploy WAR
│  ├─ Normal path → webapps/app_name.war
│  └─ Root path   → ROOT.war
├─ Necessary Adjustments
│  ├─ Memory parameters (-Xms = -Xmx, max 70% of available memory)
│  ├─ Delete/restrict management interfaces     ← Security related, don't miss
│  ├─ Disable directory listing
│  └─ Configure systemd service
└─ Issues Arise
   ├─ Check catalina.out first
   ├─ Curl localhost 8080 (distinguish Tomcat vs Proxy)
   └─ Check port occupation, JAVA_HOME, memory

Scenario Walkthrough: Works Locally but Fails on Server

Symptom: A Java Web project runs normally locally,
but after deploying to the server, there are no startup errors, the port is listening, yet accessing via domain name always fails.

  1. First, check if requests actually reach the application →
    Check status codes. This immediately distinguishes whether the request is blocked by the chain or if the application itself is unresponsive.
  2. Confirm application health via local request →
    If local returns normally, the program is running fine, narrowing the scope to the network chain.
  3. Check the address the application listens on →
    It might only be listening on the loopback address, preventing external requests from entering. This is the most common difference in such deployments.
  4. Verify runtime environment version consistency →
    Inconsistent JDK versions between local and server can cause loading failures due to compilation vs. runtime version mismatches.
  5. Confirm how external services expose ports →
    The application’s own port often needs forwarding by a front-end web service or adjusted to listen on external addresses directly.
  6. Verify layer by layer after adjustments → First local, then server local, finally external request.
    Confirm each layer.
# Check which address the application is listening on (127.0.0.1 means external access is blocked)
ss -lntp | grep java

# Version consistency: Are local and server versions the same?
java -version 2>&1 | head -1
javac -version 2>&1 | head -1

# Layered verification
curl -sS -o /dev/null -w 'Local=%{http_code}n' http://127.0.0.1:8080/
curl -sS -o /dev/null -w 'External=%{http_code}n' http://ServerIP:8080/

# Configuration check when forwarded by front-end service
nginx -t && nginx -T 2>/dev/null | grep -n 'proxy_pass' | head

Root Cause: The application defaults to listening on the loopback address, blocking external requests;
combined with inconsistent runtime environment versions compared to local. These two factors combined make “works locally, fails on server” difficult to diagnose.

Two Key Takeaways:

  • Always confirm the listening address when deploying Java projects.
    Default binding to localhost blocks external access, which is the most common reason for “port listening but inaccessible”.
  • Just because it runs locally doesn’t mean it runs on the server; verify runtime environment versions.
    If the compilation version is higher than the runtime version, loading will fail. Such discrepancies often have subtle hints in logs.

8 Common Pitfalls

  1. JDK Version Mismatch: The most common cause of deployment failure.
  2. Running Tomcat as root: Security risk.
  3. Not setting memory parameters: Defaults may not suit production.
  4. Management interface exposed with weak passwords: Common intrusion vector.
  5. Port 8080 exposed directly to public internet: Should go through reverse proxy.
  6. Not deleting sample applications: docs, examples are unnecessary.
  7. No log rotation: catalina.out will grow indefinitely.
  8. Not configuring systemd: No auto-start on boot, service lost after reboot.

Real Customer Feedback (From Cloud Market Reviews and Consultations for Similar Products)

The following excerpts are taken from real buyer reviews and consultations for similar operations services on Alibaba Cloud / Tencent Cloud / Huawei Cloud Cloud Markets (service providers include our store and peers, provided solely for reference of real feedback, not fictional tickets from this site):

1****n Rating: 5.0 Points 2018-01-04 18:04

d****9 Rating: 5.0 Points 2023-02-15 09:11 a****7 Rating: 5.0 Points 2023-02-15 09:10 d****8 Rating: 5.0 Points 2023-02-14 10:48 d****8 Rating: 5.0 Points 2023-02-14 10:43 a****3 Rating: 5.0 Points 2023-01-17 17:20

9****3 Rating: 5.0 Points 2021-07-22 10:31

FREE CHECKUP

Free server checkup

We will send a problem checklist and remediation advice within one business day — no obligation to engage.

135 410 16684
Weekdays Mon – Fri 10:00 – 22:00 (UTC+8) · 7×24 emergency response
Call now
CONTACT

Contact Us

How can we help?

* Required