Deploy / Django
Deploy Django to Your Own Server
To deploy Django to your own server, install Temps on a Linux VPS and run up --preset python from the project root. Temps installs your dependencies and starts Gunicorn on the port it injects. You link a managed PostgreSQL database, read its POSTGRES_* variables in settings.py, and run migrate yourself after deploys that change the schema.
Last updated
Detection
Auto-detected from requirements.txt, pyproject.toml, Pipfile or setup.py: Temps picks the Python preset, so up needs no flag.
How to deploy Django with Temps
Commands run on your machine unless they say otherwise. The CLI runs with bunx or npx.
- 1
Install Temps on a Linux server
On a VPS with at least 2 CPU cores and 4 GB of RAM, download the installer, read it, then run it. It installs Docker if needed, the Temps binary and its database, and asks how the console is reached: an sslip.io address or your own domain.
curl -fsSL https://temps.sh/deploy.sh -o deploy.sh less deploy.sh bash deploy.sh
- 2
Log in from your machine
Point the CLI at your Temps console. It opens a browser to approve the login.
bunx @temps-sdk/cli login https://console.example.com
- 3
Add the production dependencies
Temps adds Gunicorn when it's missing, but not once a
Procfileweb:line takes over, so list it yourself.requirements.txtDjango>=5.1 gunicorn>=22.0 whitenoise>=6.6 psycopg[binary]>=3.1
- 4
Point the health check at a path that doesn't need the database
Before switching traffic, Temps probes
/until it answers. On the first deploy the tables don't exist yet, so if/queries the database the deploy fails before you can migrate. Sethealth.pathto/health/in.temps.yaml..temps.yamlhealth: path: /health/
- 5
Deploy Django
From the project root, run
up. If the directory isn't linked to a project yet, it walks you through creating one first. For a project created from a Git repository, pushes to the tracked branch deploy automatically.bunx @temps-sdk/cli up
- 6
Create and link a PostgreSQL database
Temps doesn't create a database for you. Create one, then link it to the project (or use Databases → Create database in the dashboard). Linking injects
POSTGRES_URLplusPOSTGRES_HOST,POSTGRES_PORT,POSTGRES_DB,POSTGRES_USERandPOSTGRES_PASSWORDinto the next deployment. It doesn't injectDATABASE_URL.bunx @temps-sdk/cli services create -t postgres -n my-django-app-db bunx @temps-sdk/cli services link --id <service-id> -p my-django-app
- 7
Run migrations after deploys that change the schema
Temps doesn't run migrations and has no release phase. Find the running container, then run the migration in it over SSH, by hand or from CI right after the deploy step. Don't add it to the start command: restarts and replicas would run it more than once.
bunx @temps-sdk/cli projects show -p my-django-app --json # note the numeric "id" bunx @temps-sdk/cli containers list -p <project-id> # note the container_name ssh your-server 'docker exec <container_name> python manage.py migrate'
Production settings for Django on Temps
These blocks read everything from environment variables, so the same settings.py works locally and on Temps. They match the required settings in the Django reference.
# settings.py
import os
SECRET_KEY = os.environ["SECRET_KEY"]
DEBUG = os.environ.get("DEBUG", "False") == "True"
# Temps doesn't inject your domain, so list it in a project variable:
# ALLOWED_HOSTS=app.example.com,www.example.com
ALLOWED_HOSTS = [h.strip() for h in os.environ.get("ALLOWED_HOSTS", "").split(",") if h.strip()]
# Use the linked managed PostgreSQL; Temps injects these variables.
if os.environ.get("POSTGRES_HOST"):
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ.get("POSTGRES_DB"),
"USER": os.environ.get("POSTGRES_USER"),
"PASSWORD": os.environ.get("POSTGRES_PASSWORD"),
"HOST": os.environ["POSTGRES_HOST"],
"PORT": os.environ.get("POSTGRES_PORT", "5432"),
}
}
# Static files, served by WhiteNoise from the app container
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
# ...the rest of your middleware, unchanged
]A health view that doesn't touch the database
Pair it with health.path: /health/ in .temps.yaml.
# urls.py
from django.http import JsonResponse
from django.urls import path
def health(request):
# No database query, so the first deploy passes before you migrate.
return JsonResponse({"status": "ok"})
urlpatterns = [
path("health/", health),
# ...your other routes
]Report exceptions to Temps error tracking
Temps injects SENTRY_DSN for the project, so the standard Sentry SDK needs no other configuration.
# settings.py — add sentry-sdk[django] to requirements.txt
import sentry_sdk
sentry_sdk.init(dsn=os.environ.get("SENTRY_DSN"))Common Django deploy errors and fixes
- The container exits with
gunicorn: not found. - A
Procfileweb:line replaces the start command Temps generates, and Temps then stops adding Gunicorn for you. Listgunicorn(andwhitenoise) inrequirements.txtorpyproject.toml. - CSS and JavaScript return 404 and the admin is unstyled.
- Set
STATIC_ROOTand add WhiteNoise as above. Nightly builds runcollectstaticduring the build; on beta.56 and earlier, run it from the Procfile:web: python manage.py collectstatic --noinput && gunicorn myproject.wsgi:application --bind 0.0.0.0:$PORT. - The first deploy fails its health check.
- If
/queries tables that don't exist yet, the check fails before you can runmigrate. Pointhealth.pathin.temps.yamlat the/health/view above, deploy, then run the migration. - Data disappears after every deploy.
- Django is still on its SQLite default. App containers have no persistent volume, so link a PostgreSQL database and read the
POSTGRES_*variables. - Error pages show settings and file paths.
DEBUGis on. SetDEBUG=Falseas a project variable; Django also starts checkingALLOWED_HOSTSonce it's off.
How Temps builds and runs Django
Every app runs in a Docker container on your server, behind the Temps proxy. Before traffic switches to a new deployment, Temps health-checks it; if the check fails, the deployment is marked failed and the current version keeps serving.
| Install | uv, Poetry, Pipenv or pip, chosen from your lockfile or manifest |
|---|---|
| Start | gunicorn <project>.wsgi:application --bind 0.0.0.0:$PORT when manage.py and <project>/wsgi.py exist; one worker by default, and a Procfile web: line replaces it |
| Python | From .python-version, runtime.txt or requires-python; defaults to 3.12 |
| Static files | collectstatic runs during the build on nightly builds; on beta.56 and earlier, run it from the Procfile |
| Migrations | Not run by Temps; run manage.py migrate yourself |
What works with Django on Temps
- Gunicorn started for you, or your own Procfile
web:line - Static files served by WhiteNoise
- Managed PostgreSQL you create and link
- Health check before each cutover
- Error tracking through any Sentry SDK
- Preview environments per branch (opt-in)
Managed services for Django
Temps runs these services on your server. Create one, link it to the project, and its connection variables reach the next deployment. Set up managed services.
PostgreSQL
Injects POSTGRES_URL and POSTGRES_HOST/PORT/DB/USER/PASSWORD once linked
Redis
Injects REDIS_URL for Django's cache or sessions
Environment variables for Django
Set your own with environments vars set or in the project settings; values are encrypted at rest.
| Variable | What it does |
|---|---|
| SECRET_KEY | A new secret for production |
| DEBUG | False in production |
| ALLOWED_HOSTS | Every hostname the app is served on, comma-separated |
| POSTGRES_URL, POSTGRES_HOST … | Injected by Temps when PostgreSQL is linked |
| SENTRY_DSN | Injected by Temps for the built-in error tracking |
What it costs to self-host Django
Temps is free to self-host under MIT / Apache 2.0, with no per-seat fee and nothing metered, so hosting Django costs what the server costs. A Hetzner CX23 (2 shared vCPU, 4 GB RAM, 40 GB NVMe, 20 TB traffic) meets the minimum Temps needs and is €5.49/month excluding VAT, plus €0.50/month for an IPv4 address; Hetzner sells it in Germany and Finland only. Prices checked October 10, 2026.
| Temps | Free and open source (MIT / Apache 2.0) |
|---|---|
| Server | Hetzner CX23: €5.49/month excluding VAT, plus €0.50/month for IPv4 |
| What Django runs | One Gunicorn container, plus the managed PostgreSQL (and Redis, if you link one) on the same server. Celery workers need their own service in a Docker Compose project. |
| Observability | Web analytics, error tracking, session replay and uptime monitoring are included and run on the same server |
| Temps Premium | Optional: $399 per organisation per month for SAML SSO, SCIM, custom roles, approval gates and a tamper-evident audit log |
For one to three production apps, the server guide recommends 4 vCPU and 8 GB RAM: a Hetzner CX33 is €8.49/month excluding VAT. Any Linux VPS that meets the minimum works; sizes and other providers are in Choosing a server. For what the same app costs on Heroku, see Temps vs Heroku.
Django on Temps: frequently asked questions
Does Temps run Django migrations automatically?
No. Run python manage.py migrate yourself after any deploy that changes the schema: find the container with containers list, then run ssh your-server 'docker exec <container_name> python manage.py migrate', by hand or from CI. Don't put migrate in the start command, because restarts and replicas would run it more than once.
Why is Django using SQLite instead of PostgreSQL on Temps?
Temps injects POSTGRES_URL and the POSTGRES_* variables when a database is linked; it doesn't inject DATABASE_URL. If settings.py only reads DATABASE_URL, Django falls back to its SQLite default, and that file is lost on the next deploy because containers have no persistent volume. Read the POSTGRES_* variables, or pass os.environ["POSTGRES_URL"] to dj_database_url.parse().
How do I fix DisallowedHost (Invalid HTTP_HOST header) on Temps?
Temps doesn't inject your domain, so set ALLOWED_HOSTS as a project environment variable listing every hostname the app is served on (your custom domain, or the sslip.io hostname shown in the dashboard) and split it in settings.py. Django only checks the Host header when DEBUG=False, which is why it works locally.
Can I run Celery workers with Django on Temps?
Not as Procfile processes: Temps uses the web: line only and ignores worker: and beat:. Run Celery as a service in a Docker Compose project, or outside Temps. A linked Redis gives you REDIS_URL for the broker.
Does Temps support Django Channels and ASGI?
Yes. Replace the start command with a Procfile line such as web: daphne -b 0.0.0.0 -p $PORT myproject.asgi:application and add Daphne (or Uvicorn) to your dependencies. The reverse proxy passes WebSocket upgrades through.
How do I serve Django static files on Temps?
With WhiteNoise: add it to your dependencies, put whitenoise.middleware.WhiteNoiseMiddleware right after SecurityMiddleware, set STATIC_ROOT, and use the STORAGES setting with CompressedManifestStaticFilesStorage. Nightly builds of Temps run collectstatic during the build; on beta.56 and earlier, run it in the Procfile web: line before Gunicorn starts.
How do I read my Django app's logs on Temps?
Stream them with bunx @temps-sdk/cli runtime-logs -p my-django-app -f, or open the project's logs in the dashboard. Gunicorn and Django write to stdout and stderr, which Temps collects.
Included with every Temps project
Deployments, analytics, error tracking, session replay and uptime monitoring run in the same self-hosted platform, so your Django app doesn't need separate subscriptions for them.
Automatic HTTPS
Let's Encrypt certificates for every domain, issued and renewed for you.
Preview deployments
Turn on preview environments and each branch gets its own URL on your domain.
Web analytics
First-party analytics stored on your server. Add the React provider or a script tag.
Error tracking
Sentry-compatible: any Sentry SDK can report to the SENTRY_DSN Temps injects.
Session replay
Off by default. When on, it records the DOM with every input masked.
Uptime monitoring
HTTP monitors for your apps, with alerts through your notification channels.
Related guides
- Every setting for this stack is in the Django deployment reference.
- Which stacks are detected and which need a preset is listed in supported languages and frameworks.
- How health checks, cutover and rollbacks work is covered in Deployments.
- Moving a Django app off Heroku? Follow Migrate from Heroku.
- For a side-by-side of the two platforms, see Temps vs Heroku.
- Python API instead of a full Django site? See Deploy FastAPI to your own server.
- Comparing Heroku alternatives? See 9 best Heroku alternatives.
- Running migrations safely on deploy is covered in how to run database migrations safely on deploy.
- Backing up the database? Read automating PostgreSQL backups in Docker.
Deploy another framework
Deploy Django on a server you own
Temps is free and open source to self-host (MIT / Apache 2.0). You pay only for the server.