Python

How to Deploy a Django App to Render for Free (2026)

A complete step-by-step guide to deploying a Django app to Render for free — gunicorn, static files, a free PostgreSQL database, environment variables, and auto-deploys from GitHub.

June 9, 202611 min read
Share
Advertisement (not configured)

Introduction

You built a Django app on localhost:8000 — now you want the world to see it. The problem? Most "easy" hosts either cost money, hide a credit card behind the free tier, or make you wrestle with servers you don't understand.

Render is one of the few platforms that lets you deploy a real Django app — with a PostgreSQL database — completely free, straight from your GitHub repo. No credit card needed to start, and every push to main redeploys automatically.

This guide walks you through the whole thing: preparing your project, configuring production settings, adding gunicorn, serving static files, wiring up a free database, and going live. By the end your app will be running on a public https:// URL.

Heads up on the free tier: Render's free web services spin down after ~15 minutes of inactivity, so the first request after idle takes ~30–50 seconds to wake up. The free PostgreSQL database expires after 90 days. It's perfect for portfolios, demos, and side projects — not production traffic.

What You'll Need

  • A working Django project pushed to a GitHub repository
  • A free Render account (sign up with GitHub)
  • Python 3.10+ locally
  • About 15 minutes

Step 1 — Get Your Project Production-Ready

A few packages turn your local app into something a server can run.

pip install gunicorn whitenoise dj-database-url psycopg2-binary
Package Why you need it
gunicorn Production WSGI server (replaces runserver)
whitenoise Serves static files without needing Nginx
dj-database-url Parses Render's DATABASE_URL into Django settings
psycopg2-binary PostgreSQL driver

Now freeze everything into a requirements.txt so Render knows what to install:

pip freeze > requirements.txt

Step 2 — Configure settings.py for Production

Never hardcode secrets or run with DEBUG = True in production. Pull config from environment variables instead.

import os
import dj_database_url

# SECURITY
SECRET_KEY = os.environ.get("SECRET_KEY", "unsafe-dev-key")
DEBUG = os.environ.get("DEBUG", "False") == "True"

ALLOWED_HOSTS = ["localhost", "127.0.0.1"]

# Render provides the external hostname here
RENDER_HOST = os.environ.get("RENDER_EXTERNAL_HOSTNAME")
if RENDER_HOST:
    ALLOWED_HOSTS.append(RENDER_HOST)

Add WhiteNoise middleware

Insert WhiteNoise right after the security middleware:

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",  # add this line
    "django.contrib.sessions.middleware.SessionMiddleware",
    # ... the rest stays the same
]

Static files config

STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
STORAGES = {
    "staticfiles": {
        "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
    },
}

Database config

This one line uses your local SQLite by default but switches to Render's PostgreSQL automatically when DATABASE_URL is present:

DATABASES = {
    "default": dj_database_url.config(
        default=f"sqlite:///{BASE_DIR / 'db.sqlite3'}",
        conn_max_age=600,
    )
}

Step 3 — Add a Build Script

Render needs to know how to build your app. Create a file named build.sh in your project root:

#!/usr/bin/env bash
set -o errexit  # exit on any error

pip install -r requirements.txt
python manage.py collectstatic --no-input
python manage.py migrate

Make it executable so Render can run it:

chmod +x build.sh
git add build.sh

Step 4 — Push Everything to GitHub

Make sure your .gitignore keeps secrets and junk out of the repo:

*.pyc
__pycache__/
db.sqlite3
.env
staticfiles/

Then commit and push:

git add .
git commit -m "Prepare Django app for Render deployment"
git push origin main

Step 5 — Create the Web Service on Render

  1. Log in to Render and click New → Web Service.
  2. Connect your GitHub account and select your repository.
  3. Fill in the settings:
Field Value
Name my-django-app (becomes part of your URL)
Region Closest to your users
Branch main
Runtime Python 3
Build Command ./build.sh
Start Command gunicorn myproject.wsgi:application
Instance Type Free

Replace myproject in the start command with the folder that contains your wsgi.py (the same name as your Django project).

Don't click Create just yet — add your environment variables first.

Step 6 — Add Environment Variables

Scroll to the Environment section and add:

Key Value
SECRET_KEY A long random string (generate one below)
DEBUG False
PYTHON_VERSION 3.12.0

Generate a strong secret key locally:

python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"

Paste the output as the value for SECRET_KEY.

Step 7 — Add a Free PostgreSQL Database

SQLite won't survive on Render — its filesystem is wiped on every deploy. Use a real database instead.

  1. Click New → PostgreSQL.
  2. Give it a name, pick the Free plan, and create it.
  3. Open the database, copy its Internal Database URL.
  4. Go back to your web service → Environment → add:
Key Value
DATABASE_URL (paste the Internal Database URL)

Because of the dj-database-url line from Step 2, Django picks this up automatically — no further code changes needed.

Step 8 — Deploy

Click Create Web Service (or Manual Deploy → Deploy latest commit). Render now:

  1. Clones your repo
  2. Runs build.sh — installs packages, collects static files, runs migrations
  3. Boots gunicorn

Watch the Logs tab in real time. When you see something like:

==> Your service is live 🎉
[INFO] Booting worker with pid: 42

your app is live at https://my-django-app.onrender.com.

Step 9 — Create Your Admin User

You can't run createsuperuser interactively on Render, so use Render's Shell tab (under your service) and run:

python manage.py createsuperuser

Then visit https://my-django-app.onrender.com/admin/ and log in.

Auto-Deploys: Push to Deploy

By default, Render redeploys every time you push to main. Your workflow is now just:

git add .
git commit -m "Add new feature"
git push origin main

Render detects the push, reruns build.sh, and ships the update — zero manual steps.

Troubleshooting Common Errors

Error Cause & Fix
DisallowedHost / Bad Request (400) RENDER_EXTERNAL_HOSTNAME not added to ALLOWED_HOSTS — recheck Step 2
CSS/JS not loading WhiteNoise missing from middleware, or collectstatic didn't run
ModuleNotFoundError: gunicorn gunicorn not in requirements.txt — run pip freeze again
App won't start Wrong project name in the start command — check myproject.wsgi
DB connection refused DATABASE_URL missing or using the External instead of Internal URL
First request hangs ~30s Normal — free instances sleep after 15 min of inactivity

Free Tier Limits to Remember

  • Web service sleeps after 15 minutes idle (cold starts take ~30–50s).
  • Free PostgreSQL expires after 90 days — back up your data or upgrade before then.
  • 750 free instance hours/month shared across your free services.
  • No persistent disk — always use the database for anything you need to keep.

A common trick for demos: use a free uptime monitor (like UptimeRobot) to ping your URL every 10 minutes so it never sleeps.

Final Thought

Deploying Django used to mean provisioning a VPS, configuring Nginx, and writing systemd files at midnight. Render collapses all of that into a build script and a few environment variables — and it's free enough to host every side project in your portfolio.

Get your first app live, share the URL, and the next deploy is just a git push away. That's the whole point: spend your time building features, not babysitting servers.

Advertisement (not configured)

Written by

Raretechsol

International software company specializing in Python and JavaScript. Passionate about automation, AI, and building practical web applications.

Related Articles