Stumble Through Guide To Running Lucee Docker


Docker’s an ecosystem you have to immerse yourself into to make the most of it. But I can’t blame folks for wanting to just get in there and see what’s going on without all the reading and stuff…

I’ve put together what i call a “stumble through guide” for folks trying to get to grips with Docker, using Lucee server as an example. It’s a common approach to getting into Docker as described by the many questions I field from folks getting started. Most of all it’s a really great example of how you wouldn’t do development in this environment :wink: Still, stumbling about is often how we all learn things and with luck this will get you there quickly.

Make sure you have installed some form of Docker client and Docker engine.

$ docker -v
Docker version 17.04.0-ce, build 4845c56

And you know the Docker host IP. Might be localhost, might be an internal IP. Depending on your set up you may be able to tell like so.

$ docker-machine ip

Ok, lets just go for gold and spin up one of the official lucee docker images – Lucee 5 with NGINX.

$ docker run lucee/lucee51-nginx
Unable to find image 'lucee/lucee51-nginx:latest' locally
latest: Pulling from lucee/lucee51-nginx
693502eb7dfb: Already exists
...snip...8<... much gumph here ...snip...8<...
2017-04-22 00:34:24.398 loaded security
2017-04-22 00:34:24.399 loaded lib
22-Apr-2017 00:34:24.454 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-apr-8888"]
22-Apr-2017 00:34:24.459 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-apr-8009"]
22-Apr-2017 00:34:24.460 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 2990 ms

You’re going to get bored of that pretty quickly as it doesn’t do anything. You are looking at the STDOUT or console output of the Lucee servlet container. By default the container has no external ports nominated so nothing can talk to it.

CONTROL+C to kill the running container.

You need to nominate the container port and map it to an external port on the host. For example, NGINX port 80 inside the container to port 8080 on your desktop.

docker run -p 8080:80 lucee/lucee51-nginx

If all is going to plan you should be able to pull up the default page for the container at <-- note your specific IP may be different.

Next you’re going to feel a burning desire to modify the code that makes up that page. A container is deliberately wrapped up and impenetrable, so we will need to punch “a little hole” in the container to let our changes through. This hole is called a volume and it’s a mapping between your desktop file system and a folder inside the container.

Let’s start with a real simple set up of a folder with a single index.cfm file containing <h1>hello world</h1>.

Here’s an example. There’s no cut and paste option here; you will need to modify the file paths to follow your own desktop file structure.

$ mkdir -p /Users/modius/code/lucee-docker
$ cd /Users/modius/code/lucee-docker
$ echo “<h1>hello world</h1>” > index.cfm
$ docker run -p 8080:80 -v /Users/modius/code/lucee-docker:/var/www lucee/lucee51-nginx

Looking closely at the VOLUME:

-v /Users/modius/code/lucee-docker:/var/www

The first half is the location on your desktop:


The second half is the location within the container itself:


Technically the volume is between the docker host file system and the container filesystem and the shared folder is between the host OS and the docker host but i digress. The VM layer will automatically sync file changes between these locations.

The files are in this location because the Lucee-NGINX Docker image has defined this as the location to store these files. Where you modify files within a container is going to vary from image to image. Note, this is one your first problems when trying to work out a standard across your team. If you have different directory set-ups, different operating environments — and you will — things get messy.

Almost immediately Lucee folks want to jump into the web administrator:

This is blocked by default in the official Lucee image; it’s a security precaution. In production Docker deployments you would never make modifications here. But for the sake of stumbling about, here’s how you would grant access to the admin.

In this Docker image (lucee/lucee51-nginx) the admin is blocked by a directive in the NGINX config. You can easily override the default config with a simple replacement config that doesn’t block access.

Save default.conf into your ./lucee-docker folder:

server {
  listen 80 default_server;
  server_name _;
  index  index.cfm index.html index.htm;
  root   /var/www;
  server_name_in_redirect off;

  location ~* \.(cfm|cfc|cfr)$ {
      proxy_redirect off;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-Host $host;
      proxy_set_header X-Forwarded-Server $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_connect_timeout 600;
      proxy_send_timeout 600;
      proxy_read_timeout 600;
      send_timeout 600;

And set up another VOLUME that maps that file over the top of the default configuration (again replacing for your own file paths):

docker run -p 8080:80 -v /Users/modius/code/lucee-docker:/var/www -v /Users/modius/code/lucee-docker:/etc/nginx/conf.d lucee/lucee51-nginx

Sort of awesome that it works but a limp, salad-fingers recipe for development.

The command line incantation caper gets old real quick; no one does Docker development like this. To get to grips with something more realistic install docker-compose and convert this docker cantrip into a working docker-compose.yml file.

More on that later.


Another thing folks want to jump straight into is logging onto the container and playing around with the contents. Typically getting a shell up on the container is the last resort for debugging but everyone wants to stumble around in there to “get a feel” for what’s going on inside the container.

Following on from the earlier examples…

$ docker run -d -p 8080:80 lucee/lucee51-nginx

If we add a -d flag to the incantation, Docker detaches the container, returns a container ID and runs it in the background. Using that ID we can attach a bash shell to the container and “login” to have a look around:

$ docker exec -ti bd77da82d6bb3b58206b4cb0c619ec083e1f241e3c72023cd0c9d504d1ffdf6d bash
root@bd77da82d6bb:/usr/local/tomcat# cd /var/www
root@bd77da82d6bb:/var/www# ls

(Type exit to “logoff”)

To get rid of the running container you can reference the ID and kill it.

$ docker kill bd77da82d6bb3b58206b4cb0c619ec083e1f241e3c72023cd0c9d504d1ffdf6d


This looks like it will be a great series of how-to’s for Docker

I am still in the investigation / exploration stage of using Docker for Development/Production and have found that Docker is both very complex and powerful but can also be approached in a simple step by step process to learn.

Setting up/running single containers is very straightforward, I am currently looking at docker-compose and docker network to set up a more complete docker application environment.

I have also found to be useful to allow running multiple sites on a single device.

Quick addition to your posts: Any time that an id is required in a docker command e.g. for a container or image, that only the first few characters of the id are required e.g. in your example you can use bd77 instead of bd77da82d6bb3b58206b4cb0c619ec083e1f241e3c72023cd0c9d504d1ffdf6d as long as there is only one container that starts with bd77



I’m jumping the gun a bit, since I think modius was planning a series of articles from the basics up to our standard workflow, but if you’re already using a proxy for your local dev then you might be interested in our docker-workbench tool :slight_smile:


As @justin points out, NGINX-proxy is an integral part of our Docker Workbench project. Justin has helped me put the finishing touches on a Workbench specific post.


Thank you for this great guide Geoff - it was perfect timing. Looking forward to the next one in the series.

That Salad Fingers video is…disturbing :slight_smile: