Deploy Django on Temps
Temps detects a Django app from requirements.txt, pyproject.toml, setup.py or Pipfile and uses the python preset: it finds manage.py and <project>/wsgi.py and starts Gunicorn on $PORT with HTTPS. Set STATIC_ROOT and WhiteNoise for static files, link managed PostgreSQL (injected as POSTGRES_URL and POSTGRES_*), and run manage.py migrate yourself after schema changes.
This guide describes how the nightly release channel builds your app, using the Autopack builder. Beta (v0.1.0-beta.56) also builds with Autopack, but some details below may differ. Earlier releases and the stable channel build with Nixpacks and behave differently. To run nightly, see Release channels.
For an overview of self-hosting Django, including costs, see Deploy Django to your own server. Every framework and the preset it needs is listed under Supported platforms.
Quickstart
From your project root, deploy with your preferred package manager:
npx @temps-sdk/cli up --preset python
Temps installs your dependencies and starts your app with Gunicorn. When it finds a manage.py and a <project>/wsgi.py, it runs gunicorn <project>.wsgi:application --bind 0.0.0.0:$PORT, and adds Gunicorn to the build if your dependencies don't include it. The Python version comes from .python-version, runtime.txt or requires-python in pyproject.toml, and defaults to 3.12.
Temps doesn't create a database for you. To use PostgreSQL, add one as described in Managed PostgreSQL.
What Temps handles automatically
| Feature | How Temps handles it |
|---|---|
| Install | uv, Poetry, Pipenv or pip, chosen from your lockfile or manifest (for example pip install -r requirements.txt) |
| WSGI server | Gunicorn with its default settings (one worker); set your own command in a Procfile |
| Static files | Collected during the build on nightly; needs STATIC_ROOT — see Required Settings |
| HTTPS | Let's Encrypt certificate, auto-renewed |
| Port | PORT env var injected, defaults to 8000 |
| Health checks | HTTP check on / at deploy time, or the path set in .temps.yaml |
Database migrations are not run automatically — see Run migrations below.
Run migrations
Temps does not have a remote "run one command" CLI action yet, so migrations are run directly against the container over SSH. Find the container name, then run the migration:
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'
Run this manually (or from your CI/CD pipeline right after the deploy step) every time a deploy includes a schema change.
On the first deploy the tables don't exist yet. If your app's / needs the database, the deploy fails its health check before you can migrate, so point the health check at a view that doesn't, such as a simple /health view, with health.path in .temps.yaml (see Health checks).
Do not add python manage.py migrate to your Dockerfile's CMD or start command — with rolling deploys, restarts, or multiple replicas, the container start command can run more than once, and concurrent migration runs against the same database can conflict or corrupt data.
Required Settings
Ensure your settings.py is production-ready:
# settings.py
import os
# Temps does not inject your domain into the container, so set this yourself
# as a project environment variable, e.g.
# ALLOWED_HOSTS=app.example.com,www.example.com
# Defaulting to "*" would turn off Django's Host header validation.
ALLOWED_HOSTS = [h.strip() for h in os.environ.get("ALLOWED_HOSTS", "").split(",") if h.strip()]
# Use the managed PostgreSQL database when one is linked; Temps injects
# these variables. Otherwise your existing DATABASES setting stays in place.
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"),
}
}
# Serve static files from the app with WhiteNoise (add `whitenoise` to your dependencies)
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}
# Keep your existing middleware and add WhiteNoise right after SecurityMiddleware
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
# ...the rest of your existing middleware, unchanged
]
Nightly builds run python manage.py collectstatic --noinput during the build, so STATIC_ROOT must be set as above. On beta.56 and earlier, collect the files when the container starts by adding a Procfile to your repository root, replacing myproject with your project's package name:
Procfile
web: python manage.py collectstatic --noinput && gunicorn myproject.wsgi:application --bind 0.0.0.0:$PORT
A web: line in a Procfile replaces the start command Temps would otherwise generate. Temps then no longer adds Gunicorn for you, so make sure gunicorn and whitenoise are in your requirements.txt or pyproject.toml; otherwise the container exits with gunicorn: not found.
Environment Variables
npx @temps-sdk/cli environments vars set SECRET_KEY "your-secret-key" -e production npx @temps-sdk/cli environments vars set DEBUG "False" -e production npx @temps-sdk/cli environments vars set ALLOWED_HOSTS "yourdomain.com" -e production
When a managed PostgreSQL database is linked, Temps injects POSTGRES_URL, POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB, POSTGRES_USER and POSTGRES_PASSWORD. It doesn't inject DATABASE_URL.
Managed PostgreSQL
Create a PostgreSQL database under Databases → Create database, then link it from your project's Databases page, or use the services create and services link CLI commands. Temps injects the connection details as the POSTGRES_* variables used in Required Settings. Run migrations yourself as described in Run migrations.
Platform behavior
These rules apply to every app deployed on Temps, regardless of framework.
The one requirement: your app must listen on the port in the PORT environment variable and bind to 0.0.0.0 — not localhost or 127.0.0.1. Temps runs your app in a container and routes traffic from the host, so an app bound to localhost only accepts connections from inside the container and will fail its health check.
Health checks
After your container starts, Temps sends HTTP GET requests to verify it is healthy before routing traffic to it.
- Path:
/(the root of your application) by default - Success: 2 consecutive responses with a 2xx, 3xx, 404 or 405 status code
- Timeout: 300 seconds (5 minutes) for the app to become healthy, fixed in v0.1.0-beta.56. If the app answers with error responses (other 4xx, or 5xx) for 60 seconds in a row, the deployment fails sooner. From the next release the timeout is configurable from 30 to 3600 seconds under Project → Settings → Build & deploy
- Retry interval: 2 seconds between successful checks, 5 seconds after an error response or a failed connection
Connection errors and other 4xx or 5xx responses are retried until the timeout; if the app hasn't passed by then, the deployment fails. The check runs once, at deploy time. To check a different path, add a .temps.yaml to your repository root:
health:
path: /healthOnly path is used. Other health fields (status, interval, timeout, retries) are accepted but currently have no effect.
/health endpoint that returns a simple 200. This avoids issues where / requires authentication or returns a redirect.Auto-injected environment variables
Temps injects these variables into every deployment automatically:
| Variable | Value | Description |
|---|---|---|
PORT | Resolved port | The port your app must listen on |
HOST | 0.0.0.0 | Bind address |
SENTRY_DSN | Auto-generated | Error tracking endpoint |
TEMPS_API_URL | Your Temps URL | Platform API endpoint |
TEMPS_API_TOKEN | Deployment token | Authentication for Temps SDKs |
OTEL_EXPORTER_OTLP_ENDPOINT | Your Temps OTLP URL | OpenTelemetry trace collection |
OTEL_SERVICE_NAME | Project name | Service identifier for traces |
You do not need to configure these manually. They are available in process.env (Node.js), os.environ (Python), os.Getenv (Go), and the equivalent in other languages.