Tomcat runs from the command line but fails as a systemd service because systemd operates with a restricted execution environment that lacks your interactive shell's configuration. This isolation often exposes hidden permission errors, missing environment variables, or strict SELinux policies that do not trigger during manual testing.
Understanding the Isolation of Systemd Services
When you start Tomcat manually by running
catalina.sh or
startup.sh, you are using your current user’s session. Your shell has loaded
.bashrc,
.profile, and other initialization files that set up the
JAVA_HOME,
CATALINA_HOME, and
PATH variables. The systemd unit file, however, starts a fresh process tree that does not inherit these shell-specific configurations. Consequently, even if your user can launch the application successfully, the service may fail immediately because it cannot locate the Java executable or the Tomcat installation directory.
This disconnect is the most common reason for the discrepancy between manual and service-based startup. You must ensure that all necessary paths and variables are explicitly defined within the systemd unit file itself, rather than relying on the environment of the logged-in user.
Configuring Environment Variables in the Unit File
To resolve missing variable issues, you should edit the Tomcat service file, typically located at
/etc/systemd/system/tomcat.service or
/usr/lib/systemd/system/tomcat.service. Inside this file, you will find a section dedicated to the service definition. You need to add an
Environment directive to explicitly set the required variables.
For example, you might add lines such as:
[Service]
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_BASE=/opt/tomcat"
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
After modifying the file, you must reload the systemd daemon to apply the changes. Run the following command:
sudo systemctl daemon-reload
Then, attempt to start the service again using
sudo systemctl start tomcat. If the issue was simply missing paths, this configuration should allow the service to initialize correctly. For more insights into how Linux permissions interact with system services, see
Understanding Linux Permissions: An In-depth Look at SELinux, OpenSSH, Systemd, Docker, and RPM-based Builds.
Checking File Ownership and Permissions
Another frequent cause of failure is incorrect file ownership. When you run Tomcat manually, you are likely executing it as your own user. However, best practices dictate that Tomcat should run under a dedicated, non-privileged user account (often named
tomcat or
tomcat9). If the service file specifies a different user than the one who owns the Tomcat files, the process will lack the necessary read and execute permissions.
You can check the current ownership of your Tomcat directory using the
ls -l command. If the files are owned by
root or your personal user, but the systemd service attempts to run as
tomcat, it will fail. To fix this, change the ownership recursively:
sudo chown -R tomcat:tomcat /opt/tomcat
Ensure that the service file reflects this user in the
User= and
Group= directives. Additionally, verify that the log directories have write permissions for this user. If the logs directory is not writable, Tomcat may crash silently upon startup because it cannot create its standard output logs.
Addressing SELinux Restrictions
On Oracle Linux and other RHEL-based distributions, SELinux (Security-Enhanced Linux) enforces strict access controls. Even if file permissions are correct, SELinux may prevent the Tomcat process from accessing specific ports, reading configuration files, or writing to log directories. This is particularly true if Tomcat is installed in a non-standard location like
/opt instead of the default
/var/lib/tomcat.
You can diagnose SELinux denials by checking the audit log. Run the following command to view recent denials related to Tomcat:
sudo ausearch -m avc -ts recent | grep tomcat
If you see denials indicating that the process was blocked from connecting to port 8080 or reading files, you have two options. First, you can temporarily set SELinux to permissive mode to confirm that SELinux is the culprit:
sudo setenforce 0
If Tomcat starts successfully in permissive mode, you should restore enforcing mode (
setenforce 1) and then apply the correct SELinux contexts. Use the
semanage command to allow Tomcat to bind to specific ports or use
restorecon to reset file contexts to their defaults. Properly configuring SELinux ensures long-term security while allowing the service to function.
Reviewing Logs for Specific Errors
When a service fails to start, systemd provides limited immediate feedback in the terminal. Instead, detailed error messages are stored in the journal. Use the
journactl command to retrieve the logs for the Tomcat service. This is the most effective way to pinpoint the exact line where the initialization failed.
Run the following command to see the last few hundred lines of logs:
sudo journalctl -u tomcat.service -n 200 --no-pager
Look for keywords like "Error", "Exception", "Permission denied", or "Cannot find". Common errors include specifying an invalid
JAVA_HOME path, encountering a classpath conflict, or failing to allocate memory due to cgroup limits. If the logs indicate a memory issue, you may need to adjust the
LimitNOFILE or memory limits in the systemd unit file. Understanding these logs is crucial for troubleshooting complex Linux environments, similar to methods discussed in
Troubleshooting Linux RHEL 7.
Verifying Port Availability
Sometimes, the service appears to fail because another process is already using port 8080 or 8443. If you ran Tomcat manually earlier and did not shut it down properly, the background processes might still be holding onto the ports. Check for existing Java processes using the
ps command:
ps aux | grep java
If you find stale processes, kill them using
kill -9 <PID>. Then, try starting the systemd service again. Ensure that no firewall rules are blocking the service from binding to the network interface, although this usually results in a timeout rather than a startup failure.
Frequently Asked Questions
Why does my Tomcat service fail immediately after starting?
It usually fails because the systemd environment lacks the
JAVA_HOME or
PATH variables that your interactive shell provides. You must define these explicitly in the
Environment section of the unit file.
How do I check if SELinux is blocking Tomcat?
Run
sudo ausearch -m avc -ts recent to look for access vector cache denials involving the tomcat process. If denials appear, you need to adjust SELinux policies or file contexts accordingly.
Can I run Tomcat as root in systemd?
While technically possible, it is strongly discouraged for security reasons. Running as root increases the risk if the web application is compromised. Create a dedicated user like
tomcat and configure the service to run under that identity.
What if the logs directory is not writable?
Tomcat will fail to start if it cannot write to the
logs directory inside
CATALINA_BASE. Ensure the user specified in the
User= directive has write permissions to this folder using
chown and
chmod.
How do I reload the systemd configuration after editing the file?
Always run
sudo systemctl daemon-reload after modifying any unit file in
/etc/systemd/system or
/usr/lib/systemd/system. This forces systemd to re-read the configuration files before you restart the service.
Does umask affect Tomcat's startup behavior?
Yes, the umask determines the default permissions for newly created files. If your umask is too restrictive, Tomcat might not be able to create temporary files or logs. You can set the umask in the systemd unit file using the
UMask= directive, typically setting it to
0022 for standard access.