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
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.
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.
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(',')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)}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
release: python manage.py migrate --noinput
web: gunicorn mysite.wsgi --bind 0.0.0.0:$PORT --workers 2Django>=5.1,<5.2
gunicorn>=23
dj-database-url>=2.3
psycopg[binary]>=3.2
whitenoise>=6.83.12STATIC_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.