Dockerizing Wagtail App

Michael Yin

Last updated on March 16 2021

Table of Contents

Wagtail Tutorial Series:

  1. Create Wagtail Project
  2. Dockerizing Wagtail App
  3. Add Blog Models to Wagtail
  4. How to write Wagtail page template
  5. Add Bootstrap Theme to Wagtail
  6. How to use StreamField in Wagtail
  7. Wagtail Routable Page
  8. Add pagination component to Wagtail
  9. Customize Wagtail Page URL

The source code is available on


By the end of this chapter, you should be able to:

  1. Understand Docker Compose and the benefits.
  2. Use Docker Compose to create and manage Wagtail, Postgres services, and do development.

Install Docker Compose

Docker Compose is a tool for defining and running multi-container Docker applications. It uses YAML files to configure the application's services and performs the creation and start-up process of all the containers with a single command.

The docker-compose CLI utility allows users to run commands on multiple containers at once, for example, building images, scaling containers, running containers that were stopped, and more.

First, please download and install Docker Compose if you haven't already done so.

$ docker --version
Docker version 19.03.8, build afacb8b

$ docker-compose --version
docker-compose version 1.25.5, build 8a1c60f6

Config File Structure

Let's start with our config file structure, this can help you better understand the whole workflow:

├── compose
│   └── local
│       └── web
│           ├── Dockerfile
│           ├── entrypoint
│           └── start
├── docker-compose.yml
├── wagtail_bootstrap_blog
└── requirements.txt

You will see we have config files docker-compose.yml and some files in compose directory, you do not need to create them for now. I will talk about them with more details in the coming sections.

Note: The config file structure come from cookiecutter-django, which is a great project for people who want to learn Django.

Compose file

Note: we can ignore the npm run start here and I will talk about it later.

Compose file is a YAML file to configure your application's services.

When you run docker-compose command, if you do not specify Compose file, the default file is docker-compose.yml, that is why we create docker-compose.yml at root directory of Django project, because it can save us time when typing command during development.

Let's add docker-compose.yml

version: "3.7"

      context: .
      dockerfile: ./compose/local/web/Dockerfile
    image: wagtail_bootstrap_blog_web
    command: /start
      - .:/app
      - 8000:8000
      - ./.env/.dev-sample
      - db

    image: postgres:12.0-alpine
      - postgres_data:/var/lib/postgresql/data/
      - POSTGRES_DB=wagtail_bootstrap_blog
      - POSTGRES_USER=wagtail_bootstrap_blog
      - POSTGRES_PASSWORD=wagtail_bootstrap_blog



  1. Here we defined two services, one is web (django devserver), the other one is db
  2. We create a named docker volume postgres_data, and use it to store the db data, so even db container is deleted, the db data can still exist.

Environment Variables

We can put env variables in a specific file for easy management.

Let's create .env directory, and add .dev-sample file


Please make sure .env is not excluded in the .gitignore, so it can be added to Git repo

Please note that the db login credential should match environment variables of db service in docker-compose.yml

Next, let's update DATABASES of wagtail_bootstrap_blog/settings/ to read env variables.

import os

    "default": {
        "ENGINE": os.environ.get("SQL_ENGINE", "django.db.backends.sqlite3"),
        "NAME": os.environ.get("SQL_DATABASE", os.path.join(BASE_DIR, "db.sqlite3")),
        "USER": os.environ.get("SQL_USER", "user"),
        "PASSWORD": os.environ.get("SQL_PASSWORD", "password"),
        "HOST": os.environ.get("SQL_HOST", "localhost"),
        "PORT": os.environ.get("SQL_PORT", "5432"),


In Docker Compose, we can let it create docker container from existing docker image or custom docker image.

To build custom docker image, we need to provide Dockerfile

Please create directory and file like this

├── compose
│   └── local
│       └── web
│           ├── Dockerfile

Edit compose/local/web/Dockerfile

FROM python:3.8-slim-buster


# Install system packages required by Wagtail and Django.
RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \
    build-essential \
    libpq-dev \
    libmariadbclient-dev \
    libjpeg62-turbo-dev \
    zlib1g-dev \
    libwebp-dev \
 && rm -rf /var/lib/apt/lists/*

# Requirements are installed here to ensure they will be cached.
COPY ./requirements.txt /requirements.txt
RUN pip install -r /requirements.txt

COPY ./compose/local/web/entrypoint /entrypoint
RUN sed -i 's/\r$//g' /entrypoint
RUN chmod +x /entrypoint

COPY ./compose/local/web/start /start
RUN sed -i 's/\r$//g' /start
RUN chmod +x /start


ENTRYPOINT ["/entrypoint"]


  1. PYTHONDONTWRITEBYTECODE=1 tell Python to not write bytecode (.pyc) and __pycache__ directory on local env.
  2. RUN sed -i 's/\r$//g' /entrypoint is used to process the line endings of the shell scripts, which converts Windows line endings to UNIX line endings.
  3. In the above docker-compose.yml, we config docker volume .:/app, so here we set WORKDIR /app. If we edit code on host machine, then the code change can also been seen in /app of the docker container.

Next, let's check the entrypoint and start script.


In docker-compose.yml, we can use depends_on to let web service run after db service. However, it can not guarantee web service start after db service is trully ready. (Github Issue)

So we can add script in entrypoint to solve this problem.



set -o errexit
set -o pipefail
set -o nounset

postgres_ready() {
python << END
import sys

import psycopg2

except psycopg2.OperationalError:

until postgres_ready; do
  >&2 echo 'Waiting for PostgreSQL to become available...'
  sleep 1
>&2 echo 'PostgreSQL is available'

exec "[email protected]"
  1. We defined a postgres_ready function which is called in loop. The loop would only stop if the db service is able to connect.
  2. The last exec "[email protected]" is used to make the entrypoint a pass through to ensure that Docker container runs the command the user passes in (command: /start, in our case). For more, check this Stack Overflow answer.

Start script

Now, let's add start script.



set -o errexit
set -o pipefail
set -o nounset

python migrate
python runserver

Start application

Let's update requirements.txt to include postgres dependency


Building the docker images:

$ docker-compose build

Once the images are build, start the application in detached mode:

$ docker-compose up -d

# check realtime logs
$ docker-compose logs -f

web_1  | Django version 3.1.4, using settings ''
web_1  | Starting development server at
web_1  | Quit the server with CONTROL-C.

This will start containers based on the order defined in the depends_on option. (db first, web second)

  1. Once the containers are up, the entrypoint scripts will execute.
  2. Once Postgres is up, the respective start scripts will execute. The Django migrations will be applied and the development server will run. The Django app should then be available.

You can check the docker compose application with this command.

$ docker-compose ps
        Name                       Command              State           Ports
wagtail_project_db_1 postgres   Up      5432/tcp
wagtail_project_web_1   /entrypoint /start              Up>8000/tcp

Wagtail Tutorial Series:

  1. Create Wagtail Project
  2. Dockerizing Wagtail App
  3. Add Blog Models to Wagtail
  4. How to write Wagtail page template
  5. Add Bootstrap Theme to Wagtail
  6. How to use StreamField in Wagtail
  7. Wagtail Routable Page
  8. Add pagination component to Wagtail
  9. Customize Wagtail Page URL

The source code is available on

Michael Yin

Michael is a Full Stack Developer from China who loves writing code, tutorials about Django, Wagtail CMS and React.

He has published some ebooks on leanpub and tech course on

He is also the founder of the AccordBox which provides the web development services.

Table of Contents


Build Jamstack web app with Next.js and Wagtail CMS.

Read More


Get notified about new great Web Development Tutorial