Deploying Wagtail on CentOS8 with MariaDB/Nginx/Gunicorn
Tags: linux, wagtail, nginx, django
Published: 2021-01-02 03:58:00

Wagtail is an actively developed, open source CMS built on Python and the Django web framework. In this tutorial, we will go over how to deploy Wagtail on a CentOS 8 server with MariaDB, Nginx, and Gunicorn.

### 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
# add the user to the wheel group for sudo access.
usermod -aG wheel <username>
```

Next, lets install Python 3.8 using our newly created sudo user account.
```
su <username>
sudo dnf module -y install python38
```

Now we need to install both Nginx and MariaDB.
```
sudo dnf install nginx mariadb-server git
```

### 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>
Set 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>
Remove test database and access to it? [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.

If you're following this guide closely you should be logged in as root right now.

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
```
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
```
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.