Deploy Django with Coolify

Deploy Django on your own server with Coolify: Dockerfile builds from Git, managed PostgreSQL and Redis, automatic HTTPS, environment variables, migrations, and backups.

Problem statement

Coolify is an open-source, self-hosted platform that gives a VPS a Heroku-like workflow: connect a Git repository, and it builds images, runs containers behind a reverse proxy, issues certificates, and manages databases from a web dashboard. For Django developers who want push-to-deploy on their own server, it removes a lot of manual Nginx and systemd work.

It does not remove Django's production requirements. The app still needs a production server process, correct host and proxy settings, a plan for migrations, persistent storage for uploads, and backups. This guide sets those up in Coolify.

Django on CoolifyGit pushwebhook triggerCoolify buildDockerfile imageProxydomains and TLSContainersweb, worker, DBCoolify runs the containers; Django still needs correct proxy, host, and storage settings.
  1. Install Coolify on a fresh server with at least 2 GB of RAM and point a domain at it.
  2. Add a Dockerfile to the project that runs Gunicorn on port 8000 as a non-root user.
  3. Create PostgreSQL (and Redis if needed) resources in the same Coolify project.
  4. Create an application from your Git repository using the Dockerfile build pack, exposing port 8000.
  5. Set environment variables: secret key, database URL, allowed hosts, trusted origins.
  6. Set the domain with an https:// prefix so the proxy obtains a certificate.
  7. Run migrations as part of each deploy, add a persistent volume for media, and schedule database backups.

Step-by-step solution

1. Install Coolify

On a fresh Ubuntu or Debian server, as root:

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash

Open http://<server-ip>:8000, create the admin account immediately (the first visitor becomes admin), then set the instance domain and enable HTTPS for the dashboard. Restrict the dashboard port in your firewall once it is served through its own domain.

Coolify can also manage additional servers over SSH, so the dashboard server and the application server can be separate machines.

2. Add a production Dockerfile

FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN DJANGO_SETTINGS_MODULE=config.settings.build python manage.py collectstatic --noinput
RUN useradd --create-home app && mkdir -p /app/media && chown app:app /app/media
USER app
EXPOSE 8000
COPY --chmod=755 docker/start.sh /start.sh
CMD ["/start.sh"]
#!/bin/sh
# docker/start.sh
set -e
python manage.py migrate --noinput
exec gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 3 --access-logfile -

Running migrate in the start script is acceptable for a single web container. If you scale to several replicas, move migrations to a one-off step so they do not run concurrently. curl is installed so the container health check can call the app.

Coolify's Nixpacks build pack can build Django without a Dockerfile, but an explicit Dockerfile keeps builds reproducible and portable to any other container host.

3. Create the database and Redis

In the project, add a PostgreSQL resource and, if you use Celery or caching, a Redis resource. Coolify generates credentials and shows an internal connection URL that resolves on the Docker network shared by resources in the same environment. Use that internal URL from Django; do not make the database publicly accessible unless you need external access for a specific reason.

one project, several resourcesTraefik proxydomains and certificatesDjango applicationbuilt from your DockerfileWorker applicationsame image, Celery commandPostgreSQLinternal hostname, scheduledbackupsRediscache and brokerResources in one project talk over an internal Docker networkdjango-deployment.com
Use the internal connection URLs; the databases should not be published publicly.

4. Create the application

Add a new resource from a Git repository. For private repositories, connect a GitHub App or add a deploy key. Then configure:

  • Build pack: Dockerfile.
  • Ports Exposes: 8000 (the port Gunicorn binds inside the container).
  • Domains: https://example.com,https://www.example.com. The https:// prefix tells the proxy to obtain a Let's Encrypt certificate.
  • Health check: path /healthz/, port 8000. With a health check, Coolify waits for the new container to be healthy before switching traffic.

Create an A record for the domain pointing at the server before the first deploy, or certificate issuance fails.

5. Set environment variables

In the application's environment variables, add:

DJANGO_SETTINGS_MODULE=config.settings.production
DJANGO_SECRET_KEY=long-random-value
DATABASE_URL=postgres://postgres:generated@<postgres-internal-host>:5432/postgres
REDIS_URL=redis://default:generated@<redis-internal-host>:6379/0
DJANGO_ALLOWED_HOSTS=example.com,www.example.com
DJANGO_CSRF_TRUSTED_ORIGINS=https://example.com,https://www.example.com

Variables are available at runtime by default. Only mark a variable as a build variable if the Docker build needs it; secrets baked into image layers can be read by anyone with the image.

6. Configure Django for the proxy

Coolify's proxy (Traefik by default) terminates TLS and forwards requests over HTTP:

SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
ALLOWED_HOSTS = os.environ["DJANGO_ALLOWED_HOSTS"].split(",")
CSRF_TRUSTED_ORIGINS = os.environ["DJANGO_CSRF_TRUSTED_ORIGINS"].split(",")

Container health checks call the app directly over HTTP. Exempt the health path from the HTTPS redirect with SECURE_REDIRECT_EXEMPT = [r"^healthz/$"], and make sure the host the check uses is allowed.

7. Persist media and add workers

Container filesystems are replaced on every deploy. In the application's Storages settings, add a persistent volume mounted at /app/media, or use object storage with django-storages, which also works when you add a second server.

uploads must outlive the containerContainer filesystem✕replaced on every deploy✕uploads vanish silently✕workers cannot see themPersistent storage✓volume mounted at /app/media✓survives redeploysincluded in server backupsOr skip local media entirely and use S3-compatible storagedjango-deployment.com
Object storage also makes it possible to run more than one app container later.

For Celery, create a second application from the same repository with a custom start command such as celery -A config worker --loglevel=info, no domain, and the same environment variables.

8. Deploy, verify, and back up

Enable automatic deployment on push (webhook) or deploy from the dashboard. Watch the build and runtime logs in the deployment view.

after the first deploySite loads on the domain over HTTPScertificate issuedAdmin login worksCSRF and proxy header rightAn upload survives a redeploymedia volume mountedDatabase backup scheduledto S3, not only localCoolify itself backed upits own database and keysLosing the Coolify server loses its configuration toodjango-deployment.com
Test the redeploy check deliberately; it is the one people discover in production.

Verification check:

curl -sI https://example.com/ | head -1
curl -s https://example.com/healthz/
  • The admin login works over HTTPS.
  • An uploaded file is still present after a redeploy.

Configure scheduled backups on the PostgreSQL resource and send them to S3-compatible storage, so a server loss does not take the backups with it.

Explanation

Coolify combines a build system, a container runtime, a reverse proxy, and a certificate manager behind one dashboard. Each deploy builds a new image from your repository and replaces the running container, which is why anything written to the container filesystem disappears and why migrations must be part of the deploy. The proxy is what makes HTTPS automatic, and it is also why Django must be told to trust X-Forwarded-Proto.

Edge cases / notes

Rollback

Coolify keeps previous deployments and can redeploy an earlier image from the deployment history. Rolling back code does not undo migrations, so keep them backward compatible.

Resource limits

Builds happen on the server by default and can exhaust memory on small machines while the app is running. Add swap, give the server more memory, or build images in CI and deploy a prebuilt image from a registry.

Coolify updates

Coolify updates itself from the dashboard. Treat it as infrastructure: read release notes and take a server snapshot before major version updates.

When to automate this

Most of the workflow is already automated by Coolify. What remains worth scripting is the Django side: a consistent start script, a health endpoint, and a smoke test after each deploy. Teams that outgrow a single server often keep the same Dockerfile and move to a registry-based CI pipeline.

Next steps

FAQ

Does Coolify need a Dockerfile for Django?

No. Nixpacks can detect Python projects. A Dockerfile is recommended for production because it makes the build explicit and reproducible.

Why does my Coolify Django app show a Bad Gateway error?

The exposed port does not match the port Gunicorn listens on, Gunicorn binds to 127.0.0.1 instead of 0.0.0.0, or the container crashed. Check the port setting and the runtime logs.

Where should Django migrations run on Coolify?

For a single container, in the start script before Gunicorn. For multiple replicas, run them once per deploy as a separate command so they never run concurrently.

Are uploads kept between Coolify deploys?

Only if you mount a persistent volume at the media path or store uploads in object storage.

Keep going