Django has always been straightforward to deploy and tedious to deploy well: gunicorn, static files, a database, migrations, a background worker, a reverse proxy and a certificate, each a small decision. Dokploy removes the proxy and certificate decisions (Traefik and Let's Encrypt are built in), gives you PostgreSQL and Redis as services, and builds from your repository on push. This guide uses a Dockerfile with gunicorn and WhiteNoise, a Celery worker as a second application, and the Starter preset from the MassiveGRID Dokploy hosting page as the server.

The Dockerfile

Dokploy's Railpack build type supports Python and will run a Django app with no Dockerfile, which is fine for a first deploy. For production the Dockerfile below pins the Python version, installs only binary wheels where possible (keeping the build fast and memory-light on a small server), collects static files at build time and serves them with WhiteNoise so no separate web server is needed:

FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PORT=8000
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends libpq5 && rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir --only-binary=:all: -r requirements.txt || pip install --no-cache-dir -r requirements.txt
COPY . .
RUN SECRET_KEY=build DATABASE_URL=sqlite:///tmp/build.db python manage.py collectstatic --noinput
EXPOSE 8000
CMD ["sh", "-c", "python manage.py migrate --noinput && gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 3 --timeout 60 --access-logfile - --error-logfile -"]

Replace config.wsgi with your project module. Binding to 0.0.0.0 matters: a process on 127.0.0.1 is unreachable from Traefik and produces the Bad Gateway described in our domain troubleshooting post. Three gunicorn workers suit two cores; use 2 × cores + 1 as the rule.

Settings for life behind Traefik

import os, dj_database_url
SECRET_KEY = os.environ["SECRET_KEY"]
DEBUG = False
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",")
CSRF_TRUSTED_ORIGINS = [f"https://{h}" for h in ALLOWED_HOSTS if h]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
USE_X_FORWARDED_HOST = True
DATABASES = {"default": dj_database_url.config(conn_max_age=600)}
CACHES = {"default": {"BACKEND": "django.core.cache.backends.redis.RedisCache", "LOCATION": os.environ.get("REDIS_URL", "redis://localhost:6379/1")}}
MIDDLEWARE.insert(1, "whitenoise.middleware.WhiteNoiseMiddleware")
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
            "staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"}}
LOGGING = {"version": 1, "handlers": {"console": {"class": "logging.StreamHandler"}}, "root": {"handlers": ["console"], "level": "INFO"}}

Traefik terminates TLS and forwards plain HTTP with X-Forwarded-Proto; SECURE_PROXY_SSL_HEADER tells Django the request was secure so redirects, cookies and request.is_secure() behave. CSRF_TRUSTED_ORIGINS is required in Django 4+ for any POST from your domain.

Step 1: PostgreSQL and Redis in Dokploy

In a new project, Create Service, Database, PostgreSQL (16), then Redis. Dokploy generates the internal hostnames; build DATABASE_URL as postgres://user:password@<postgres host>:5432/dbname and REDIS_URL as redis://:<password>@<redis host>:6379/0. On the PostgreSQL service, schedule a daily backup to an S3 destination; a 500 MB database costs a few gigabytes a month of bucket space at 14-day retention.

Step 2: the web application

Create Service, Application from your repository with the Dockerfile build type. Environment: SECRET_KEY, ALLOWED_HOSTS=app.example.com, DATABASE_URL, REDIS_URL, plus mail and third-party keys. Add the domain with HTTPS, letsencrypt and port 8000. Deploy; the first build pulls the base image and installs dependencies (one to three minutes on two cores), later builds reuse the cached layers when only code changed. Dokploy's Advanced, Ports setting stays off; Traefik is the only public entry.

Step 3: Celery worker and beat

Create a second Application from the same repository, same Dockerfile and environment, no domain, with the start command overridden to:

celery -A config worker --loglevel=info --concurrency=2

For periodic tasks either run a third application with celery -A config beat or, simpler, use Dokploy's Schedule Jobs to call a management command on a cron expression. Set CELERY_BROKER_URL to the Redis URL. Each worker process takes 100 to 300 MB; two is plenty on the Starter preset.

Step 4: media files, admin and the first superuser

Costs and scaling

Django, gunicorn with three workers, Celery, PostgreSQL and Redis fit in the Starter preset (2 vCPU, 4 GB, 64 GB NVMe) at $9.58 a month on shared cores; move to dedicated cores ($19.80) when builds or request latency need to be steady, and to Growth (4 vCPU, 8 GB, $39.60 dedicated) when PostgreSQL wants more cache or you add a second project. Compare that with per-dyno pricing in our PaaS cost comparison. When one server is not enough, Dokploy adds a build server or Swarm workers through a registry.

Two habits keep a Django deployment on Dokploy boring: set DEBUG=False and a real SECRET_KEY before the first deploy, and take a database backup before every release that includes a migration. With those in place, a push to the branch is a complete release: build, migrate, health check, switch, with the previous deployment one click away in the Deployments tab if something slips through.

Frequently Asked Questions

Do I need Nginx to deploy Django on Dokploy?

No. Traefik, which Dokploy installs, is the reverse proxy and TLS terminator, and WhiteNoise serves static files from gunicorn efficiently. Add Nginx only if you serve very large media files from the server itself, and even then an S3-compatible bucket is usually the better answer.

How do I run Django migrations on Dokploy?

At container start, as the Dockerfile in this guide does with python manage.py migrate --noinput before gunicorn, or from the service’s Terminal tab in the Dokploy UI for one-off runs. Keep a scheduled PostgreSQL backup so a failed migration is recoverable.

Can Dokploy build a Django app without a Dockerfile?

Yes. The Railpack build type supports Python: it installs requirements and you set the start command to gunicorn config.wsgi:application --bind 0.0.0.0:8000. You still need to run collectstatic and migrations, which you can put in the start command. The Dockerfile route gives you pinned versions and faster rebuilds.

Dokploy hosting from $9.58 a month

A MassiveGRID VPS preset for Dokploy: 2 vCPU, 4 GB RAM and 64 GB NVMe for $9.58 a month on shared cores or $19.80 on dedicated cores, in New York, London, Frankfurt or Singapore, on a Proxmox HA cluster with Ceph storage, a 100% uptime SLA and a 15-day money-back guarantee. Run the one-line installer yourself or open a ticket and we install Dokploy for you.

Configure a Dokploy server

Further Reading