Django deployment - PaaS
Preface
Recently i had to decide which hosting solution i want to use for my private blog. The blog was developed with the help of Dev-Case and is based on Django.
Since i have read a lot of good things about PaaS in connection with Django lately, the decision was made quickly.
The providers to choose from were:
- DigitalOcean
- Railway
- Render
In the end, i decided to go with DigitalOcean because i have already had good experiences here (VPS, Space). Another plus for DO was that i didn't need another provider for the S3-Storage.
Preparation
To be able to host my Django project with the App-Platform, i have made the following preparations:
Environment variables
For this i use django-environ. Here are a few basic settings:
import os
from pathlib import Path
from django.core.management.utils import get_random_secret_key
import environ
BASE_DIR = Path(__file__).resolve().parent.parent
env = environ.Env()
env_file = os.path.join(BASE_DIR, ".env")
if os.path.isfile(env_file):
env.read_env(env_file)
SECRET_KEY = env.str("SECRET_KEY", default=get_random_secret_key())
DEBUG = env.bool("DEBUG", default=False)
ALLOWED_HOSTS = env.list(
"ALLOWED_HOSTS",
default=[
"127.0.0.1",
"0.0.0.0",
],
)
Database
Here i decided to use Postgres.
Thanks to django-environ i can use a DATABASE_URL
here.
A possibility to use SQLite should still exist but is a matter of personal taste.
DATABASE_URL = env.str("DATABASE_URL", default=False)
if DATABASE_URL:
DATABASES = {"default": env.db()}
else:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
S3 Storage
To be able to use the S3 compatible storage later on (DO Space):
USE_S3_STORAGE = env.bool("USE_S3_STORAGE", default=False)
AWS_ACCESS_KEY_ID = env.str("AWS_ACCESS_KEY_ID", default="")
AWS_SECRET_ACCESS_KEY = env.str("AWS_SECRET_ACCESS_KEY", default="")
AWS_STORAGE_BUCKET_NAME = env.str("AWS_STORAGE_BUCKET_NAME", default="")
AWS_S3_REGION_NAME = env.str("AWS_S3_REGION_NAME", default="")
AWS_S3_ENDPOINT_URL = env.str("AWS_S3_ENDPOINT_URL", default="")
AWS_S3_CUSTOM_DOMAIN = env.str("AWS_S3_CUSTOM_DOMAIN", default="")
AWS_LOCATION = env.str("AWS_LOCATION", default="")
AWS_IS_GZIPPED = env.bool("AWS_IS_GZIPPED", default=False)
AWS_S3_FILE_OVERWRITE = env.bool("AWS_S3_FILE_OVERWRITE", default=True)
AWS_DEFAULT_ACL = env.str("AWS_DEFAULT_ACL", default="public-read")
STATICFILES_DIRS = (str(BASE_DIR.joinpath("static")),)
STATIC_ROOT = str(BASE_DIR.joinpath("staticfiles"))
MEDIA_ROOT = str(BASE_DIR.joinpath("media"))
if USE_S3_STORAGE:
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
STATICFILES_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
STATIC_URL = f"https://{AWS_S3_ENDPOINT_URL}/{STATIC_ROOT}/"
MEDIA_URL = f"https://{AWS_S3_ENDPOINT_URL}/{MEDIA_ROOT}/"
else:
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage"
STATIC_URL = "static/"
MEDIA_URL = "/media/"
SSL
A valid SSL certificate is supplied automatically:
SECURE_SSL_REDIRECT = env.bool("SECURE_SSL_REDIRECT", default=False)
SECURE_HSTS_SECONDS = env.int("SECURE_HSTS_SECONDS", default=0)
SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool(
"SECURE_HSTS_INCLUDE_SUBDOMAINS", default=False
)
SECURE_HSTS_PRELOAD = env.bool("SECURE_HSTS_PRELOAD", default=False)
SESSION_COOKIE_SECURE = env.bool("SESSION_COOKIE_SECURE", default=False)
CSRF_COOKIE_SECURE = env.bool("CSRF_COOKIE_SECURE", default=False)
PIP requirements
The following packages are required:
django
gunicorn
django-environ
psycopg2-binary
boto3
django-storages
With the help of the requirements.txt file, the buildtool later also recognizes that this is a python project.
App Platform
Now that everything is ready, you can start creating the Django project on DO. A detailed instruction i save me at this point, because the docs and further post about it on the internet are very good.
Important settings are (example Dev-Case):
- run_command:
gunicorn dev_case.wsgi:application --bind 0.0.0.0:8080 --worker-tmp-dir /dev/shm
- http_port:
8080
Depending on the project structure:
routes:
- path: /
source_dir: /
With a "dev" database a dynamic variable can be used as environment variable. Example: ${db.DATABASE_URL}
. In this case the name of the database would be db
.
When using a managed-databases from DO, you should create the database manually and not via the GUI. Otherwise, errors may occur with a redeploy. I will link a post about it here soon.
After the build process completes:
Access your app’s console through the console tab and run the following commands:
python3 manage.py migrate
for the initial database migrationspython3 manage.py createsuperuser
to create an administrative user
S3 and CORS
To prevent later errors with CORS, make the following adjustment in your Space setting:
Your Space -> Settings -> CORS Configurations (Add):
- Add your domain (with wildcard) in "Origin", examples:
*ondigitalocean.app
*example.com
- Allow/Check:
- GET, HEAD
This should solve the problems with fonts, scripts etc. (missing header, Access-Control-Allow-Origin).
Conclusion
I am very satisfied with DO's APP-Platform so far. The performance is good and i feel safe. Soon i will test the log forwarding. If there is something to write about it, i will link it here ;)