Run Wagtail CRX with Docker

Wagtail CRX runs well in Docker. When working with Docker, there are two different approaches. This guide will also work with any Wagtail site, or most types of Django sites.

The first step is to install Docker, on both your development environment (i.e. your computer) and on your server (hosting) environment.

For the sake of this guide, we will assume your Django project is named myproject.

Step 1: Choose How to Use Docker

The Versioned Image Approach

The most common way of working with Docker is to create a static Docker image, containing all of your code, which is effectively versioned in tandem with your git repository. Each time you change your project code, you also create a new version of the image. Because this image is effectively static and mirrors version control, there is no way to store dynamic files such as a database or media files. In this setup, you would want to use 3rd party services such as AWS S3 for files, or AWS RDS for database. Nearly all cloud platforms provide similar services.

Next, create a file in your main project folder named Dockerfile (no file extension). Copy the contents below into the file:

FROM python:latest

ENV PYTHONUNBUFFERED 1

# Set the Django settings to use.
ENV DJANGO_ENV "dev"
ENV DJANGO_SETTINGS_MODULE "myproject.settings.dev"

# Install a WSGI server into the container image.
RUN pip install waitress

# Code will end up living in /app/
WORKDIR /app/

# Copy and install the project requirements.
COPY ./requirements.txt /app/requirements.txt
RUN pip install -r /app/requirements.txt

# Copy the entire project code.
COPY . /app/

# Prepare the app.
RUN python manage.py migrate
RUN python manage.py collectstatic --noinput

# Create a "coderedcms" user account to run the app.
RUN useradd coderedcms
RUN chown -R coderedcms /app/
USER coderedcms

# Finally, run the app on port 8000.
EXPOSE 8000
CMD exec waitress-serve --listen "*:8000" "myproject.wsgi:application"

The “Image as Environment” Approach

The second approach is to use the container as the runtime environment, but store your code and files outside the container. This way you only need to update the container when you want to change your python version, or apply other security updates. For those who like to use the filesystem, such as writing logs, media files, etc. “normally”, this is the recommended way to use Docker, as it will fit within your existing workflow.

First, create a script which will be used as the entry point, meaning it runs every time the container starts. The entry point will be used to set up the app each time. Copy the contents below into a filed named docker-entrypoint.sh.

pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --noinput

Next, create a file in your main project folder named Dockerfile (no file extension). Copy the contents below into the file:

FROM python:latest

ENV PYTHONUNBUFFERED 1

# Set the Django settings to use.
ENV DJANGO_ENV "dev"
ENV DJANGO_SETTINGS_MODULE "myproject.settings.dev"

# Install a WSGI server into the container image.
RUN pip install waitress

# Code will end up living in /app/
WORKDIR /app/

# Create a "coderedcms" user account to run the appp.
RUN useradd coderedcms
RUN chown -R coderedcms /app/
USER coderedcms

# Copy our entrypoint script.
COPY ./docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

# Finally, run the app on port 8000.
EXPOSE 8000
ENTRYPOINT ["docker-entrypoint.sh"]
CMD exec waitress serve --listen "*:8000" "myproject.wsgi:application"

Step 2: Build Your Image

Next, with Docker running on your machine, create an image by running the following from your command line, replacing /path/to/Dockerfile and /path/to/project/ with the correct paths on your machine.

$ docker build --pull -t myproject:v1 -f /path/to/Dockerfile /path/to/project/

This will likely take a while, as Docker is going to download the FROM image (Python in this case) and then run all of those commands in your Dockerfile. Once complete, this will have created an image named myproject tagged with v1. If you are using the “Versioned Image” approach, you would likely want to change this tag every time you build the image. Docker image tags work essentially like version control, as such many people choose to use their current git commit ID as the tag. If you are using the “Image as Environment” approach, then this tag would likely be your Python version, e.g. py3.8.1

Step 3: Run a Container Using the Image

Now, create a container using the image. If using the “Versioned Image” approach:

$ docker run --publish 8000:8000 --detach --name myproject-run myproject:v1

If using the “Image as Environment” approach, you also need to map a local directory on your machine to a directory inside the container. This ensures that the files that get created or modified are shared between your machine and the container, and they will remain on your machine after the container is deleted. The command below runs the container, but before doing so mounts the local directory ./ into the container’s /app/ directory:

$ docker run --publish 8000:8000 --detach --name myproject-run --mount type=bind,source=./,target=/app myproject:v1

Either approach will run an instance of your image myproject:v1 named myproject-run, and map port 8000 on your machine to port 8000 of the container. Now going to http://localhost:8000 should serve up your app from the container.

Read the official Docker guide and documentation at: https://docs.docker.com/get-started/.