Develop, Dockerize & Deploy Contao 4: Part 1
A series about how to manage source code, dockerizing and automatically deploying your Contao 4 application.

This article series is meant to illustrate how to bring your Contao 4 application from source code state to life in a running application on Docker-based systems.
We will discuss the how the source code of the Contao 4 app can be stored and managed using Git & GitLab, how to use the GitLab CI pipeline to build and store a proper docker image and finally how to deploy that application to a server.
All software used in this article can be obtained free for testing purposes (a great time to be a software developer, right?)
Introduction: Docker and basic principles
The basic idea behind docker is to provide a standardized, portable way of distributing and deploying application. You can think of it as a shipping container in real life which also adheres to a standardized specification so that it can be loaded by any container ship, no matter what is inside that container — be it stacks of bananas or a living room. This is where the name docker originates from.
A docker application has all its dependencies and even an abstraction of its own operating system inside its container, so it can be moved from server to server and even from platform to platform as long as there is a docker daemon running that provide the standardized interfaces for the container. You could think of it as having an RV or a camper van. Park it in a designated area, connect power and maybe water and you’re good to go. Tired of the old container? Want to move it somewhere else? No problem, just disconnect and lift it away! There is a new version available? Just destroy the old container and create a new one!
Docker today runs on almost any operating system on any server — from IBM-Z mainframes, Windows and Linux servers to your Raspberry Pi running Raspbian. This means as a developer you do not need to worry about specific system architecture your application will run in. For example: You build your app around the popular Ubuntu operating system in version 18.04. You install all the libraries you need and complete the app. Then you only have to package it in a Docker image which is then used to create a container on a server somewhere.
Strategic thoughts before we start
Docker containers are supposed to be stateless. This means that there should be no user or application data stored inside the container — think of it as everything in the container is disposable and can be destroyed any minute. How do you store data then you might ask? That is where Docker volumes come in.
A Docker volume is an abstraction of storage space that can be attached to any container — in the simplest case it is just a directory on your harddrive, in a sophisticated setup it might be on a storage cluster somewhere in another datacenter. Yes, you read that right — a volume can be completely decoupled and independent of the host server the container is running on. It can also be used by multiple containers simultaneusly. That is how you store your data and are able to have multiple containers access the same files.
Volumes are mounted to directories in containers, so in our case for Contao we would need two volumes: One for the files
directory and one for the var/logs
directory as those are the only directories we want to keep when the container is destroyed (called the “shared state”)— everything else comes with the container and will be destroyed with the container.
For our Contao 4 installation our final Docker setup will have the following components:
- a Contao 4 container
- a MySQL container for our database
- 3 volumes, one for /files, one for /var/log, and one for /var/lib/mysql (where the MySQL container stores its database files)
Part 1: Setting up your development environment and Git
This section will cover how to install docker on your development machine, create a development container and launch an empty Contao 4 installation. I also assume that you have a text editor for developers installed, if not just install Visual Studio Code.
You can skip the first sections if you are already familiar with Docker, docker-compose and developing using docker containers.
Installing Docker
For Windows: We will be using Docker for Windows and the Windows Subsystem for Linux 2 (WSL2). WSL2 will run a lightweight Virtual Machine running Ubuntu with a custom kernel featuring Windows integration. Docker for Windows will the provide the additional tools to run docker containers from our Windows system.
I recommend you start with installing the Windows Terminal from the Microsoft Store to get a better Command Line experience.
To install WSL2 you can follow the official guide by Microsoft and install the Ubuntu Distribution from the Microsoft Store afterwards. After you followed the instructions, install the WSL2 Linux kernel update package from the Microsoft download site.
After you followed the guide and installed the things mentioned before, you should now be able to setup the Ubuntu Linux distribution when you click on the Ubuntu App in your Windows start menu. Choose a username and a fast to type password. When done and you are greeted by the command prompt, close the window.
You should now be able to open “Windows Terminal” from the start menu and type the command wsl
into the Powershell command line. You should also find “Ubuntu” in the dropdown menu opened by the down-pointing bracket.

If that works and you see a message welcoming you to Ubuntu 20.04, everything works as intended and we can now install Docker for Windows. To use Docker with WSL2 we need to install the Edge Version. Run the “Docker Desktop Installer” and when asked, enable the “WSL2 Backend”. When it has finished it will ask you to sign out and in again. After you have done that, you successfully installed Docker for Windows and are ready to go!
For Linux (Ubuntu/Debian): To get the most recent version you need to add the official Docker PPA to your system. After that, installation is straightforward and just a simple APT-command. You can find the detailed Guide on the official docker site.
For other Linux Distros, you can check the official guide listed here.
For macOS: Installing Docker for Mac is as easy as downloading it from Docker Hub and dragging it to your applications folder like any other app. I encourage you to read the Getting Started Guide to learn about the things specific to Docker for Mac.
Setup Git
Next, install Git for your platform. On Windows install Git for Windows, on macOS install XCode from the AppStore, it will install Git and other tools as part of its package. On Linux just install “git” using the package manager of your distribution.
Create the basic Contao 4 container image
Go to a folder of your choosing and create a new folder, which we will name contao-on-docker
for the purposes of this article. I have a source code folder in my home/user directory called source
and a folder inside named repos
, so I can reach it via command line by typing cd $HOME/source/repos
on Windows, Linux and macOS. Therefore the complete path for this new folder will be $HOME/source/repos/contao-on-docker
.
If you do not want to follow along and copy every single file, you can find the complete repository with the basic setup on my GitLab Account.
Open up the folder in the text editor of your choice, I use Visual Studio Code in this article. Create a new file called .gitignore
with the following contents:
This will prevent files and directories we do not want in our Git-Repository to be ignored by Git. The /vendor
directory of Contao is such an example as it can be restored anytime using the composer.lock
file and a composer install
command (which will be part of our Docker image build process).
We need some configuration files for the PHP installation as well as for the Caddy webserver which will provide SSL encryption using Let’s Encrypt and pass PHP requests to PHP-FPM.
Next up it is time to create the Dockerfile which instructs the Docker daemon how to build a Docker image for our Contao Application. We will base the image on the lightweight Alpine Linux. The Dockerfile will install PHP inside the container image and copy over all of our required files and configurations. Finally it will publish the volume mount points and ports used by the webserver.
We also need to create the php-config.ini file. I did not change anything other than the timezone:
Furthermore lets add a configuration file to enable XDebug later if we want to:
For our basic PHP container only two things are missing now: A webserver that will handle file requests and pass PHP script requests to PHP-FPM and a script to start all of it. I use Caddy as a webserver (and you should too) because it is modern, fast and easily configurable — plus it supports automatic HTTPS using Let’s Encrypt. How easy? Here is the config file:
The last thing is the boostrap script to start both of our services in the container, as well as ensure that the proper filesystem permissions are set:
To test this out, we need the rest of the files and directories. So create the app, config, contao, src and web directories next to the Dockerfile. As mentioned previously, you can use my repository as a template ready to go.

We will be using the Contao “Managed Edition” in the current LTS Version (4.9 at the time of this writing). The setup is almost identical with the Developer Guide in the official documentation, so if you have Composer installed, you can create a new contao project and just take the composer.json and composer.lock out of that directory and put it into the directory with the Dockerfile.
The directory should now look like the screenshot on the left.
It is time now to give it a first test and build our very basic and very own Contao 4 image! To do that, use the command line to navigate to the current directory with the Dockerfile. Then run docker build -t contao-test .
.
The Docker daemon should now begin to build the image — installing PHP, Composer, copying our files and running composer install
.
When the build process has finished, we are ready for a test drive! Make sure that there are no other webservers running on your system and then type docker run --name test -p 80:80 -p 443:443 -v 'files:/srv/files' -v 'logs:/srv/var/logs' contao-test
which will tell the Docker Daemon to spin up a new container with the appropriate name “test”. After a minute or so you should be able to go to https://localhost in your Webbrowser and be greeted by a privacy warning and — after you clicked “continue to website” you should be greeted by a familiar Contao 4 error message! Hooray! Also try if you can reach the Install-Tool under “https://localhost/contao/install”.

Congratulations! You have created your very own Contao 4 Docker Container! In the next article of this series I will show you how to setup the development setup using our new Container and a MySQL Container using docker-compose as well as how to manage the source code using Git.