Installing Tomcat on Mac OS X May 13, 2008

Tomcat

Having to use Tomcat for an important project I am working on, I installed it on my Mac. However, in the beginning, I didn’t make much effort: I just uncompressed the official archive in my /usr/local, and I was able to start and stop Tomcat manually, that’s all… But today I decided to learn a few things about Mac OS X, trying to install it properly. Let’s see what I did!

Updated

  • Friday, January 30, 2009: added instructions to create an admin user for Tomcat; corrected and enhanced instructions for permissions; added comments on deploying applications which need access to the filesystem; added link to Lambda Probe; corrected the StartupItem files; many thanks to Derk Norton for suggesting some of those corrections.
  • Saturday, September 4, 2010: replaced instructions about setting up Tomcat as a StartupItem by instructions to set it up as a launchd job; added a section about Tomcat 7.

This tutorial is based on my experience installing Tomcat 6.0.29 on a Mac OS X Snow Leopard 10.6.4 system, and I have not tested those instructions with other versions of the operating system.

Downloading and uncompressing Tomcat

First, download from Tomcat’s website the version of Tomcat you want to use. Choose the .tar.gz file in the Core section of the Binary Distributions. Let’s say you just downloaded it to your Downloads directory (usually /Users/username/Downloads/). Open a Terminal and execute the following commands:

sudo mkdir -p /usr/local
cd /usr/local
sudo tar xvzf ~/Downloads/apache-tomcat-6.0.29.tar.gz
sudo ln -s apache-tomcat-6.0.29 tomcat

You also need to create an admin user for Tomcat. I will assume you are familiar with vim, but you can use any other text editor. Let’s edit the right file:

sudo vim /usr/local/tomcat/conf/tomcat-users.xml

Then replace its contents by the following:

/usr/local/tomcat/conf/tomcat-users.xml

1
2
3
4
5
6
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
    <role rolename="manager"/>
    <role rolename="admin"/>
    <user username="admin" password="pass" roles="admin,manager"/>
</tomcat>

Of course, you can replace the word pass by any password you want to use.

Tomcat should already be working, but we should not run it as root, since it may be a huge security issue. That is why we need to first create an unprivileged user as well as its primary group.

Creating an unprivileged user

We will be using the command-line utility dscl for this. First, you need to choose the User ID and the Group ID, which must be a positive integer, lower than 500, since User IDs above 500 are reserved to normal users. You just have to pick a number between 0 and 500 which is not yet used by any other user or group. Check this out with the commands:

dscl . -list /Groups PrimaryGroupID
dscl . -list /Users  UniqueID

For this tutorial, I will be using the number 107. We can create the group and the user with the following commands:

sudo dscl . -create /Groups/_tomcat PrimaryGroupID 107
sudo dscl . -create /Groups/_tomcat RealName       "Tomcat Users"
sudo dscl . -create /Groups/_tomcat Password       \*
sudo dscl . -create /Users/_tomcat  UniqueID       107
sudo dscl . -create /Users/_tomcat  PrimaryGroupID 107
sudo dscl . -create /Users/_tomcat  HomeDirectory  /usr/local/tomcat
sudo dscl . -create /Users/_tomcat  UserShell      /usr/bin/false
sudo dscl . -create /Users/_tomcat  RealName       "Tomcat Administrator"
sudo dscl . -create /Users/_tomcat  Password       \*

Choosing /usr/bin/false as the UserShell, and setting the Password to “*” turns this account unusable as a standard user account.

Setting permissions

Since Tomcat will be run as the _tomcat user, you must change the ownership of some directories and files. Just run the following commands:

cd /usr/local/tomcat
sudo chown -R root:wheel .
sudo chmod 644 conf/*
sudo chown root:_tomcat conf/tomcat-users.xml
sudo chmod 640 conf/tomcat-users.xml
sudo mkdir conf/Catalina
sudo chown _tomcat:_tomcat conf/Catalina
sudo chown _tomcat:admin logs temp webapps work
sudo chmod 2770 logs temp webapps work
cd -

This way, Tomcat should have the right permissions for all the directories it needs to write into, and at the same time, you won’t need to use root privileges to read the logs, and so on.

But be careful when developing an application which needs to have access to some directories in the filesystem (for storing files inside the web application for example): you might need to give Tomcat specific permissions on those directories for this to work.

However, I think that a good way to avoid a lot of Unix permissions headaches is to deploy your web applications only using tools such as the Tomcat Manager, or the excellent Lambda Probe.

Setting Tomcat as a launchd job

If we want to have Tomcat start up automatically at boot time, or if we simply want to easily start it and stop it without having to think about details like paths and permissions, it’s a good idea to use it as a launchd job.

The first big thing we need to do for that is to create a wrapper script that will start Tomcat and not return until the Tomcat process exits, which is not what the startup.sh and catalina.sh scripts do, as they launch Tomcat as a background process.

To do that, let’s first create a new shell script in Tomcat’s bin directory:

sudo touch /usr/local/tomcat/bin/tomcat-launchd.sh
sudo chmod +x /usr/local/tomcat/bin/tomcat-launchd.sh
sudo vim /usr/local/tomcat/bin/tomcat-launchd.sh

Then put the following content:

/usr/local/tomcat/bin/tomcat-launchd.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#! /bin/sh

# tomcat-launchd.sh
#
# Wrapper script that starts Tomcat and waits for the Tomcat process
# to exit. This is needed for proper interaction with launchd.

#---------------------------------------------------------
# Helper functions
#---------------------------------------------------------

# NOTE: We are inheriting CATALINA_HOME from launchd, because its value
#       was defined in the launchd plist configuration file.

function shutdown() {
   
    # Bye Tomcat!
    echo "Shutting down Tomcat... "
    $CATALINA_HOME/bin/catalina.sh stop
    echo "done."
   
    # Cleaning up the temporary file
    rm -f $CATALINA_PID
   
}


function startup() {
   
    # Define the file where we want the Tomcat process ID to be stored.
    export CATALINA_PID=$(mktemp /tmp/`basename -s .sh $0`.XXXXXX)
    if [ $? -ne 0 ]
    then
        echo "$0: Failed to create temporary file. Aborting."
        exit 1
    fi
    rm -f $CATALINA_PID
   
    # Let's go!
    echo "Starting up Tomcat... "
    . $CATALINA_HOME/bin/catalina.sh start
   
    # Register the shutdown function as callback to execute when a signal
    # is sent to this process.
    trap shutdown HUP INT QUIT ABRT KILL ALRM TERM TSTP
    echo "done."
   
}


function wait_for_tomcat_to_exit() {
    echo "Waiting for Tomcat to exit (PID: `cat $CATALINA_PID`)... "
    wait `cat $CATALINA_PID`
    echo "done waiting for Tomcat to exit."
}


#---------------------------------------------------------
# Let's go
#---------------------------------------------------------

startup
wait_for_tomcat_to_exit

Then, we need to create a configuration plist file for the Tomcat launchd job:

sudo vim /Library/LaunchDaemons/org.apache.tomcat.plist

Then put the following content:

/Library/LaunchDaemons/org.apache.tomcat.plist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>org.apache.tomcat</string>
    <key>ServiceDescription</key>
    <string>Tomcat Servlet/JSP Server</string>
    <key>UserName</key>
    <string>_tomcat</string>
    <key>GroupName</key>
    <string>_tomcat</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>CATALINA_HOME</key>
        <string>/usr/local/tomcat</string>
        <key>JAVA_HOME</key>
        <string>/System/Library/Frameworks/JavaVM.framework/Home</string>
    </dict>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/tomcat/bin/tomcat-launchd.sh</string>
    </array>
    <key>StandardOutPath</key>
    <string>/usr/local/tomcat/logs/launchd-stdout.log</string>
    <key>StandardErrorPath</key>
    <string>/usr/local/tomcat/logs/launchd-stderr.log</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>

Using launchd to manage Tomcat

The first thing we need to do is load this configuration file:

sudo launchctl load /Library/LaunchDaemons/org.apache.tomcat.plist

Tomcat should be launching as the user _tomcat, and if you go to http://localhost:8080/ using your favorite browser, you should get to the homepage of your new Tomcat server.

With the above launchd configuration file, Tomcat is pretty much guaranteed to always be running on your system: it will be restarted automatically for you, by launchd, even if it exits for any reason (for example, a crash, or a reboot). The key that controls the fact that Tomcat is launched at boot time is actually the key named RunAtLoad. So, if you don’t like this behavior, you can always set that key to false, and reload the configuration:

sudo launchctl unload /Library/LaunchDaemons/org.apache.tomcat.plist
sudo launchctl load   /Library/LaunchDaemons/org.apache.tomcat.plist

You should run those two commands to reload the configuration anytime you make a change to the plist file.

Now, let’s say that you want to just restart the Tomcat server (maybe because you changed some of Tomcat’s configuration files). With the above launchd configuration file, all you have to do is this:

sudo launchctl stop org.apache.tomcat

Indeed, you don’t even need to do an equivalent start call, because launchd will take care of relaunching the Tomcat server. That is because we set the key KeepAlive to true. If you prefer to be able to really shut down Tomcat, then you can just change that key to false, reload the launchd configuration, and then, to achieve the same result, you’ll have to run two commands now:

sudo launchctl stop  org.apache.tomcat
sudo launchctl start org.apache.tomcat

Of course, if you omit the start command, then the server will be permanently shut down (until the next reboot, if you still have the key RunAtLoad set to true).

You can customize the behavior of launchd with regard to Tomcat even further, but I suggest you just check out launchd plists’s man page if you want to learn more about that.

Update for Tomcat 7

Tomcat 7 is around the corner. It’s still considered beta software at the moment (at time of writing, the latest released version of Tomcat is 7.0.2), but it seems to work pretty well. I tried to see if those instructions would work with Tomcat 7, and apparently, they do, except for one thing. The content you need to put in the tomcat-users.xml file is a little different:

/usr/local/tomcat/conf/tomcat-users.xml

1
2
3
4
5
6
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
    <role rolename="manager-gui"/>
    <role rolename="admin"/>
    <user username="admin" password="pass" roles="admin,manager-gui"/>
</tomcat>

The difference is that I had to replace the role manager by manager-gui. Tomcat’s migration page explains that difference very well.

Other than that, you should be able to install Tomcat 7 on your Mac using those instructions.

Enjoy!

9 Comments
Derk Norton January 28th, 2009

Excellent!!!

This is just what I was looking for! Thanks!

Derk Norton January 28th, 2009

A couple of corrections…

Hey Joel, I found a couple of things that need correcting in the directions above:

Under “Changing Ownership” you will need to run the same command on the actual apache-tomcat-6.0.16 directory as well since the recursion (with the -R option only) won’t traverse the symbolic link.

In the StartupParameters.plist file a binding to the restart command is missing.

And most importantly, in the RestartService function in the Tomcat startup script the “stop” and “start” commands are called in the wrong order! It should be “stop” then “start” :-)

Cheers,
Derk

Joel January 30th, 2009

Re: A couple of corrections…

Hi Derk!

Many thanks for your comments. I appreciate it very much, since it helps me enhancing my tutorials. I changed many things in the article today to correct the mistakes you found, but I also changed some other things.

Concerning the “Changing Ownership” section, you were right, it didn’t work. But I wanted to avoid as much as possible using the name “apache-tomcat-6.0.16″ since it can change (actually, the current version is 6.0.18), and I want as much as possible this tutorial to be a copy-paste how-to. So I found another way to traverse the symbolic link: using the -H option will traverse it. Anyway, I completely changed this section, because it was a very basic way of dealing with permissions issues, and I had some more tips on how to do it better.

For the rest, nothing else to say, except that I changed the content like you suggested, and that I mentioned your name in the top of the article, in the “Updated” section, to thank you.

Glad to have such good readers!

See ya!

Tim February 10th, 2011

Just installed Tomcat 7.0.8 with your instructions. After 2 years of using Tomcat with horrible security and no automatic startup, this is a welcome change. Thanks for posting this! Much appreciated!

Louis March 3rd, 2011

Thank you for this walkthrough!
The script used by launchd is nicer than the solution in http://www.malisphoto.com/tips/tomcatonosx.html .
The RunAtLoad and KeepAlive parameters are useful!

I used a small script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/sh

# Start/stop script for tomcat.
# Louis Romero, 03/03/2011

case "$1" in
    start)
        echo "Starting tomcat (loading into launchd)."
        launchctl load /Library/LaunchDaemons/org.apache.tomcat.plist
        ;;
    stop)
        echo "Stopping tomcat (unloading from launchd)."
        launchctl unload /Library/LaunchDaemons/org.apache.tomcat.plist
        ;;
    restart)
        $0 stop
        $0 start
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac
exit 0

and an alias in my ~/.zshrc (or equivalent shell rc) to call it:

function tomcat() { sudo ~/bin/tomcat.sh "$@"; }

That way, I use it as follow:

tomcat {start|stop|restart}

An interesting addition would be to have a `status’ argument, to see if tomcat is running, but I’m not sure what the right solution would be. Any idea?
Readers, feel free to review my script to!

cozappz July 12th, 2012

Hi Joel!

A small thing I noticed on osx 10.7: the home folder key is NFSHomeDirectory instead of HomeDirectory.
So the command may be:
sudo dscl . -create /Users/_tomcat NFSHomeDirectory /usr/local/tomcat

Thanks for the article.

Pulkit SInghal February 15th, 2013

For the “Setting Permissions” section:

If you already have the conf/Catalina/localhost/ROOT.xml present then the following two lines:
====
sudo mkdir conf/Catalina
sudo chown _tomcat:_tomcat conf/Catalina
====

Need to be changed to something like:
====
sudo chmod -R 744 conf/Catalina
sudo chown -R _tomcat:_tomcat conf/Catalina
====

Otherwise you will face errors such as:
1) SEVERE: Unable to create directory for deployment: /usr/local/tomcat/conf/Catalina/localhost
2) FileNotFoundException due to permission denied for ROOT.xml

Neil Robertson August 3rd, 2013

Joel, this article was excellent and I learned a lot from it.

I noticed a small mistake in the tomcat-users.xml file. The closing tag should be , not .

Thanks!

Neil

Neil Robertson August 3rd, 2013

What I meant to say was:

The closing tag should be tomcat-users, not tomcat.

Thanks!

Neil

Leave a Reply