Back to all posts

Deploy Django to Production with Temps

Deploy Django with Temps: git-push deploys, automatic HTTPS via Pingora, managed PostgreSQL auto-provisioned on first deploy, automatic migrations, and built-in monitoring.

Temps Team

Temps Team

June 6, 2026 · 2mo ago

Deploying Django to production without a dedicated DevOps team means wrestling with Nginx, Gunicorn process management, SSL certificates, static file serving, and database migrations — all before your first user ever hits the site. Temps collapses that entire stack into a single Rust binary you control: git-push to deploy, automatic HTTPS via Pingora (Cloudflare's open-source proxy), managed PostgreSQL auto-provisioned on first deploy, migrations that run automatically, and built-in error tracking, analytics, and uptime monitoring — free to self-host, or on the upcoming Temps Cloud managed add-on.

TL;DR: bunx @temps-sdk/cli deploy my-django-app -b main -e production -y — your Django app is live with HTTPS, managed PostgreSQL, automatic migrations, and health-checked rollbacks in under 2 minutes. Temps is Apache 2.0, self-hostable for free, with an optional Temps Cloud managed add-on (telemetry retention, offsite backups, AI credits) coming soon.


How Do I Deploy Django to Production Without a Managed Platform?

The fastest path: push your code to a git repository, connect it to Temps, and run one CLI command. Temps detects your Python project via Nixpacks, generates an optimized Dockerfile, installs your dependencies, runs python manage.py migrate before traffic switches over, and starts routing encrypted traffic — all without you touching Nginx, Gunicorn config, or SSL tooling.


What You'll Get

After this tutorial, your Django app will have:

  • Gunicorn WSGI server — the production-grade server Django recommends, configured automatically
  • Managed PostgreSQL provisioned automatically — database is ready before your first deploy completes
  • Automatic database migrationspython manage.py migrate runs on every deploy before traffic switches
  • Static files via WhiteNoise — no separate CDN or Nginx config required for serving static assets
  • Auto-rollback — health checks every 5 seconds, 2 consecutive failures trigger rollback within 60 seconds
  • Automatic HTTPS via Let's Encrypt, renewed without intervention
  • Built-in error tracking and request analytics — no Sentry subscription needed
  • Encrypted environment variablesSECRET_KEY, DATABASE_URL, and other secrets injected at runtime, never baked into images

Prerequisites

  • A Django application (3.2 or newer)
  • Git repository (GitHub, GitLab, or Bitbucket)
  • Python 3.9+ project with requirements.txt, pyproject.toml, or Pipfile

How Do I Deploy Django to Production Without Heroku?

Heroku's free tier is gone, and paying for dynos gets expensive fast — especially when you need managed Postgres, SSL, and process management. Temps gives you the same git-push workflow on infrastructure you control, with no per-seat fees and no bandwidth bills.


Comparing Django Deployment Options

FeatureTempsPythonAnywhereRenderRailway
Git-push deploysYesPartial (manual)YesYes
Managed PostgreSQLIncludedSee their pricing pageSee their pricing pageSee their pricing page
Automatic HTTPSYes (Let's Encrypt)YesYesYes
Static filesWhiteNoise (built-in)Manual config requiredManual config requiredManual config required
Error trackingBuilt-in (no extra cost)NoNoNo
Session replayBuilt-in (no extra cost)NoNoNo
Request analyticsBuilt-in (no extra cost)NoNoNo
Self-host optionYes (Apache 2.0)NoNoNo
Auto-rollbackYes (60s window)NoHealth checksHealth checks
PricingFree self-hostSee their pricing pageSee their pricing pageSee their pricing page
Vendor lock-inNone — runs on any Linux serverProprietaryProprietaryProprietary

Project Structure

Temps works with any Django project layout. Here's a minimal production-ready example:

my-django-app/
├── manage.py
├── myproject/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── requirements.txt  # or pyproject.toml / Pipfile
└── .env             # optional, local development only

requirements.txt (production-ready):

Django>=4.2
gunicorn>=21.2.0
psycopg2-binary>=2.9
whitenoise>=6.6.0
dj-database-url>=2.1.0

Deploy Django with the Temps CLI

Step 1: Install Temps CLI

# macOS / Linux
curl -fsSL https://temps.sh/install.sh | bash

Step 2: Login and Create Project

bunx @temps-sdk/cli login

# Create project and connect your git repository
bunx @temps-sdk/cli projects create \
  -n "My Django App" \
  -d "Django application" \
  --repo your-org/your-django-app \
  --branch main \
  --preset python

Temps automatically detects Python projects via Nixpacks. Use --preset docker if you already have a Dockerfile.

Step 3: Configure Environment Variables

Django requires several production environment variables. Set them before deploying:

# Django secret key (generate a strong random value)
bunx @temps-sdk/cli environments vars set SECRET_KEY "your-secret-key-here" -e production

# Database URL (Temps provisions this automatically — copy the value from your dashboard)
bunx @temps-sdk/cli environments vars set DATABASE_URL "postgresql://user:pass@host:5432/db" -e production

# Allowed hosts (your domain or Temps subdomain)
bunx @temps-sdk/cli environments vars set ALLOWED_HOSTS "my-django-app.temps.sh,yourdomain.com" -e production

# Disable debug mode in production
bunx @temps-sdk/cli environments vars set DEBUG "False" -e production

# Or import all at once from a file
bunx @temps-sdk/cli environments vars import .env.production -e production

Step 4: Configure settings.py for Production

Update your Django settings to read from environment variables:

import os
import dj_database_url

SECRET_KEY = os.environ.get("SECRET_KEY")
DEBUG = os.environ.get("DEBUG", "False") == "True"
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",")

# Database — auto-configured from DATABASE_URL env var
DATABASES = {
    "default": dj_database_url.config(
        default=os.environ.get("DATABASE_URL"),
        conn_max_age=600,
        conn_health_checks=True,
    )
}

# Static files — WhiteNoise serves them without a separate CDN
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",  # immediately after SecurityMiddleware
    # ... rest of your middleware
]

STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

# WhiteNoise compression and caching
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"

Step 5: Deploy

bunx @temps-sdk/cli deploy my-django-app -b main -e production -y

Temps will:

  1. Detect your Django application via Nixpacks
  2. Generate an optimized Dockerfile (unless you supply one)
  3. Install dependencies (pip install -r requirements.txt)
  4. Run python manage.py collectstatic --noinput
  5. Run python manage.py migrate before traffic switches over
  6. Deploy to production with Gunicorn as the WSGI server
  7. Provision an SSL certificate via Let's Encrypt

Your app is live at your-app.temps.sh within ~2 minutes.


Managed PostgreSQL

Temps provisions a managed PostgreSQL database automatically when you first deploy. You don't need to create or configure it manually.

The DATABASE_URL environment variable is injected into your container at runtime. Your dj_database_url.config() call picks it up automatically — no hardcoded credentials, no connection string in your image layers.

What's included:

  • PostgreSQL with automatic backups
  • Connection pooling ready
  • HA (high-availability) cluster available for production workloads
  • Database migrations run automatically on every deploy

Static Files with WhiteNoise

Django's built-in runserver serves static files in development, but it's not designed for production. WhiteNoise handles this cleanly without a separate Nginx or CDN configuration:

  1. Add whitenoise to requirements.txt
  2. Add whitenoise.middleware.WhiteNoiseMiddleware to MIDDLEWARE (after SecurityMiddleware)
  3. Set STATIC_ROOT and use CompressedManifestStaticFilesStorage

Temps runs python manage.py collectstatic --noinput automatically during the build phase. Your static files are served with correct cache headers and gzip compression from day one.


Health Check Endpoint

Temps polls your health endpoint every 5 seconds. Two consecutive failures within the 60-second error window trigger automatic rollback to the last healthy deployment. Add a lightweight /health view:

# urls.py
from django.urls import path
from django.http import JsonResponse

def health_check(request):
    return JsonResponse({"status": "healthy"})

urlpatterns = [
    path("health/", health_check),
    # ... your other URLs
]

Keep it fast and dependency-free. Optionally check database connectivity:

from django.db import connections
from django.db.utils import OperationalError

def health_check(request):
    try:
        connections["default"].cursor()
    except OperationalError:
        return JsonResponse({"status": "error", "db": "unreachable"}, status=503)
    return JsonResponse({"status": "healthy"})

Automatic Dockerfile Generation

You don't need Docker knowledge. Temps generates an optimized Dockerfile for your Django project:

Generated Dockerfile:

FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application
COPY . .

# Collect static files
RUN python manage.py collectstatic --noinput

# Run with Gunicorn (production WSGI server)
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]

If you have a custom Dockerfile, Temps uses that instead.


Environment Variables

Managing Variables

# Single variable
bunx @temps-sdk/cli environments vars set SECRET_KEY "$(python -c 'import secrets; print(secrets.token_urlsafe(50)')" -e production

# List all (values hidden by default)
bunx @temps-sdk/cli environments vars list -e production

# Import from file
bunx @temps-sdk/cli environments vars import .env.production -e production

Key Variables for Django Production

VariablePurposeExample
SECRET_KEYDjango cryptographic signingA 50+ character random string
DATABASE_URLPostgreSQL connection stringAuto-injected by Temps
ALLOWED_HOSTSRequest host validationmyapp.temps.sh,myapp.com
DEBUGDisable debug modeFalse
DJANGO_SETTINGS_MODULESettings file to usemyproject.settings.production

Environment variables are encrypted at rest and injected at runtime — they never appear in your Docker image layers.


Built-in Monitoring

After deployment, your Temps dashboard includes monitoring at no extra cost:

Request Analytics

  • Request count and latency percentiles (p50, p95, p99)
  • Endpoint breakdown with per-route timing
  • Error rates by view
  • Geographic distribution of traffic

Error Tracking

  • Exception capture with full Django stack traces
  • Request context (headers, user, environment)
  • Error grouping and trend analysis

No Sentry, no Datadog, no Rollbar. It's built into the same binary that runs your proxy. Temps also exposes a Sentry-compatible DSN if you want to use the Sentry SDK client-side alongside the built-in server-side tracking.

Uptime Monitoring

Temps checks your health endpoint continuously and alerts you to outages — replacing tools like Pingdom or Better Uptime for basic production monitoring.


Custom Domains

DNS Configuration

Add an A record pointing to your Temps server IP:

TypeNameValue
A@YOUR_SERVER_IP
AwwwYOUR_SERVER_IP

Adding Your Domain

# HTTP-01 challenge (default, works for most domains)
bunx @temps-sdk/cli domains add -d yourdomain.com

# DNS-01 challenge (required for wildcard domains)
bunx @temps-sdk/cli domains add -d "*.yourdomain.com" --challenge dns-01

Update ALLOWED_HOSTS in your environment variables to include your custom domain:

bunx @temps-sdk/cli environments vars set ALLOWED_HOSTS "yourdomain.com,www.yourdomain.com" -e production

Scaling Your Application

Horizontal Scaling

Scale to multiple replicas:

bunx @temps-sdk/cli environments scale -e production -r 3

Temps handles load balancing automatically via Pingora, Cloudflare's open-source Rust proxy.

Update Resource Allocation

bunx @temps-sdk/cli projects config -p my-django-app \
  --cpu-limit 2 \
  --memory-limit 1024 \
  -y
bunx @temps-sdk/cli deploy my-django-app -b main -e production -y

Troubleshooting

Build Fails

Check build logs in the Temps dashboard or stream them via CLI:

bunx @temps-sdk/cli deploy my-django-app -b main -e production

Common issues:

  • Missing packages in requirements.txt (e.g., psycopg2-binary, gunicorn, whitenoise)
  • Python version mismatch — specify FROM python:3.11-slim in a custom Dockerfile
  • collectstatic failing because SECRET_KEY isn't available at build time — set a dummy build-time value or defer static collection to startup

App Crashes on Start

# Stream live logs
bunx @temps-sdk/cli runtime-logs -p my-django-app -f
  1. Check that SECRET_KEY, DATABASE_URL, and ALLOWED_HOSTS are all set
  2. Confirm DEBUG=False is set — Django will refuse to start with a missing SECRET_KEY when DEBUG=False
  3. Confirm the Gunicorn command points to the correct WSGI module (myproject.wsgi:application)
  4. Confirm the app binds to 0.0.0.0:8000 (not 127.0.0.1)

Migrations Fail

Temps runs python manage.py migrate automatically. If migrations fail:

# Check migration status
bunx @temps-sdk/cli runtime-logs -p my-django-app -f
  • Verify DATABASE_URL is set correctly in the production environment
  • Check for unapplied squashed migrations or dependency conflicts
  • Use --run-syncdb only for apps without migrations (not recommended for production)

Health Check Failures

Temps checks your health endpoint every 5 seconds. Keep it fast:

def health_check(request):
    return JsonResponse({"status": "ok"})  # No DB query — fast and dependency-free

FAQ

Do I need Gunicorn?

Yes — Django's built-in runserver is not safe for production. Temps uses Gunicorn as the production WSGI server automatically. Nixpacks detects Django and configures Gunicorn for you; if you supply your own Dockerfile, include gunicorn in your requirements.txt and set it as the CMD.

How do I run manage.py migrate?

You don't need to. Temps runs python manage.py migrate automatically on every deploy, before traffic switches to the new container. This means your database schema is always in sync with your deployed code. If a migration fails, the deploy stops and the previous version keeps serving traffic.

Does Temps support Django Channels?

Django Channels (WebSockets and async consumers) requires an ASGI server such as Daphne or Uvicorn instead of Gunicorn. Temps supports ASGI applications — supply a custom Dockerfile that runs daphne or uvicorn myproject.asgi:application as the CMD, and Temps will deploy it the same way. The Pingora proxy handles WebSocket upgrades correctly.

Is this free?

Temps is Apache 2.0, so self-hosting is completely free — you only pay for the server you run it on. If you'd rather not operate infrastructure yourself, Temps Cloud is a managed add-on (telemetry retention, offsite backups, AI credits) coming soon; pricing hasn't been announced yet.

Can I use a different database?

Yes. While Temps auto-provisions PostgreSQL, you can connect to any external database by setting the appropriate environment variable. MySQL, SQLite (not recommended for production), and any other Django-compatible backend are supported as long as you include the driver in requirements.txt.

What Python version does Temps use?

Nixpacks reads your .python-version file, runtime.txt, or pyproject.toml to select the Python version. If none is specified, it defaults to a recent stable Python 3 release. To pin a version, add a .python-version file:

3.11

Quick Reference

# Install CLI
curl -fsSL https://temps.sh/install.sh | bash

# Login
bunx @temps-sdk/cli login

# Create project and connect repo
bunx @temps-sdk/cli projects create \
  -n "My Django App" \
  -d "Django application" \
  --repo myorg/my-django-app \
  --branch main \
  --preset python

# Set required environment variables
bunx @temps-sdk/cli environments vars set SECRET_KEY "your-secret-key" -e production
bunx @temps-sdk/cli environments vars set ALLOWED_HOSTS "my-django-app.temps.sh" -e production
bunx @temps-sdk/cli environments vars set DEBUG "False" -e production

# Deploy (migrations run automatically)
bunx @temps-sdk/cli deploy my-django-app -b main -e production -y

# Stream live logs
bunx @temps-sdk/cli runtime-logs -p my-django-app -f

# Scale replicas
bunx @temps-sdk/cli environments scale -e production -r 3

# Add custom domain
bunx @temps-sdk/cli domains add -d yourdomain.com

Ready to deploy your Django app? Get started at temps.sh — self-host for free (Apache 2.0), with a managed Temps Cloud add-on coming soon:

curl -fsSL https://temps.sh/install.sh | bash && bunx @temps-sdk/cli login
#django#deployment#python#postgresql#self-hosted#tutorial