DEV Community

Cover image for Django deployment with App Platform & S3 Space
rburkhardt
rburkhardt

Posted on • Updated on • Originally published at rburkhardt.com

Django deployment with App Platform & S3 Space

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",
    ],
)
Enter fullscreen mode Exit fullscreen mode

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",
        }
    }

Enter fullscreen mode Exit fullscreen mode

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/"

Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

PIP requirements

The following packages are required:

django
gunicorn
django-environ
psycopg2-binary
boto3
django-storages
Enter fullscreen mode Exit fullscreen mode

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: /
Enter fullscreen mode Exit fullscreen mode

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.

edited (2022-09-26): link to the post mentioned above.

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 migrations
  • python3 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.


Thanks for reading.

Originally published at rburkhardt.com
Feel free to subscribe to my RSS and connect on Twitter or Github

Top comments (0)