DocsDjango

Django

Deploy a Django app on Ruust: built with Nixpacks, served by Gunicorn, with migrations run on each deploy.

Ruust does no framework detection of its own: it hands your repo to Nixpacks (or to your Dockerfile), and it is Nixpacks that detects Python and Django from requirements.txt and a manage.py at the repo root, then builds it (no configuration needed). You serve the app with Gunicorn against your project’s WSGI callable. Because an Egg is a long-running server that is always on, background work and server-side rendering keep working with no cold starts. If you keep a Dockerfile at the repo root, Ruust builds that instead of using Nixpacks. Pin your dependencies in requirements.txt, including gunicorn, your database driver and whitenoise for static files.

text
Django>=5.0
gunicorn>=22.0
dj-database-url>=2.1
whitenoise>=6.6
psycopg[binary]>=3.1
requirements.txt: pin Django, Gunicorn, WhiteNoise and your database driver.

Deploy it

  1. Push your app to a Git repo with requirements.txt, manage.py and a Procfile at the root.
  2. In the dashboard, choose New Egg and connect the repo.
  3. Add the environment variables below on the Egg (SECRET_KEY, ALLOWED_HOSTS, DATABASE_URL, DEBUG=False).
  4. Pick a size and a region (London or Virginia), then lay the Egg.
  5. Watch it incubate, hatch and go live on its <name>.<region>.ruust.run URL.

The port

Ruust sets a PORT env var and your app must listen on it and bind to 0.0.0.0 (not localhost or 127.0.0.1). Binding to the wrong address is the top reason an Egg builds but never hatches. A Procfile at the repo root pins the start command: put your build-time collectstatic and migrate steps on a release: line, then run Gunicorn on the web: line with -b 0.0.0.0:$PORT so it honours the contract. The web: line is honoured by the Nixpacks build, which bakes it into the image as the start command at build time, not by Ruust itself, and is ignored when you supply your own Dockerfile.

procfile
release: python manage.py collectstatic --noinput && python manage.py migrate --noinput
web: gunicorn myproject.wsgi -b 0.0.0.0:$PORT
Procfile: collect static and migrate on deploy, then bind Gunicorn to $PORT on 0.0.0.0. Replace `myproject` with your settings package.

Environment variables and settings

Set your configuration as environment variables on the Egg. They are available at build and run time, encrypted at rest and never printed in logs. Read them in settings.py: keep DEBUG=False in production, set SECRET_KEY from the environment, list your hostnames in ALLOWED_HOSTS (comma-separated), and parse DATABASE_URL with dj-database-url. Set DJANGO_SETTINGS_MODULE only if your settings module is not the default myproject.settings.

python
import os
import dj_database_url

SECRET_KEY = os.environ['SECRET_KEY']
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')

DATABASES = {
    'default': dj_database_url.config(conn_max_age=600),
}

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    # ...the rest of your middleware
]

STATIC_ROOT = BASE_DIR / 'staticfiles'
CSRF_TRUSTED_ORIGINS = ['https://' + h for h in ALLOWED_HOSTS if h]
settings.py: read configuration from the environment and serve static files with WhiteNoise.