Wagtail Tutorial Series:
To learn more about Wagtail CMS, please check Build Blog With Wagtail CMS (4.0.0)
- Create Wagtail Project
- Modern Frontend Techs for Wagtail
- Dockerizing Wagtail App
- Add Blog Models to Wagtail
- How to write Wagtail page template
- Create Stylish Wagtail Pages with Tailwind CSS
- How to use StreamField in Wagtail
- Wagtail Routable Page
- Add Pagination Component to Wagtail
- Customize Wagtail Page URL
- Add Full Text Search to Wagtail
- Add Markdown Support to Wagtail
- Add LaTeX Support & Code Highlight In Wagtail
- How to Build Form Page in Wagtail
- How to Create and Manage Menus in Wagtail
- Wagtail SEO Guide
- Online Demo http://wagtail-blog.accordbox.com/
- Source code: https://github.com/AccordBox/wagtail-tailwind-blog
Wagtail Tips:
- Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
- Wagtail Tip #2: How to Export & Restore Wagtail Site
Write style in Wagtail:
- How to use SCSS/SASS in your Django project (Python Way)
- How to use SCSS/SASS in your Django project (NPM Way)
Other Wagtail Topics:
Objectives
By the end of this chapter, you should be able to:
- Understand Docker Compose and the benefits.
- 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 20.10.11, build dea9396
$ docker-compose --version
docker-compose version 1.29.2, build 5becea4c
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
├── manage.py
├── wagtail_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
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"
services:
web:
build:
context: .
dockerfile: ./compose/local/web/Dockerfile
image: wagtail_blog_web
command: /start
volumes:
- .:/app
ports:
- 8000:8000
env_file:
- ./.env/.dev-sample
depends_on:
- db
db:
image: postgres:14.0-alpine
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_DB=wagtail_blog
- POSTGRES_USER=wagtail_blog
- POSTGRES_PASSWORD=wagtail_blog
volumes:
postgres_data:
Notes:
- Here we defined two services, one is
web
(django), the other one isdb
- 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
SQL_ENGINE=django.db.backends.postgresql
SQL_DATABASE=wagtail_blog
SQL_USER=wagtail_blog
SQL_PASSWORD=wagtail_blog
SQL_HOST=db
SQL_PORT=5432
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_app/settings.py to read env variables.
import os
DATABASES = {
"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"),
}
}
Dockerfile
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.10-slim-buster
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
# Install system packages required by Wagtail and Django.
# https://github.com/wagtail/wagtail/blob/v4.0.2/wagtail/project_template/Dockerfile
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
WORKDIR /app
ENTRYPOINT ["/entrypoint"]
Notes:
PYTHONDONTWRITEBYTECODE=1
tell Python to not write bytecode (.pyc) and__pycache__
directory on local env.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.- In the above
docker-compose.yml
, we config docker volume.:/app
, so here we setWORKDIR /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.
Entrypoint
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.
compose/local/web/entrypoint
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
postgres_ready() {
python << END
import sys
import psycopg2
try:
psycopg2.connect(
dbname="${SQL_DATABASE}",
user="${SQL_USER}",
password="${SQL_PASSWORD}",
host="${SQL_HOST}",
port="${SQL_PORT}",
)
except psycopg2.OperationalError:
sys.exit(-1)
sys.exit(0)
END
}
until postgres_ready; do
>&2 echo 'Waiting for PostgreSQL to become available...'
sleep 1
done
>&2 echo 'PostgreSQL is available'
exec "$@"
- We defined a
postgres_ready
function which is called in loop. The loop would only stop if thedb
service is able to connect. - The last
exec "$@"
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.
compose/local/web/start
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
python manage.py migrate
python manage.py runserver 0.0.0.0:8000
Start application
Let's update requirements.txt to include postgres dependency
psycopg2-binary==2.9.4 # new
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 | Starting development server at http://0.0.0.0:8000/
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)
- Once the containers are up, the entrypoint scripts will execute.
- 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 docker-entrypoint.sh postgres Up 5432/tcp
wagtail_project_web_1 /entrypoint /start Up 0.0.0.0:8000->8000/tcp
Wagtail Tutorial Series:
To learn more about Wagtail CMS, please check Build Blog With Wagtail CMS (4.0.0)
- Create Wagtail Project
- Modern Frontend Techs for Wagtail
- Dockerizing Wagtail App
- Add Blog Models to Wagtail
- How to write Wagtail page template
- Create Stylish Wagtail Pages with Tailwind CSS
- How to use StreamField in Wagtail
- Wagtail Routable Page
- Add Pagination Component to Wagtail
- Customize Wagtail Page URL
- Add Full Text Search to Wagtail
- Add Markdown Support to Wagtail
- Add LaTeX Support & Code Highlight In Wagtail
- How to Build Form Page in Wagtail
- How to Create and Manage Menus in Wagtail
- Wagtail SEO Guide
- Online Demo http://wagtail-blog.accordbox.com/
- Source code: https://github.com/AccordBox/wagtail-tailwind-blog
Wagtail Tips:
- Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
- Wagtail Tip #2: How to Export & Restore Wagtail Site
Write style in Wagtail:
- How to use SCSS/SASS in your Django project (Python Way)
- How to use SCSS/SASS in your Django project (NPM Way)
Other Wagtail Topics: