Deploying Wagtail on CentOS8 with MariaDB/Nginx/Gunicorn
March 7, 2021
Getting Started
Start with a fresh install of CentOS 8 and make sure your server is up to date.
$ dnf update -y
# reboot the server to apply the updates if needed.
Create a non-root sudo user, if you haven't already
$ useradd <username>
$ passwd <username> #enter new password
$ usermod -aG wheel <username> # add the user to the wheel group for sudo access.
Now we need to install both Nginx and MariaDB using our newly created user account.
$ su <username>
$ sudo dnf install nginx git gcc mariadb-server mariadb-common mariadb-connector-c-devel python3-devel
Setup the environment
At this point I like to create a Wagtail user account for managing the project code.
$ sudo groupadd --system wagtail
$ sudo useradd --system --gid wagtail --shell /bin/bash --home /opt/wagtail wagtail
$ sudo mkdir /opt/wagtail
$ sudo chown wagtail:wagtail /opt/wagtail
If you're hosting your project on GitHub or GitLab you can create an SSH key using the following commands.
$ sudo su wagtail
$ ssh-keygen -t rsa -b 2048 -C "<your email address>"
Generating public/private rsa key pair.
Enter file in which to save the key (/opt/wagtail/.ssh/id_rsa): <enter>
Created directory '/opt/wagtail/.ssh'.
Enter passphrase (empty for no passphrase): <enter password and/or press enter>
Enter same passphrase again: <enter password and/or press enter>
Your identification has been saved in /opt/wagtail/.ssh/id_rsa.
Your public key has been saved in /opt/wagtail/.ssh/id_rsa.pub.
You can get the key by running the following command.
$ cat /opt/wagtail/.ssh/id_rsa.pub
Setting up the database
Before we do anything else, lets start the MariaDB server and enable MariaDB on boot. Run these commands as root.
$ systemctl start mariadb
$ systemctl enable mariadb
Now, to give ourselves a nice, secure default setup run the following command.
$ mysql_secure_installation
Enter current password for root (enter for none): <enter>
Switch to unix_socket authentication [Y/n] n
Change the root password? [Y/n] n
Remove anonymous users? [Y/n] <enter>
Disallow root login remotely? [Y/n] <enter>
Remove test database and access to it? [Y/n] <enter>
Reload privilege tables now? [Y/n] <enter>
Log in to MariaDB and create the database and user.
$ mysql -u root -p
Enter password: <enter>
> CREATE DATABASE wagtail CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
> CREATE USER 'wagtaildb'@'localhost' IDENTIFIED BY '<db user password';
> GRANT ALL PRIVILEGES ON wagtail.* to wagtaildb@localhost;
> FLUSH PRIVILEGES;
> exit
Deploying your Project
The next step is to pull the code down from your repository. This guide assumes GitHub or GitLab is being used but feel free to use whatever you prefer. For this example I am using an empty project hosted on GitLab named Hello.
Switch to the wagtail user and create the Python virtual environment in the wagtail users home directory.
$ su - wagtail
$ python3.8 -m venv .venv
$ source .venv/bin/activate
Upgrade setuptools and pip
$ pip install --upgrade setuptools pip
Create a directory to store the logs.
$ mkdir logs
Clone your git repositiory and cd into the project directory.
$ git clone https://gitlab.com/Skriptmonkey/hello.git
$ cd hello
I have a 'my.cnf' file in my settings folder for the database credentials. This is what the file looks like.
[client]
database = wagtail
user = wagtaildb
password = <db user password>
default-character-set = utf8
The database section in my base.py file looks like this.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'OPTIONS': {
'read_default_file': '/opt/wagtail/hello/hello/settings/my.cnf',
},
}
}
Install the dependancies.
$ pip install -r requirements.txt
Migrate your database.
$ python manage.py migrate
And finally, collect your static assets. Your STATIC_ROOT variable in the base.py settings file should be set to someplace that Nginx can easily access. I go with '/var/www/static'. Run the following commands as root.
$ cd /opt/wagtail
$ source .venv/bin/activate
$ cd hello
$ python manage.py collectstatic --noinput
Let's also create a wagtail super user so that we can access the admin pages.
$ python manage.py createsuperuser
Username (leave blank to use 'wagtail'): <username>
Email address: <email address>
Password: <password>
Password (again): <re-enter password>
Superuser created successfully.
Change back to the wagtail user and create a new settings file in the settings folder called local.py.
$ su - wagtail
$ vim /opt/wagtail/hello/hello/settings/local.py
In this settings file we want to import from .base, add a SECRET_KEY, and add in our ALLOWED_HOSTS. How you generate a secret key is up to you. I like to create a "test" project on my local machine and take the SECRET_KEY from that.
from .base import *
SECRET_KEY = '<your secret key>'
# SECURITY WARNING: define the correct hosts in production!
ALLOWED_HOSTS = ['exampleurl.com', 'www.exampleurl.com']
Configuring Gunicorn
If Gunicorn hasn't been added to the requirements.txt file in your project it should be. But if it isn't you can install it through pip as the wagtail user.
$ pip install gunicorn
We need to create a start file for gunicorn. This file will be called whenever you need to start the application server. Feel free to use your favorite text editor.
$ vim /opt/wagtail/gunicorn_start
Paste this into your new file and update the variables to reflect your project. Pay special attention to the DJANGO_SETTINGS_MODULE, Wagtail uses a different convention than Django by default.
#!/bin/bash
NAME="hello"
DJANGODIR=/opt/wagtail/hello
USER=wagtail
GROUP=wagtail
WORKERS=3
BIND=unix:/opt/wagtail/run/gunicorn.sock
DJANGO_SETTINGS_MODULE=hello.settings.production
DJANGO_WSGI_MODULE=hello.wsgi
LOGLEVEL=error
cd $DJANGODIR
source /opt/wagtail/.venv/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH
exec /opt/wagtail/.venv/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
--workers $WORKERS \
--user=$USER \
--group=$GROUP \
--bind=$BIND \
--log-level=$LOGLEVEL \
--log-file=-
Make the file executable.
$ chmod +x ~/gunicorn_start
Make the run directory to store the unix socket file.
$ mkdir ~/run
Set the permissions on the new directory.
$ chmod 755 ~/run
Now exit out of the wagtail user so we can setup the systemd gunicorn service.
First create the systemd service file using your favorite editor.
$ sudo vim /etc/systemd/system/gunicorn.service
Add the following to the file:
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=wagtail
Group=wagtail
WorkingDirectory=/opt/wagtail
ExecStart=/opt/wagtail/gunicorn_start
[Install]
WantedBy=multi-user.target
Start the gunicorn service and enable the service to start on boot.
$ sudo systemctl start gunicorn
$ sudo systemctl enable gunicorn
Configuring Nginx
Lets get SELinux squared away before we start on configuring Nginx. We need to install the SELinux utilities.
$ sudo dnf install policycoreutils-python-utils -y
Now we add httpd_t
to the permissive domains in SELinux.
$ sudo semanage permissive -a httpd_t
While we're allowing things to happen on our server lets also allow both http and https through the server firewall.
$ sudo firewall-cmd --zone=public --permanent --add-service=http
$ sudo firewall-cmd --zone=public --permanent --add-service=https
Change into the Nginx conf.d directory.
$ cd /etc/nginx/conf.d/
Once again, using your favorite editor create a config file for Nginx.
$ sudo vim wagtail.conf
Add this into the file:
upstream app_server {
server unix:/opt/wagtail/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
#listen [::]:80; # <- Uncomment this if you also have AAAA DNS record for IPV6.
server_name IP_ADDRESS_OR_DOMAIN_NAME; # <- insert here the ip address/domain name
keepalive_timeout 5;
client_max_body_size 4G;
access_log /opt/wagtail/logs/nginx-access.log;
error_log /opt/wagtail/logs/nginx-error.log;
location /static/ {
autoindex on;
alias /opt/wagtail/hello/static/;
}
location /media/ {
alias /opt/wagtail/hello/media/;
}
location / {
try_files $uri @proxy_to_app;
}
location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
}
Test your Nginx configuration.
$ sudo nginx -t
Start the service and enable it on boot.
$ sudo systemctl start nginx
$ sudo systemctl enable nginx
Reboot the server.
$ sudo reboot
That's it!
You should now have a functional Wagtail site deployed.
Next I'd recommend configuring your webserver with an SSL certificate. Let's Encrypt is a good, free option.