Django hosting with gunicorn and a real database
Push the repository, set the build and start commands, give the app an address. Python is detected from requirements.txt, static files are collected in the build, and a managed PostgreSQL is one click when SQLite stops being enough.
What the repository needs
- requirements.txt with
django,gunicornandwhitenoisefor static files. - Build command:
python manage.py collectstatic --noinput && python manage.py migrate. - Start command:
gunicorn project.wsgi -b 0.0.0.0:$PORT. SECRET_KEY,DEBUG=0,ALLOWED_HOSTSandDATABASE_URLin environment variables.
The database
SQLite at /data/db.sqlite3 survives redeploys and is fine for a small site. For anything with concurrent writes, create a managed PostgreSQL in the panel, put its URL in DATABASE_URL with dj-database-url, and let the build's migrate step apply your migrations on every deploy.
Static and media files
WhiteNoise serves collected static files from the app itself, which is enough behind the Cloudflare proxy. User uploads go under /data/media so they persist; set MEDIA_ROOT there.
Frequently asked questions
Does the admin work?
Yes, at /admin as usual. Create the superuser without a shell: put DJANGO_SUPERUSER_USERNAME, DJANGO_SUPERUSER_EMAIL and DJANGO_SUPERUSER_PASSWORD in the environment, add python manage.py createsuperuser --noinput to the build command for one deploy, then take it out again.
CSRF errors behind the proxy?
Add the app's https address (and your domain) to CSRF_TRUSTED_ORIGINS and set SECURE_PROXY_SSL_HEADER to ("HTTP_X_FORWARDED_PROTO", "https").
Which port does the app listen on?
The PORT environment variable, 8080 by default, on 0.0.0.0. A server bound to 127.0.0.1 answers only itself and the web address gets nothing.
How does the public address work?
Give the app a web address in the panel and it is served at https://name.fadehost.app behind the Cloudflare proxy with a certificate. It comes with the plan on every tier. On the free tier the address sleeps when nobody has used it for a while and wakes on the next request; on a paid tier it stays awake. Your own domain can point at it.
What happens on a deploy?
The repository is cloned at your branch, dependencies install, the build runs when there is one, the app starts. With auto-deploy on, every push does the same. The live console shows each step; a failed one is explained in plain words.