How to Deploy Django on Render
It is easy to get a Django app running on Render, but it is also easy to ship an unsafe or incomplete production setup.
Problem statement
It is easy to get a Django app running on Render, but it is also easy to ship an unsafe or incomplete production setup.
The common failures are predictable:
DEBUGleft enabledSECRET_KEYcommitted to Git- missing
ALLOWED_HOSTSorCSRF_TRUSTED_ORIGINS - PostgreSQL not configured from environment variables
- static files not collected or not served correctly
- migrations forgotten during deployment
- app code deployed successfully, but the database schema is still old
If you want to deploy Django on Render safely, you need a setup that covers the app server, database, static files, secrets, HTTPS-aware settings, and verification after release.
Quick answer
The shortest correct path to deploy Django on Render is:
- Prepare your Django app for production
- Add
gunicorn, a PostgreSQL driver, and optionallydj-database-urlandwhitenoise - Create a PostgreSQL database on Render
- Create a Python web service connected to your Git repository
- Set environment variables such as
SECRET_KEY,DATABASE_URL, and host/CSRF settings - Use Gunicorn as the start command
- Run
collectstaticduring build - Apply migrations as part of the release process before treating the deploy as healthy
- Verify homepage, admin, database writes, and static files
You can do this either:
- manually in the Render dashboard
- with a
render.yamlblueprint for repeatable setup
Step-by-step solution
1. Prepare your Django app for Render
Add production dependencies
Install the common packages used for a Django deployment on Render:
pip install gunicorn dj-database-url whitenoise psycopg[binary]
pip freeze > requirements.txt
If your project already uses a lockfile workflow, keep using that. The important part is that Render can install the exact dependencies during build.
Configure production settings
A minimal production-safe settings pattern looks like this:
import os
from pathlib import Path
import dj_database_url
BASE_DIR = Path(__file__).resolve().parent.parent
DEBUG = os.environ.get("DEBUG", "False").lower() == "true"
SECRET_KEY = os.environ["SECRET_KEY"]
ALLOWED_HOSTS = [
"your-service.onrender.com",
"yourdomain.com",
]
CSRF_TRUSTED_ORIGINS = [
"https://your-service.onrender.com",
"https://yourdomain.com",
]
DATABASES = {
"default": dj_database_url.parse(
os.environ["DATABASE_URL"],
conn_max_age=600,
)
}
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = False
If you prefer ALLOWED_HOSTS from environment variables, that is fine too. Just make sure the Render hostname and any custom domain are included.
A safe parsing pattern is:
ALLOWED_HOSTS = [h for h in os.environ.get("ALLOWED_HOSTS", "").split(",") if h]
CSRF_TRUSTED_ORIGINS = [o for o in os.environ.get("CSRF_TRUSTED_ORIGINS", "").split(",") if o]
If you enable HSTS, do it only after HTTPS is confirmed to work correctly on every intended domain.
Make static files work on Render
If you want Django to serve static files directly, WhiteNoise is the simplest starting point.
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
# other middleware...
]
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
"staticfiles": {
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
},
}
Then collect static files:
python manage.py collectstatic --noinput
For many apps, WhiteNoise is enough. If your static or media workload grows, object storage is usually the better next step, especially for user-uploaded files.
Verify locally in production mode
Before deploying, test with production-like settings locally:
export DEBUG=False
export SECRET_KEY='replace-me'
export DATABASE_URL='postgresql://user:pass@localhost:5432/mydb'
python manage.py collectstatic --noinput
python manage.py migrate
gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
Check:
- app boots without Django debug mode
- static files resolve
- database connection works
- no settings rely on local-only assumptions
2. Create the supporting services on Render
Create a PostgreSQL database
In Render, create a managed PostgreSQL database first.
Choose:
- the same region as the web service
- an appropriate plan for your environment
- backup expectations that match your recovery needs
Once created, Render provides a connection string you can use as DATABASE_URL.
Verification check:
- confirm the database status is healthy
- confirm you can reference its connection string from your app service
Decide between dashboard setup and render.yaml
Manual dashboard setup is fine for a first deployment or a single environment.
Use render.yaml when you want:
- repeatable environment creation
- lower config drift
- service definitions stored in Git
For teams or multiple environments, blueprint-based deployment is usually easier to maintain.
3. Deploy Django on Render from the dashboard
Connect the repository
Create a new Web Service in Render and connect your GitHub or GitLab repository.
Choose:
- the correct branch
- Python as the environment
Configure the web service
Use a build command that installs dependencies and collects static files:
pip install -r requirements.txt && python manage.py collectstatic --noinput
Use a start command that runs Gunicorn on Render’s assigned port:
gunicorn myproject.wsgi:application --bind 0.0.0.0:$PORT
Also make sure:
- the web service region matches the database region
- the instance type is appropriate for your app size
Verification check:
- build logs should show dependency installation and successful
collectstatic - startup logs should show Gunicorn booting cleanly
Set environment variables
In the Render dashboard, add at least:
SECRET_KEYDEBUG=FalseDATABASE_URL- any app-specific secrets
- optionally host lists if you load them from environment
Do not hardcode secrets in the repository.
If you manage host settings via environment variables, a common pattern is:
ALLOWED_HOSTS = [h for h in os.environ.get("ALLOWED_HOSTS", "").split(",") if h]
CSRF_TRUSTED_ORIGINS = [o for o in os.environ.get("CSRF_TRUSTED_ORIGINS", "").split(",") if o]
Then set values like:
ALLOWED_HOSTS=your-service.onrender.com,yourdomain.comCSRF_TRUSTED_ORIGINS=https://your-service.onrender.com,https://yourdomain.com
Configure migrations and static file collection
collectstatic fits naturally in the build step. Migrations need more care.
Do not let a new release depend on manual post-deploy migrations. Run:
python manage.py migrate
as part of a controlled release step before treating the deployment as healthy. If you cannot automate that safely yet, restrict production use until migration completion is verified, and prefer backward-compatible migrations.
Rollback note: application code is easier to roll back than database schema. Prefer additive, backward-compatible migrations where possible.
Add a health check path
Configure a simple health endpoint that does not require login, for example /healthz/, and point Render health checks to it if your service setup uses custom health check paths.
A minimal Django view can be as simple as:
from django.http import HttpResponse
def healthz(request):
return HttpResponse("ok", content_type="text/plain")
And in urls.py:
from django.urls import path
from .views import healthz
urlpatterns = [
path("healthz/", healthz),
]
Verification check:
/healthz/returns200- it does not depend on session state or admin login
- it still works after a fresh deploy
4. Deploy Django on Render with render.yaml
A simple blueprint can define both the web service and database:
services:
- type: web
name: my-django-app
env: python
buildCommand: pip install -r requirements.txt && python manage.py collectstatic --noinput
startCommand: gunicorn myproject.wsgi:application --bind 0.0.0.0:$PORT
envVars:
- key: SECRET_KEY
generateValue: true
- key: DEBUG
value: "False"
- key: DATABASE_URL
fromDatabase:
name: my-django-db
property: connectionString
databases:
- name: my-django-db
This is useful because the deployment definition lives with the application code. That reduces manual drift across environments.
5. Run the first production deployment
Trigger the deploy and watch the logs.
You want to see:
- dependencies installed successfully
collectstaticcomplete without errors- Gunicorn starting without import or settings failures
Ensure migrations are applied as part of the release process:
python manage.py migrate
If you need Django admin access, create a superuser:
python manage.py createsuperuser
Use this as a controlled one-time setup step. Over time, many teams replace interactive production admin setup with scripted or managed workflows.
Verification check:
- homepage returns
200 /admin/loads- login works
- a database-backed page can read and write data
- static assets load without
404 /healthz/returns200
6. Configure a custom domain and HTTPS
Attach the custom domain in Render and update your DNS records as instructed in the Render dashboard. DNS changes may take time to propagate.
After that, update Django:
ALLOWED_HOSTS = ["your-service.onrender.com", "yourdomain.com"]
CSRF_TRUSTED_ORIGINS = ["https://your-service.onrender.com", "https://yourdomain.com"]
If you use www, add both hostnames and both trusted origins.
With Render terminating HTTPS in front of your app, these settings matter:
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
Verification check:
- custom domain resolves
- HTTPS loads correctly
- forms submit without CSRF errors
- redirects do not loop
Explanation
This setup works because it matches how Django should run behind a managed platform proxy:
- Gunicorn serves the Django WSGI app
- Render provides the runtime and public endpoint
- PostgreSQL is injected through
DATABASE_URL - WhiteNoise handles static assets if you are not using external storage
- Django is made proxy-aware with
SECURE_PROXY_SSL_HEADER - secrets stay outside the repository in environment variables
Manual dashboard setup is good for getting started. render.yaml is better when you want repeatable infrastructure and fewer manual mistakes.
When manual Render setup becomes repetitive
Once you are deploying multiple environments or multiple projects, the same steps repeat: service creation, environment variable setup, build commands, migration checks, health checks, and smoke tests. That is the point where a reusable render.yaml, a hardened settings module, and a release checklist become worthwhile. Automating those parts reduces config drift more than it reduces effort.
Edge cases / notes
- Static vs media files: WhiteNoise is for static files, not user-uploaded media. If users upload files, plan external object storage.
- DisallowedHost errors: usually caused by missing Render hostname, custom domain, or
wwwvariant inALLOWED_HOSTS. - CSRF failures after adding a domain: update
CSRF_TRUSTED_ORIGINSwith fullhttps://origins. - Missing
DATABASE_URL: fail fast in production. Do not silently fall back to SQLite or an incomplete database config. - Migration risk: a bad migration can break a healthy app. Prefer additive, backward-compatible schema changes.
- Rollback: if a code change fails, redeploy the previous commit. If an environment variable change caused the issue, restore the previous value. Database rollback is harder and may require restoring from backup or applying corrective migrations.
- Health checks: keep the endpoint simple. Do not require authentication, and do not make it expensive to compute.
- Gunicorn tuning: the basic start command is enough to begin. Tune workers and timeouts later based on real traffic and memory use.
Internal links
For the settings side of this deployment, see the Django deployment checklist for production.
If you want to compare Render with a self-managed server approach, read Deploy Django with Gunicorn and Nginx on Ubuntu.
For more detail on static file handling, see how to configure Django static files in production.
For recovery planning after a bad release, read how to roll back a Django deployment safely.
FAQ
Can I deploy Django on Render without Docker?
Yes. Render supports Python web services directly, so Docker is not required for a standard Django deployment.
Do I need Gunicorn on Render for Django?
Yes, for a typical Django deployment you should run Gunicorn as the app server:
gunicorn myproject.wsgi:application --bind 0.0.0.0:$PORT
How do I run migrations on Render safely?
Run python manage.py migrate as part of a controlled release process, not as an afterthought once the new code is already serving traffic. Prefer backward-compatible migrations so old and new app versions can tolerate the same schema during rollout.
Why are my static files not loading on Render?
Usually one of these is missing:
STATIC_ROOTcollectstaticduring build- WhiteNoise middleware and storage config
- correct static file references in templates
Should I use render.yaml or configure services manually?
Use manual dashboard setup for a first deployment or a single app. Use render.yaml when you want repeatability, versioned infrastructure, and lower config drift.