Django buildpack

Django hosting in India, deployed from GitHub

Push a Django project and the Python buildpack installs requirements.txt, collects your static files and starts gunicorn from the Procfile. The database is managed PostgreSQL in the same plan, migrations run on every deploy, and you never log in to a server.

From ₹100/month + 18% GST, PostgreSQL included, billed in INR.

Deploy Django in 5 steps

  1. Prepare the project and connect GitHub

    Next to manage.py, commit a requirements.txt (Django, gunicorn, dj-database-url, whitenoise), a Procfile and a .python-version file. Then create an app in the dashboard and connect the GitHub repository and branch that holds them.

  2. The Python buildpack takes over

    requirements.txt marks the repository as Python. The buildpack installs the Python release named in .python-version, pip-installs your requirements and, because manage.py is present, runs collectstatic. The Procfile web line then starts gunicorn bound to $PORT.

  3. Set the Django settings as environment variables

    Add DJANGO_SECRET_KEY, DEBUG=False and ALLOWED_HOSTS (your free address and your domain, comma-separated) in the dashboard, and read them in settings.py with os.environ. Nothing secret needs to be committed.

    SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
    DEBUG = os.environ.get('DEBUG') == 'True'
    ALLOWED_HOSTS = os.environ['ALLOWED_HOSTS'].split(',')
  4. Wire PostgreSQL through DATABASE_URL

    Attach a PostgreSQL 16 database and DATABASE_URL is added to the environment. dj-database-url turns it into the DATABASES setting, and the Procfile release line applies migrations before each new version serves a request.

    import dj_database_url
    DATABASES = {'default': dj_database_url.config(conn_max_age=600)}
  5. Add your domain, then trust it in Django

    Add yourdomain.in in the dashboard, publish the TXT record, then the A or CNAME record. Put the domain in ALLOWED_HOSTS and in CSRF_TRUSTED_ORIGINS (with https://) before you switch DNS, otherwise the admin login and every form post fail on the new address.

A minimal Django app that deploys

Procfile
release: python manage.py migrate --noinput
web: gunicorn mysite.wsgi --bind 0.0.0.0:$PORT --workers 2
requirements.txt
Django>=5.1,<5.2
gunicorn>=23
dj-database-url>=2.3
psycopg[binary]>=3.2
whitenoise>=6.8
.python-version
3.12
mysite/settings.py (excerpt)
STATIC_ROOT = BASE_DIR / 'staticfiles'
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
MEDIA_ROOT = '/app/data/media'
CSRF_TRUSTED_ORIGINS = ['https://yourdomain.in']

Django gotchas to know before you push

  • collectstatic needs STATIC_ROOT

    The buildpack runs collectstatic during the build, and the build fails if STATIC_ROOT is not set. There is no separate static server in front of gunicorn, so serve the collected files with WhiteNoise. To skip the step, set DISABLE_COLLECTSTATIC=1.

  • Keep user uploads in /app/data

    Point MEDIA_ROOT at /app/data/media. That volume is the only path that persists between deploys; files Django writes anywhere else in the project are lost the next time you push.

  • Leave SECURE_SSL_REDIRECT off

    Plain HTTP is already redirected to HTTPS before a request reaches gunicorn. Turning on SECURE_SSL_REDIRECT adds nothing and, depending on the forwarded headers, can send the browser round in a redirect loop.

  • Size gunicorn to the plan's memory

    Every gunicorn worker is a full copy of your Django project in memory. On a 512 MB app start with --workers 2, watch the per-app memory chart in the dashboard, and add workers only while there is room.

FAQ

Django hosting questions

No. runserver is Django's development server and Django's own documentation says not to use it in production. Add gunicorn to requirements.txt and start it from the Procfile web line; for async views, run gunicorn with uvicorn workers.
From the release line in the Procfile, release: python manage.py migrate --noinput. It runs once per deploy, after the build and before the new version takes traffic; if a migration fails, the deploy stops and the previous version keeps serving.
The one in .python-version at the repository root, written as major.minor, for example 3.12. Without that file the buildpack uses its current default, which changes when the buildpack is updated, so pin the version your tests run on.
Yes. They use the same Python buildpack; only the Procfile changes. Flask: web: gunicorn app:app --bind 0.0.0.0:$PORT. FastAPI: web: gunicorn main:app -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:$PORT, with uvicorn in requirements.txt.
Prefer an app? Add this site to your home screen.Get the app
Django Hosting India – Python PaaS from ₹100/mo | HostCore