Using TomCat's Virtual Hosts


Home Page | Comments | Articles | Faq | Documents | Search | Archive | Tales from the Machine Room | Contribute | Login/Register

Usually, when you have multiple 'contexts' (applications) in TomCat, they are divided into different "directories" or different instances. Having multiple instances of TomCat allow to stop/start them on their own (so they don't step on each others toes) but it also means that you have multiple servers running at once with more memory and processor consumptions.

Splitting applications in contexts is fine, but sometimes you don't want to add the /context to your URL, in that case you must use Virtual Hosts in Apache to "point" the '/' of the vhost to the right Context.

TomCat provide an alternative on his own: TomCat's Virtual Hosts.

There are two different way to define virtual hosts in TomCat, exactly like there are two different way to define them in Apache you can have PORT or IP based VirtualHosts, where different 'hosts' listen on different IPs or Ports on the same machine and handle different Contexts (directories), or Name-based virtual hosts where the various Contexts are split by the hostname in the http request.

Both way are done by defining them in the 'server.xml' configuration file.

To have TomCat listening on different IPs or Ports you define different "services" in the server.xml. Removing a lot of stuff from the server.xml, a default definition is composed like this:

<?xml version='1.0' encoding='utf-8'?>
<Server port="8007" shutdown="SHUTDOWN">

...a lot of stuff...

<Service name="Catalina">
	<Connector port="8080" address="127.0.0.1"
    		protocol="HTTP/1.1" 
		connectionTimeout="20000" 
		redirectPort="8443" />
	<Connector port="8009" address="127.0.0.1" 
		protocol="AJP/1.3" redirectPort="8443" />
	<Engine name="Catalina" defaultHost="localhost">
		<Realm className="org.apache.catalina.realm.UserDatabaseRealm" 
			resourceName="UserDatabase"/>
		<Host name="localhost"  appBase="webapps"
			unpackWARs="false" autoDeploy="false"
			xmlValidation="false" xmlNamespaceAware="false">
		</Host>
	</Engine>
</Service>

</Server>

The trick is the <Service > ... </Service> block. Each block define one "service" that can listen on multiple port/ip and handle different "hosts" each one can have a different 'appBase' and one or more 'Context'. Note that usually the 'address' option is not specified so the 'Service' listen to all the available ip addresses instead of being tied to one ip address only.

To add another 'host' that listen to a specific port/ip and handle a different set of contexts (or only one), we simply cut & paste the <Service> block and then change the bits that we need to change (minimum: the 'name' and the ip/ports):

<Service name="Catalina">
  	..just like above...
</Service>

<Service name="Catalina2">
	<Connector port="8081" address="127.0.0.1"
		protocol="HTTP/1.1" connectionTimeout="20000" 
		redirectPort="8443" />
	<Connector port="8019" address="127.0.0.1" 
		protocol="AJP/1.3" redirectPort="8443" />
	<Engine name="Catalina" defaultHost="localhost">
		<Realm className="org.apache.catalina.realm.UserDatabaseRealm" 
			resourceName="UserDatabase"/>

		<Host name="localhost" appBase="wathever"
			unpackWARs="false" autoDeploy="false"
			xmlValidation="false"
			xmlNamespaceAware="false">
		</Host>
	</Engine>
</Service>

In this case we've added a new 'service' listening on port 8081/8019 (for http and ajp) and with a 'appBase' of 'wathever' (that's the directory under which the web apps are deployed). Of course if we want to keep the services listening on the same Port but on different IPs we only need to change the 'address' parameter in the Connectors.

An alternative is to have name-based virtual Hosts, in this case we have only one "service" defined but we define multiple <Host> block in it. Just keep the previous configuration as example:

<Service name="Catalina">
	<Connector port="8080" address="127.0.0.1"
		protocol="HTTP/1.1" 
		connectionTimeout="20000" 
		redirectPort="8443" />
	<Connector port="8009" address="127.0.0.1" 
		protocol="AJP/1.3" redirectPort="8443" />
	<Engine name="Catalina" defaultHost="localhost">

		<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
			resourceName="UserDatabase"/>

		<Host name="localhost" appBase="webapps"
			unpackWARs="false" autoDeploy="false"
			xmlValidation="false" 
			xmlNamespaceAware="false">
		</Host>

		<Host name="hostname_01" appBase="wathever"
			unpackWARs="false" autoDeploy="false"
			xmlValidation="false" 
			xmlNamespaceAware="false">
		</Host>
	</Engine>
<Service>

In this case we define 2 virtual hosts, one named "localhost" and one named "hostname_01". Of course the hostnames have to be the FQDN name for the host, note that one of the Hosts defined MUST MATCH THE 'defaultHost' defined for the 'Engine'. The 'default' is obviously picked if the name in the request doesn't match any of the vhost defined.

If the same 'host' has to respond to multiple names (it happens usually with and without the 'www'), you can define Aliases with the <Alias>...</Alias> block inside the Host block itself, example:

<Host name="www.somedomain.com"  appBase="wathever"
	unpackWARs="false" autoDeploy="false"
	xmlValidation="false" xmlNamespaceAware="false">
	<Alias>somedomain.com</Alias>
</Host>

In any case, if we want to have the 'root' context of a Vhost associated with a specific application, we have to define a Context to 'tie' the application with the URL, something like this:

<Host...
<Context path="" docBase="examples"/>
</Host>

This way when the specific Host will be requested (either by IP or by name), the application in the specified directory (in the webApps for that Host) will be served.

The major problem in using this kind of configuration is that the server.xml file has to be edited every time a new host/service is defined and then TomCat has to be restarted. Unfortunately, unlike Apache, TomCat does not support an "include ..." option to simply include at run-time all the files in a specific directory, so we cannot just drop a new config file somewhere and then give him a restart to have the new config file picked up.

An option could be to have the configuration file "built" at startup by a script that checks and then "merge" multiple configuration files specified in different directories (or defined by some sort of meta-commands in the XML like <!--include-->). This can be done but it requires to build a "wrapper" around the default tomcat start/stop script (catalina.sh) and an obvious disadvantage is that starting tomcat with the default script will probably produce unexpected results.


Comments are added when and more important if I have the time to review them and after removing Spam, Crap, Phishing and the like. So don't hold your breath. And if your comment doesn't appear, is probably becuase it wasn't worth it.

1 message this document does not accept new posts
Virtual host By Virtual host - posted 11/02/2011 14:32

Ciao Davide, intanto grazie per la spiegazione, ma mi sono arenato da qualche parte perché tutti i vhost mi rimandano su quello di default. Penso di non aver capito il valore della variabile appBase ed i parametri da impostare alla Context. Fisicamente su disco cosa deve risultare? Io ho fatto due deploy di due applicazioni dal formato WAR. Cosa dovrei mettere nei parametri appena detti?

Grazie

Giorgio

--
Virtual host


Previous Next

Davide Bianchi, works as Unix/Linux administrator for an hosting provider in The Netherlands.

Do you want to contribute? read how.  
 


This site is made by me with blood, sweat and gunpowder, if you want to republish or redistribute any part of it, please drop me (or the author of the article if is not me) a mail.


This site was composed with VIM, now is composed with VIM and the (in)famous CMS FdT.

This site isn't optimized for vision with any specific browser, nor it requires special fonts or resolution.
You're free to see it as you wish.

Web Interoperability Pleadge Support This Project
Powered By Gojira