> ## Documentation Index
> Fetch the complete documentation index at: https://docs.suga.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy Django with Postgres on Suga

> Deploy a Django app on Suga with a private Postgres database, automatic builds from GitHub, and a public HTTPS URL.

[Django](https://www.djangoproject.com/) apps deploy on Suga straight from a GitHub repo, with Postgres running alongside as a private container. Suga detects the Python install and start commands automatically, so no Dockerfile is needed.

This guide explains how to deploy a Django app with Postgres in two ways:

1. [Step-by-step via the canvas](#deploy-using-the-canvas)
2. [Using a coding agent](#deploy-using-an-agent)

Your repo needs a `manage.py`, your project's WSGI entry at `<project>/wsgi.py`, and a `requirements.txt` (or `pyproject.toml`) listing `django`, `gunicorn`, and a Postgres driver (`psycopg` or `psycopg2`). The guide adds `dj-database-url` so `settings.py` can parse `DATABASE_URL`, and `whitenoise` so the app can serve static files without a separate nginx; both are optional if you already handle those pieces differently.

`settings.py` should read `SECRET_KEY`, `DATABASE_URL`, `ALLOWED_HOSTS`, and `DEBUG` from environment variables so the same code runs locally and on Suga. WhiteNoise middleware belongs right after `SecurityMiddleware`, and `STATIC_ROOT` should be set.

<Note>
  Env vars only reach the running container, not the build, and `collectstatic` imports `settings.py` during the build. Read them with fallbacks so the import still works: `os.environ.get("SECRET_KEY", "insecure-build-key")` and `dj_database_url.config(default="sqlite://:memory:")`.
</Note>

## Deploy using an Agent

To deploy with a coding agent such as [Claude Code](https://claude.com/product/claude-code), [Codex](https://openai.com/codex/), or [OpenCode](https://opencode.ai/), follow these steps:

<Steps>
  <Step title="Connect the Suga MCP">
    If you haven't already, connect your agent to the [Suga MCP server](/agents/mcp).
  </Step>

  <Step title="Ask your agent to deploy">
    Ask your agent to deploy your repo to Suga. Here's a basic prompt you can use, with your repo, branch, and Django project name filled in:

    ```markdown Prompt icon="sparkles" wrap expandable theme={null}
    Deploy this Django app on Suga with a Postgres database, using the Suga MCP.
    Repo: <owner/repo>  Branch: <branch>
    Django project name: <the folder next to manage.py containing wsgi.py>

    Ask which organization to use and create a new project for this deployment. Add two containers:

    - postgres: image `postgres:18-alpine`, private on port 5432. Set `POSTGRES_USER` and `POSTGRES_DB` to something matching the app name. Leave `POSTGRES_PASSWORD` blank and marked sensitive. Add a volume mounted at `/var/lib/postgresql`.
    - the app container: no image, private port 8000, public HTTPS endpoint on 8000. Connect the repo as the build source (auto-detect, no Dockerfile).

    On the app container:
    - Set `DATABASE_URL` to `postgresql://<postgres-user>:{{<postgres-container-id>.variables.POSTGRES_PASSWORD}}@postgres:5432/<postgres-db>` so the app reads the password from postgres without duplication.
    - Set `ALLOWED_HOSTS` as a reference to the app container's own `SUGA_PUBLIC_HOSTNAME`.
    - Set `DEBUG` to `False`.
    - Set `RAILPACK_BUILD_CMD` build arg to `python manage.py collectstatic --noinput` so WhiteNoise's static files are baked into the image.
    - Set `RAILPACK_START_CMD` build arg to `python manage.py migrate && gunicorn <project>.wsgi:application --bind 0.0.0.0:8000` so migrations run before the server starts.
    ```

    The agent will give you a link to the new environment on the Suga canvas. Open it to review the setup.
  </Step>

  <Step title="Set the sensitive values">
    Set `POSTGRES_PASSWORD` on the postgres container: click the value input and paste a value generated with `openssl rand -hex 32`. On the app container, add `SECRET_KEY` as a **Sensitive** env var (generate one with `python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"`).

    <Note>
      Save both values somewhere secure, you'll want them later.
    </Note>
  </Step>

  <Step title="Apply to deploy">
    Click **Apply** in the top right. Suga clones the app repo, installs dependencies, runs migrations, starts Gunicorn on port 8000, and rolls out both containers. The Django app is served at the public URL and reaches Postgres privately at `postgres:5432`. Open `/admin` and you'll get the Django admin login with its stylesheets in place, which confirms both the deploy and the static files.
  </Step>
</Steps>

## FAQ

<AccordionGroup>
  <Accordion title="Do I need a Dockerfile to deploy Django on Suga?">
    No. Suga detects Python from `requirements.txt` or `pyproject.toml` and runs the standard install and start flow. If you already have a Dockerfile, you can tell the agent to use it instead; otherwise auto-detect handles the common case.
  </Accordion>

  <Accordion title="Which Python versions does Suga support?">
    Python 3.10 and up, defaulting to 3.13. Pin a different one in `.python-version`, `runtime.txt`, `.tool-versions`, or `mise.toml`.
  </Accordion>

  <Accordion title="What if my app needs a different start command?">
    Pass `RAILPACK_START_CMD` as a build arg on the app container with your exact command. Common alternatives: `gunicorn <project>.wsgi:application --bind 0.0.0.0:8000` for a custom project name, `daphne -b 0.0.0.0 -p 8000 <project>.asgi:application` for ASGI or Channels, `uwsgi --http :8000 --module <project>.wsgi:application` for uWSGI, or `hypercorn <project>.asgi:application --bind 0.0.0.0:8000` for Hypercorn. Suga only cares that something listens on the container's private port.
  </Accordion>

  <Accordion title="What if my `settings.py` doesn't use `dj-database-url`?">
    Two options. If your project uses `django-environ`, it parses `DATABASE_URL` the same way, so keep the guide's env var and drop `dj-database-url` from `requirements.txt`. If your `settings.py` reads individual `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, and `DB_NAME` variables instead, skip `DATABASE_URL` on the app container and set those variables directly, referencing the postgres container's `POSTGRES_PASSWORD` for the password. The reference works for any variable name.
  </Accordion>

  <Accordion title="How do I run migrations before the app starts?">
    Wrap the start command with the migration: pass `RAILPACK_START_CMD=python manage.py migrate && gunicorn <project>.wsgi:application --bind 0.0.0.0:8000` as a build arg. Migrations run each time a new container boots and no-op if there's nothing to apply.
  </Accordion>

  <Accordion title="Can I skip auto-migrations at boot?">
    Yes. Drop the `python manage.py migrate && ` prefix from `RAILPACK_START_CMD` and run migrations manually — from the container's shell in the dashboard, or from your CI pipeline before you Apply. Running migrations at boot is convenient, but teams with strict migration workflows often prefer to trigger them explicitly.
  </Accordion>

  <Accordion title="How do I serve static files?">
    Add `RAILPACK_BUILD_CMD=python manage.py collectstatic --noinput` as a build arg on the app container. In `settings.py`, add [WhiteNoise](https://whitenoise.readthedocs.io/) middleware to `MIDDLEWARE` (right after `SecurityMiddleware`) and set `STATIC_ROOT`. WhiteNoise then serves the baked static files without a separate nginx.
  </Accordion>

  <Accordion title="What if I serve static files from S3 or a CDN?">
    Skip WhiteNoise entirely. Point `STATICFILES_STORAGE` at `django-storages`' S3 backend (or your CDN's storage backend) in `settings.py`, and add AWS credentials as **Sensitive** env vars on the app container. Those only reach the running container, so `collectstatic` can't upload during the build: drop `RAILPACK_BUILD_CMD` and run `collectstatic` from CI before you Apply.
  </Accordion>

  <Accordion title="Do I need `collectstatic` if my app has no custom static assets?">
    Yes if you use Django's admin, which ships with its own CSS and JS. WhiteNoise's `CompressedManifestStaticFilesStorage` refuses to serve any static asset unless the manifest was built. If your project is a pure JSON API with the admin disabled, drop `RAILPACK_BUILD_CMD` from the build args and remove WhiteNoise from `MIDDLEWARE`.
  </Accordion>

  <Accordion title="Why does my build fail on `collectstatic` with a missing `SECRET_KEY` or `DATABASE_URL`?">
    Env vars only reach the running container, so `settings.py` can't read them during the build. Give every setting read at import time a fallback, for example `os.environ.get("SECRET_KEY", "insecure-build-key")` instead of `os.environ["SECRET_KEY"]`. The real values arrive as env vars at boot.
  </Accordion>

  <Accordion title="Can I use a database other than Postgres?">
    Yes. Swap the `postgres:18-alpine` container for a MySQL, MariaDB, or MongoDB image (or any container that runs the database you want). Update Django's `DATABASES` setting and the driver in your `requirements.txt` to match.
  </Accordion>

  <Accordion title="How do I run Celery or a background worker?">
    Add a second app container on the canvas with the same repo but a different start command, for example `RAILPACK_START_CMD=celery -A <project> worker --loglevel=info`. Set the same `DATABASE_URL` and any broker env vars, and leave it with no public endpoint. Add a Redis container from the template if Celery needs a broker.
  </Accordion>

  <Accordion title="What about Postgres backups?">
    The managed volume persists across restarts, redeploys, and rollbacks. For point-in-time recovery or off-site backups, run `pg_dump` on a schedule from another container, or point the app at a managed Postgres service instead of a container.
  </Accordion>

  <Accordion title="Can I use a custom domain?">
    Yes. Attach your domain to the app container in the dashboard once the app is deployed. Add the new hostname to your Django `ALLOWED_HOSTS` too.
  </Accordion>

  <Accordion title="What does deploying Django plus Postgres cost on Suga?">
    The Free tier fits a small Django plus Postgres stack, including the build history and volume. Pro is per-seat with hosting credits that offset compute and storage. Full pricing at [suga.app/pricing](https://suga.app/pricing).
  </Accordion>
</AccordionGroup>
