eXo Platform Docker Composed

eXo and Docker compose

UPDATED 06/09/2015: Added support for MYSQL_ environment variables in exoplf image allowing for a fully custom exo+mysql setup (i.e.: change mysql password, database name etc.)

1. Introduction

This post's goal is to explain how to build a dockerized eXo Platform following Docker's core principle of "one task, one container".

In this example eXo platform will be using MySql as the database making it more production ready.

Also eXo's JCR data as well as MySql data will be stored in separate and dedicated Docker Data containers.
To link all this containers together we will be using the newly released docker-compose tool.

The final docker composition will look like this:

eXo PLF + MySQL Docker Composition

2. Containers Descriptions

We will need 4 containers:

  • mysql
    • using the official mysql/mysql-server:latest image
  • mysqldata
    • will expose volume /var/lib/mysql
  • exoplf
    • using a tweaked exo platform image, supporting server.xml customization and custom jcr data volume
  • exodata
    • will expose volume /srv/exo where eXo's JCR data will be stored

3. Containers Docker Files

Lets describe these containers.


We will pull the official mysql/mysql-server:latest available on DockerHub Registry here:


It supports the initial creation of the database and user through Environment variables.


You can change any of these but note you will need to adjust the server.xml file in the exo-plf container (described later on)

This container will mount /var/lib/mysql from the mysql-data container

Mysql Data

This is the container that will store the Mysql data directory.
It is very simple.
It's Dockerfile looks like this:

FROM busybox:latest  
MAINTAINER Eric Taïeb Walch <teknologist@gmail.com>  
RUN mkdir -p /var/lib/mysql && chmod -R 777 /var/lib/mysql  
VOLUME /var/lib/mysql  

The important line here is:

VOLUME /var/lib/mysql  

It makes the /var/lib/mysql directory mountable by other containers.

DockerHub image is teknologist/mysql-docker-data available at the DockerHub Registry here:


GitHub repo: https://github.com/teknologist/mysql-docker-data

eXo Data

Same as above, this image will be used to spawn a container that holds /srv/exo directory.
This directory contains all the persistent data for eXo's JCR files.

Again it is very simple.
It's Dockerfile looks like this:

FROM busybox:latest  
MAINTAINER Eric Taïeb Walch <teknologist@gmail.com>  
RUN mkdir -p /srv/exo  
RUN chmod 777 -R /srv/exo  
VOLUME /srv/exo  

DockerHub image is teknologist/exo-data

Available at the DockerHub Registry:


GitHub repo: https://github.com/teknologist/docker-data

eXo Platform

This image is a fork of eXo's original image.

Changes are:

  • Upgraded to PLF 4.2.0-RC1
  • Added MySQL JDBC Connector to lib/ directory
  • Added server.xml to switch default HSQL database to MySQL
  • Added environment variables to setup mysql connection properties

The github repo for this Docker image is available here:

As you can see the server.xml refers to a mysql database host named mysql.
This is important as this is how containers are linked using docker-compose.

Short story:
If you name a container mysql and link it in the docker-compose.yml file, then docker-compose will set an entry in the hosts file named mysql pointing at the IP of the container.
This makes linking container trivial.

The Docker image is teknologist/ubuntu-jdk7-exo-mysql available at the DockerHub Registry here:


4. Docker composing all these containers together

This is where the magic happens. By writing a simple 25 lines docker-compose.ym we will spawn an entire eXo Platform infrastructure in minutes.

First of all you need to have a recent docker version installed (1.3 or higher) for docker-compose to work.
As of this writing I am using Docker version 1.6.2, build 7c8fca2.
You can find how to install docker here: https://docs.docker.com/installation/

Next step is installing docker-compose.
On supported platforms (GNU/Linux, OSX, etc.) this is as simple as entering the following commands:

curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose  
chmod +x /usr/local/bin/docker-compose  

See https://docs.docker.com/compose/install/ for more information.

Once your docker + docker-compose environment is ready, create a directory, exo-compose for example. In that directory create a file called docker-compose.yml with the following content:

  restart: on-failure:5
  image: teknologist/exo-data
  restart: on-failure:5
  image: teknologist/mysql-docker-data
  restart: on-failure:5
  image: mysql/mysql-server:latest
    MYSQL_USER: exo
    MYSQL_PASSWORD: changeme
  - mysqldata
  restart: on-failure:5
  - 8080:8080/tcp
  image: teknologist/ubuntu-jdk7-exo-mysql
    MYSQL_USER: exo
    MYSQL_PASSWORD: changeme
  - mysql
  - exodata

UPDATED 09/16/2015 to include MYSQL_ environment variables to the exoplf service.

Beware this a yaml file so indentation is of the utmost importance. Gist available here.

As you can see 3 things are important here and let you link everything together:


This directive instructs each container to mount the volumes from the respective data container.

 - mysql

This is the main compose directive that links exo platform to mysql. With this directive, docker-compose:

  • creates a network connection between the exo and mysql containers
  • creates an entry named mysql in eXo container's /etc/hosts file pointing to the mysql container IP

If you look back at the server.xml file, this is how the mysql host in the jdbc connection string gets resolved.

You can find docker-compose full documentation here:

Last but not least, both mysql and exoplf services' MYSQL_ environment variables:

    MYSQL_USER: exo
    MYSQL_PASSWORD: changeme

NOTE: MYSQL_ROOT_PASSWORD is only needed in mysql service

NOTE: You may want (highly recommended) change mysql exo user password. In order to do that you need to pull my ubuntu-jdk7-exo-mysql image and modify the server.xml. I recommend setting it up as an environment variable in the docker-compose.yml exoplf section, then changing the server.xml to use that environment variable. I may update the images later to reflect this. UPDATE: I have updated the exoplf image to support MYSQL_ environment variables. It is know trivial to setup custom MySQL connection details. Just set the MYSQL_ environment variables correctly on both exoplf and mysql services in docker-compose.yml - Content has been updated in this post to reflect this.

Now you are ready to spawn the infrastructure. In the directory where you created the docker-compose.yml file, just launch: docker-compose up

eric@morpheus:~/exo-compose$ docker-compose up  
Creating exocompose_mysqldata_1...  
Creating exocompose_exodata_1...  
Creating exocompose_mysql_1...  
Creating exocompose_exoplf_1...  
Attaching to exocompose_mysql_1, exocompose_exoplf_1  
mysql_1  | Running mysql_install_db  
mysql_1  | 2015-06-08 12:07:00 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).  
mysql_1  | 2015-06-08 12:07:00 0 [Note] /usr/sbin/mysqld (mysqld 5.6.25) starting as process 22 ...  
exoplf_1 | 2015-06-08 12:07:01,891 | INFO  | The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib [o.a.catalina.core.AprLifecycleListener<main>]  
exoplf_1 | 2015-06-08 12:07:02,334 | INFO  | Initializing ProtocolHandler ["http-nio-"] [o.apache.coyote.http11.Http11NioProtocol<main>]  
exoplf_1 | 2015-06-08 12:07:02,384 | INFO  | Using a shared selector for servlet write/read [o.apache.tomcat.util.net.NioSelectorPool<main>]  
exoplf_1 | 2015-06-08 12:07:02,386 | INFO  | Initializing ProtocolHandler ["ajp-bio-"] [org.apache.coyote.ajp.AjpProtocol<main>]  
exoplf_1 | 2015-06-08 12:07:02,405 | INFO  | Initialization processed in 1216 ms [org.apache.catalina.startup.Catalina<main>]  

NOTE: If you want to run your services in the background, you can pass the -d flag (for daemon mode) to docker-compose up and then use docker-compose ps to see what is currently running. Of course, each docker container supports classic docker commands, so you could check exoplf's logs via a docker logs commands.

I like to start the first time without -d so I can see what is going on in the initial containers setup.

NOTE: Now the thing is that docker-compose starts containers fast and the initial startup of mysql creates the database and user so at immediate startup exo's container will show an error connecting to the database. You can ignore that because while exo continues its startup process the database will become available. This only happens the first time as the databases need to be created.

Depending on your docker's host performance after some time you'll see:

exoplf_1 | 2015-06-08 12:09:11,830 | INFO  | Starting ProtocolHandler ["http-nio-"] [o.apache.coyote.http11.Http11NioProtocol<main>]  
exoplf_1 | 2015-06-08 12:09:11,835 | INFO  | Starting ProtocolHandler ["ajp-bio-"] [org.apache.coyote.ajp.AjpProtocol<main>]  
exoplf_1 | 2015-06-08 12:09:11,838 | INFO  | Server startup in 129433 ms [org.apache.catalina.startup.Catalina<main>]  

And you can connect to your docker's host port 8080 (provided you didn't change it in the docker-compose ports directive) and you'll be greeted with the initial eXo setup page:

eXo Welcome page

Complete the rest of the setup wizard and you are set.

5. Operating your eXo/Mysql Docker composition

You can stop and start your infrastructure as you wish. Persistence data is kept in the data containers so no loss of data/configuration.

Stopping your existing infrastructure

If you started in foreground , just hit [CTRL]-C.
If you started in background, just enter: docker-compose stop

eric@morpheus:~/exo-compose$ docker-compose stop  
Stopping exocompose_exoplf_1...  
Stopping exocompose_mysql_1...  

Note you don't see anything related to the data containers as they don't really run, they just provide volumes.

Starting your existing infrastructure

Again, just enter: docker-compose start

eric@morpheus:~/exo-compose$ docker-compose start  
Starting exocompose_mysqldata_1...  
Starting exocompose_exodata_1...  
Starting exocompose_mysql_1...  
Starting exocompose_exoplf_1...  

To see what is currently running in your compose infrastructure

Just enter: docker-compose ps

eric@morpheus:~/exo-compose$ docker-compose ps  
         Name                       Command               State            Ports
exocompose_exodata_1     /bin/sh                          Exit 0  
exocompose_exoplf_1      /bin/sh -c ${EXO_APP_DIR}/ ...   Up>8080/tcp  
exocompose_mysql_1       /entrypoint.sh mysqld            Up       3306/tcp  
exocompose_mysqldata_1   /bin/sh                          Exit 0  

Again, you see the data containers are not in running state and that is normal.
Also note mysql port 3306 is not exposed outside the container. docker-compose does all the tunnelling for you.

To see your container's logs

If you want to tail exoplf's log just type: docker logs exocompose_exoplf_1

eric@morpheus:~/exo-compose$ docker logs exocompose_exoplf_1  
2015-06-08 12:22:12,758 | INFO  | The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib [o.a.catalina.core.AprLifecycleListener<main>]  
2015-06-08 12:22:13,129 | INFO  | Initializing ProtocolHandler ["http-nio-"] [o.apache.coyote.http11.Http11NioProtocol<main>]  
2015-06-08 12:22:13,177 | INFO  | Using a shared selector for servlet write/read [o.apache.tomcat.util.net.NioSelectorPool<main>]  
2015-06-08 12:22:13,179 | INFO  | Initializing ProtocolHandler ["ajp-bio-"] [org.apache.coyote.ajp.AjpProtocol<main>]  
2015-06-08 12:22:13,198 | INFO  | Initialization processed in 1274 ms [org.apache.catalina.startup.Catalina<main>]  
2015-06-08 12:22:13,517 | INFO  | Starting service Catalina [org.apache.catalina.core.StandardService<main>]  
2015-06-08 12:22:13,517 | INFO  | Starting Servlet Engine: Apache Tomcat/7.0.55 [org.apache.catalina.core.StandardEngine<main>]  
2015-06-08 12:22:13,543 | INFO  | Deploying web application archive /opt/exo/platform-community-4.2.0-RC1/webapps/forum-gadgets.war [org.apache.catalina.startup.HostConfig<localhost-startStop-5>]  
2015-06-08 12:22:13,543 | INFO  | Deploying web application archive /opt/exo/platform-community-4.2.0-RC1/webapps/wiki.war [org.apache.catalina.startup.HostConfig<localhost-startStop-9>]  
2015-06-08 12:22:13,543 | INFO  | Deploying web application archive /opt/exo/platform-community-4.2.0-RC1/webapps/eXoPlatformResources.war [org.apache.catalina.startup.HostConfig<localhost-startStop-10>]  

Again, each of this containers supports all docker commands.

6. Final thoughts

As you can see docker-compose makes building docker based infrastructures very easy.

Other tools such as Google's Kubernetes are very promising too. They not only let you compose but also scale (through minions, yes I didn't invent that).

Another very promising tool I have used to explore docker composition on CoreOS is Rancher

It looks like we are moving more and more from VM based cloud infrastructure to highly dense container based ones. Think lower overhead (if not zero), think also green!

Thanks for reading, sharing, commenting, +1 ing etc...