Deploy Django on Zenifra
This guide takes a Django project from a GitHub repository to production, with static files, a managed database, and secure settings.
Prerequisites
- A Django project in a GitHub repository, with
manage.pyat the root requirements.txtwithdjango,gunicorn,whitenoise, and the database driver- GitHub account connected to Zenifra
Django==5.1.1
gunicorn==23.0.0
whitenoise==6.7.0
psycopg[binary]==3.2.2
dj-database-url==2.2.0The examples assume the settings package is called config (config/settings.py, config/wsgi.py). Replace it with your project's name.
Adjust settings.py
import os
import dj_database_url
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = os.environ.get("DJANGO_DEBUG", "false").lower() == "true"
ALLOWED_HOSTS = [".clients.zenifra.com"] + [
h for h in os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",") if h
]
CSRF_TRUSTED_ORIGINS = ["https://*.clients.zenifra.com"] + [
f"https://{h}" for h in os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",") if h
]
# Zenifra terminates HTTPS and reports the original protocol in this header
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
DATABASES = {
"default": dj_database_url.config(conn_max_age=600, conn_health_checks=True)
}
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
# ... other middleware
]
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}WhiteNoiseMiddleware must come right after SecurityMiddleware.
Environment variables
| Variable | Example | Purpose |
|---|---|---|
DJANGO_SECRET_KEY | long random value | Signs sessions and tokens |
DATABASE_URL | postgres://user:password@host:5432/db | Database connection |
DJANGO_ALLOWED_HOSTS | app.mycompany.com | Custom domains, comma-separated |
DJANGO_DEBUG | false | Never true in production |
Generate a key with python -c "import secrets; print(secrets.token_urlsafe(50))". Variables also exist during the build, which lets collectstatic run with the full settings.py.
Console configuration
| Field | Value |
|---|---|
| Runtime | Python 3.12 |
| Port | 8000 |
pre-build | empty |
build | python manage.py collectstatic --noinput |
start | python manage.py migrate --noinput && gunicorn config.wsgi --bind 0.0.0.0:8000 --workers 2 --access-logfile - |
Why migrate in start?
In start, the migration runs with database access in the app's final environment, right before the server comes up. Django migrations use transactions on PostgreSQL, so a failure does not leave the schema half-applied.
User uploads (MEDIA)
Files uploaded by users cannot live on the instance's ephemeral disk. Choose one path:
- Persistent storage with the
/app/mediadirectory andMEDIA_ROOT = "/app/media". To serve those files, use your own view or an object storage service. - An S3-compatible object storage service with
django-storages.
Background tasks with Celery
Deploy the Celery worker as another project, from the same repository, with start set to celery -A config worker --loglevel=info. Use the managed cache or managed queues as the broker.
Create the superuser
Set DJANGO_SUPERUSER_USERNAME, DJANGO_SUPERUSER_EMAIL, and DJANGO_SUPERUSER_PASSWORD, and prepend this to start once:
python manage.py createsuperuser --noinput || trueAfter the user exists, remove the command and the password variable.
Common problems
| Symptom | Fix |
|---|---|
DisallowedHost | Add the domain to ALLOWED_HOSTS or DJANGO_ALLOWED_HOSTS |
CSRF verification failed | Add https://your-domain to CSRF_TRUSTED_ORIGINS |
| CSS and JS return 404 | collectstatic did not run or WhiteNoise is missing from MIDDLEWARE |
Infinite redirect with SECURE_SSL_REDIRECT | Set SECURE_PROXY_SSL_HEADER as in the example |
KeyError: 'DJANGO_SECRET_KEY' during the build | Add the variable to the project before deploying |
too many connections | Lower --workers or conn_max_age; see Connection problems |
Next steps
Last updated on
Python with FastAPI and Flask
Deploy Python APIs on Zenifra with FastAPI or Flask: requirements.txt, uvicorn, gunicorn, workers, port, Alembic migrations, and common errors.
Ready-made Dockerfiles for Go, Java, PHP, and .NET
Tested Dockerfiles to deploy Go, Spring Boot, Laravel, and ASP.NET Core on Zenifra as OCI images, with environment variables, a configurable port, build, and push.