Wagtail on Google App Engine demonstration
This is the simplest possible Wagtail site on Google Cloud, using App Engine Flexible and Cloud SQL.
Assumptions
- A project has been created in the Google Cloud Platform Console
- Billing has been enabled for your project
- The Cloud SQL API has been enabled
These steps are detailed in the Google Cloud documentation.
Install this app in Cloud Shell
git clone https://github.com/torchbox/wagtail-appengine-demo
cd wagtail-appengine-demo
virtualenv env
source env/bin/activate
sudo apt-get install libmysqlclient-dev
pip install -r requirements.txt
Configure Cloud SQL
Create a new Cloud SQL database
Create a new Cloud SQL instance, using MySQL:
gcloud sql instances create wagae \
--assign-ip \
--database-version=MYSQL_5_7 \
--region=europe-west2 \
--gce-zone=europe-west2-c \
--storage-size=50 \
--storage-type=HDD \
--tier=db-f1-micro
Adjust region, zone storage and machine size (tier) options as necessary. While PostgreSQL is a more common option for Django and Wagtail developers, it is currently only available in beta on Google Cloud SQL.
Create a new user and database, substituting a secure password for
[DB_PASSWORD]
:
gcloud sql users create wagtail % --instance=wagae --password=[DB_PASSWORD]
gcloud sql databases create wagtail --instance=wagae
Fetch the instance connection name for the database you created:
gcloud sql instances describe wagae --format='value(connection_name)'
your-project-id:europe-west2:wagae
We will refer to this as [CONNECTION_NAME]
later.
Create Google Storage buckets
GCS will be used to store static assets and uploaded media. Create two new buckets, using a common prefix. Note that bucket names must be globally unique - you may want to use your project ID as the prefix.
gsutil mb gs://my-wagtail-site-static
gsutil mb gs://my-wagtail-site-media
Configure CORS headers on the static bucket so fonts can be loaded from the site's domain, and make the contents of both buckets readable by default:
gsutil cors set cors.json gs://my-wagtail-site-static
gsutil defacl set public-read gs://my-wagtail-site-static
gsutil defacl set public-read gs://my-wagtail-site-media
Configure local settings
Create a file called wagae/settings/local.py
, and add your site-specific
settings:
DB_CONNECTION_NAME = '[CONNECTION_NAME]'
DB_PASSWORD = '[DB_PASSWORD]'
GS_BUCKET_PREFIX = '[BUCKET_PREFIX]'
GS_PROJECT_ID = '[GCP_PROJECT_ID]'
SECRET_KEY = '[SECRET_KEY]'
[GCP_PROJECT_ID]
is the name of your GCP project. [BUCKET_PREFIX]
is the
common prefix of the two GCS buckets you created, e.g. my-wagtail-site
.
[SECRET_KEY]
can be generated by running python generate_key.py
.
Install the database tables
Run Cloud SQL Proxy in a new shell (press '+' in the header of your Cloud Shell):
cloud_sql_proxy -instances="[CONNECTION_NAME]=tcp:3306"
Replace [CONNECTION_NAME]
with the connection name.
Create Wagtail's database tables and an initial user:
./manage.py migrate
./manage.py createsuperuser
Upload static files
Collect the static files locally and upload them:
./manage.py collectstatic --noinput
gsutil -m rsync -R static/ gs://my-wagtail-site-static/
Check everything works
./manage.py runserver 0.0.0.0:8080
Click on the 'Web preview' button in the header of your Cloud Shell. You should be able to log in to the Wagtail admin interface, using the user details you have just created.
Deploy to App Engine
Edit app.yaml
, setting [CONNECTION_NAME]
to the Cloud SQL database connection
name.
Then deploy the application:
gcloud app deploy
Hardening
- Once you know the production domain for your site, specify this in
ALLOWED_HOSTS
andBASE_URL
. - Update the origin in cors.json and rerun
gsutil cors set cors.json
- Increase
SECURE_HSTS_SECONDS
(e.g. to 2592000, which is 30 days) after testing - Change the
^admin/
entrypoint to a less guessable alternative
High security options
Separate instances
Run two (clustered) instances of Wagtail: one for editors, with firewalled access to the domain; one from a read-only replica of the database, with the admin UI disabled (remove url(r'^admin/', include(wagtailadmin_urls)),
from urls.py
).
Static site generation
Use wagtail-bakery to export the pages and assets at page publish time. Upload incremental changes to a CDN, e.g. using Firebase hosting. This approach provides the highest levels of performance and security, but more 'dynamic' features (e.g. search, form submissions, server-side A/B testing and personalisation) may become harder to implement. See wagtail-netlify for a similar process using an alternative static hosting provider.
Notes
App Engine Standard
While it is possible to run Wagtail on App Engine Standard, we don't recommend this. App Engine Standard currently only supports Python 2.7, which will not be supported in the imminent releases of Django 2.0 and Wagtail 2.0.
Wagtail sends email notifications to authors and editors, e.g. to prompt reviews when pages have been submitted for moderation. The Google App Engine documentation suggests three third party providers of secure SMTP services:
- https://cloud.google.com/appengine/docs/flexible/python/sending-emails-with-mailgun
- https://cloud.google.com/appengine/docs/flexible/python/sending-emails-with-mailjet
- https://cloud.google.com/appengine/docs/flexible/python/sending-emails-with-sendgrid
Search
Wagtail supports three backends for full-text search, each of which can power both the admin UI and public-facing search. These are Elasticsearch, PostgreSQL FTS and a simple database search. For simplicity's sake, this demo uses the latter. For more performant and feature-rich search, we currently recommend the Elasticsearch backend. Elasticsearch is available on Google Cloud either as a Cloud Launcher App or through Elastic.co's service.