B u s L o g i c:- prolog, code, chatter.

Run a Prolog web server in Docker

It can be tricky to get a Prolog web application on to a cloud profider (like AWS) because most functions only support major languages like python, dotnetcore or nodejs. For example AWS lambda only has a few language options. So if you want to deploy a Prolog app then a full server environment needs to be created and the prolog run time installed.

That is unless you use Docker! Docker containers are supported quite well in most cloud providers now and it is faily easy to setup Prolog to run under Docker, both SWI-Prolog and Scryer Prolog have pre-built docker images that can be enhanced for your needs.

For this example, a very simple webserver, hosting a few files will be created in a Docker container. The webserver will look like this:

:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- use_module(library(http/http_files)).

server(Port) :-
    http_server(http_dispatch, [port(Port)]).

http:location(files, root(files), []).
:- http_handler(root(.), http_reply_from_files('files', []), [prefix]).

There is nothing fancy here, all the webserver does is allow files in the files directory to be served up, and will look for index.html by default.

The website, or files/index.html will contain the following:

<!DOCTYPE html>
        <title>Prolog is alive!</title>
        <p>Hello, I am using Prolog on Docker!</p>

Great! If server.pl is run locally and server(8080) is called, you will be able to see the message in the browser.

Now to dockerize the app. There are two more artefacts required

  1. A Dockerfile (be careful of spelling and case, they are both important!)
  2. A start.pl to run the server.

The start file does not need to be complicated, but there is one problem. Docker can try to shut down the container if the starting thread stops processing, and Prolog will generally load the web server and do nothing more from then on.

As long as Prolog never stops processing then this shutdown doesn't happen, so a periodic loop to stop the run predicate ending does the trick. I haven't found a better way of stopping this yet, but there is likely a Docker setting somewhere.

:- ensure_loaded(server).

run :-

:- run.

So start.pl will basically load server.pl, start the server and then loop forever at a 10 second interval.

The final thing to create is the DockerFile, I am using SWI-Prolog so my Dockerfile will extend the swipl docker image.

FROM swipl
ARG UID=1000
ARG GID=1000

# The user that will run the app 
ARG USER=docker
ARG PW=docker

# Add the user that will run the app
RUN useradd -m ${USER} --uid=${UID} && echo "${USER}:${PW}" | chpasswd

# Setup default user, when enter docker container

COPY ./prolog /app
CMD ["swipl", "/app/start.pl"]

I won't go into great detail here because most of this is standard Docker stuff. There are a couple of very important considerations though. First is the line


This sets the working directory to the root of the webserver and by doing this we have allowed the server to find the files directory, which it needs in order to server files!

CMD ["swipl", "/app/start.pl"]

This command will run the start.pl file that we created.

I also like to have a simple build file (dock_local.ps1) that will create the image deploy it locally and keep it running. The command are simple and this can be converted to a bash or bat file if required. The build file looks like this:

docker stop prolog_hello_dev
docker build -t prolog_hello .
docker run -d --rm -p 5001:8080 --name prolog_hello_dev prolog_hello

Now let's run the build:

Building the docker file locally

This build will create an a docker called prolog_hello_dev which is available on port 5001. You will notice that the server is running on port 8080, but the -p 5001:8080 flag when running docker maps this to port 5001 on the local machine.

Prolog running in Docker

Browsing to http://localhost:5001 brings up the message!

Prolog in the browser

So there you have it, a very simple Prolog web app running in a docker container.