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

# Dockerize a Django App with Postgres and Redis

export const UseCaseHero = ({title, description, prompt, category, features, devinUrl, agent, intent, playbookId, type}) => {
  const encodedPrompt = encodeURIComponent(prompt || '');
  const tag = 'docs-use-case-gallery';
  const utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=hero-cta';
  const agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  const devinHref = type === 'schedule' ? 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + (prompt ? '&prompt=' + encodedPrompt : '') : type === 'review' ? 'https://app.devin.ai/review?' + utm : agent === 'ada' ? 'https://app.devin.ai/search?' + utm + '&noSubmit=true' + (prompt ? '&prompt=' + encodedPrompt : '') : devinUrl ? devinUrl.includes('?') ? devinUrl + '&' + utm + agentParams : devinUrl + '?' + utm + agentParams : prompt ? 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encodedPrompt : 'https://app.devin.ai/?' + utm + agentParams;
  const buttonLabel = type === 'schedule' ? 'Schedule in Devin ↗' : type === 'review' ? 'Set Up Devin Review ↗' : agent === 'advanced' ? 'Try in Devin ↗' : agent === 'dana' ? 'Try in Dana ↗' : agent === 'ada' ? 'Try in Ask Devin ↗' : 'Try in Devin ↗';
  const featureList = features ? features.split(',').map(f => f.trim()) : [];
  return <div className="uc-hero">
      <div className="uc-hero-inner">
        <div className="uc-hero-left">
          <h1 className="uc-hero-title">{title}</h1>
          <p className="uc-hero-desc">{description}</p>
          <div>
            <a href={devinHref} target="_blank" rel="noopener noreferrer" className="try-in-devin-btn">
              {buttonLabel}
            </a>
          </div>
        </div>
        <div className="uc-hero-meta">
          <div className="uc-meta-item">
            <span className="uc-meta-label">Author</span>
            <span className="uc-meta-value">Cognition</span>
          </div>
          <div className="uc-meta-item">
            <span className="uc-meta-label">Category</span>
            <span className="uc-meta-value">{category}</span>
          </div>
          {featureList.length > 0 && <div className="uc-meta-item">
              <span className="uc-meta-label">Features</span>
              <span className="uc-meta-value">{featureList.join(', ')}</span>
            </div>}
        </div>
      </div>
    </div>;
};

export const PromptBlock = ({children, type, agent, intent, playbookId}) => {
  var utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=prompt-block';
  var tag = 'docs-use-case-gallery';
  var agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  var label = type === 'schedule' ? 'Schedule in Devin' : type === 'playbook' ? 'Create Playbook' : type === 'knowledge' ? 'Add to Knowledge' : agent === 'advanced' ? 'Try in Devin' : agent === 'dana' ? 'Try in Dana' : agent === 'ada' ? 'Try in Ask Devin' : 'Try in Devin';
  var buildUrl = function (text) {
    var encoded = encodeURIComponent(text);
    if (type === 'schedule') return 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + '&prompt=' + encoded;
    if (type === 'playbook') return 'https://app.devin.ai/settings/playbooks/create?' + utm + '&body=' + encoded;
    if (type === 'knowledge') return 'https://app.devin.ai/knowledge?' + utm + '&body=' + encoded;
    if (agent === 'ada') return 'https://app.devin.ai/search?' + utm + '&noSubmit=true&prompt=' + encoded;
    return 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encoded;
  };
  const ref = React.useRef(null);
  const [href, setHref] = React.useState('#');
  React.useEffect(() => {
    if (!ref.current) return;
    var codeEl = ref.current.querySelector('pre code');
    if (codeEl) {
      var text = codeEl.textContent.trim();
      if (text) setHref(buildUrl(text));
    }
    var header = ref.current.querySelector('[data-component-part="code-block-header"]');
    if (header && !header.querySelector('.prompt-block-devin-link')) {
      var link = document.createElement('a');
      link.href = href;
      link.target = '_blank';
      link.rel = 'noopener noreferrer';
      link.className = 'prompt-block-devin-link';
      link.style.cssText = 'display:inline-flex;align-items:center;gap:6px;text-decoration:none;color:#fff;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;background:#317CFF;transition:background 0.2s;margin-left:8px;';
      link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> ' + label;
      link.onmouseenter = function () {
        link.style.background = '#2968D9';
      };
      link.onmouseleave = function () {
        link.style.background = '#317CFF';
      };
      header.appendChild(link);
    }
    var existingLink = ref.current.querySelector('.prompt-block-devin-link');
    if (existingLink && href !== '#') existingLink.href = href;
  });
  return <div className="prompt-block" ref={ref}>{children}</div>;
};

<UseCaseHero title="Dockerize a Django App with Postgres and Redis" description="Devin writes a multi-stage Dockerfile, a docker-compose.yml with Django, PostgreSQL, and Redis, then builds and runs the stack to verify it works." prompt="Dockerize the Django API in src/. It uses PostgreSQL for the database and Redis for caching and Celery task queues. Multi-stage Dockerfile with python:3.12-slim, docker-compose.yml with Django (gunicorn), PostgreSQL 16, Redis 7, and a Celery worker. Run as non-root user, health check on /api/health/. Build it, run docker-compose up, verify the API responds, run the test suite inside the container. If anything fails, debug and fix it — keep iterating until everything works. Do not open a PR until the entire stack starts cleanly and all tests pass." category="Feature Development" features="" />

<div className="uc-detail-wrapper">
  <Tip>Don't want to set this up manually? Paste a link to this page into a Devin session and ask it to set everything up for you.</Tip>

  <Steps>
    <Step title="(Optional) Scope the project with Ask Devin">
      If you're not sure which services your Django app depends on or how the project is structured, use [Ask Devin](https://app.devin.ai/search?utm_source=docs\&utm_medium=use-case-gallery) to investigate first:

      <PromptBlock agent="ada">
        ```txt Scope the Django project for containerization theme={null}
        What does our Django app in src/ depend on?
        1. Which databases and caches does it connect to?
        2. Does it use Celery or any background workers?
        3. What's in requirements.txt or pyproject.toml?
        4. Are there any existing Docker files or deployment configs?
        5. How does the app handle static files and media?
        ```
      </PromptBlock>

      You can also start a Devin session directly from Ask Devin, and it will carry over everything it learned as context.
    </Step>

    <Step title="Give Devin your Django project and requirements">
      Point Devin at the Django project to containerize and mention anything specific — base image preferences, services the app depends on, or image size constraints. Devin reads your `requirements.txt` or `pyproject.toml` to figure out the rest.

      <PromptBlock>
        ```txt Dockerize Django API with Postgres and Redis theme={null}
        Dockerize the Django API in src/. It uses PostgreSQL for the database
        and Redis for caching and Celery task queues.

        - Multi-stage Dockerfile: install dependencies in a builder stage,
          copy only the venv into a slim python:3.12-slim runtime image
        - docker-compose.yml with Django (gunicorn), PostgreSQL 16, and Redis 7
        - .dockerignore that excludes tests, docs, .git, and __pycache__
        - Run as non-root user
        - Health check on /api/health/
        - Celery worker as a separate service in compose

        ## Testing & verification
        - Build the image and run docker-compose up
        - Wait for all services to be healthy
        - Verify the API responds on localhost:8000/api/health/
        - Run the Django test suite inside the container
        - If anything fails — build errors, missing env vars, connection
          issues, failing tests — debug and fix it. Keep iterating until
          the entire stack starts cleanly and all tests pass.
        - Do not open a PR until everything works end-to-end
        ```
      </PromptBlock>
    </Step>

    <Step title="Devin investigates and builds">
      Devin reads your Django project and dependency files to understand the build process, then creates the Docker configuration:

      1. **Reads dependency files** — Parses `requirements.txt` or `pyproject.toml`, identifies Django, psycopg2, redis, celery, and gunicorn
      2. **Writes the Dockerfile** — Creates a multi-stage build that installs dependencies in a builder stage and copies the virtual environment into a minimal `python:3.12-slim` runtime image

      ```dockerfile theme={null}
      # ---- Builder ----
      FROM python:3.12-slim AS builder
      WORKDIR /app
      COPY requirements.txt .
      RUN python -m venv /opt/venv \
          && /opt/venv/bin/pip install --no-cache-dir -r requirements.txt

      # ---- Runtime ----
      FROM python:3.12-slim
      RUN groupadd -r django && useradd -r -g django django
      COPY --from=builder /opt/venv /opt/venv
      ENV PATH="/opt/venv/bin:$PATH"
      WORKDIR /app
      COPY src/ .
      RUN python manage.py collectstatic --noinput
      EXPOSE 8000
      USER django
      CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3"]
      ```

      3. **Writes docker-compose.yml** — Adds Django, PostgreSQL, Redis, and a Celery worker with health checks, volumes, and a shared network

      ```yaml theme={null}
      services:
        web:
          build: .
          ports:
            - "8000:8000"
          env_file: .env
          depends_on:
            db:
              condition: service_healthy
            redis:
              condition: service_healthy
          healthcheck:
            test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health/')"]
            interval: 10s
            retries: 3

        db:
          image: postgres:16-alpine
          environment:
            POSTGRES_DB: app_db
            POSTGRES_USER: app_user
            POSTGRES_PASSWORD: app_pass
          volumes:
            - pgdata:/var/lib/postgresql/data
          healthcheck:
            test: ["CMD-SHELL", "pg_isready -U app_user -d app_db"]
            interval: 5s
            retries: 5

        redis:
          image: redis:7-alpine
          healthcheck:
            test: ["CMD", "redis-cli", "ping"]
            interval: 5s
            retries: 5

        celery:
          build: .
          command: celery -A config worker -l info
          env_file: .env
          depends_on:
            db:
              condition: service_healthy
            redis:
              condition: service_healthy

      volumes:
        pgdata:
      ```

      4. **Adds .dockerignore** — Excludes files that don't belong in the build context (`__pycache__`, `.git`, `tests/`, `docs/`, `*.pyc`)
      5. **Runs `docker compose up --build`** — Builds the image and starts all services in Devin's terminal
      6. **Verifies the app** — Curls `/api/health/` to confirm Django starts cleanly, connects to PostgreSQL, and reaches Redis
    </Step>

    <Step title="Guide the session with slash commands">
      Use slash commands during the session to steer Devin's workflow:

      * **`/plan`** — Ask Devin to outline its approach before writing any Docker configuration. Review the plan and suggest changes.
      * **`/test`** — Tell Devin to rebuild and re-verify the container stack. Use this after each change to catch issues early.
      * **`/review`** — Ask Devin to review its own Dockerfile and compose config for security issues, image size, and best practices before opening the PR.
    </Step>

    <Step title="Verify and iterate">
      Once Devin opens the PR, review the generated files. Common follow-ups:

      <PromptBlock>
        ```txt Optimize image size theme={null}
        The Docker image is 520MB. Shrink it — make sure we're not copying
        unnecessary files, strip .pyc files from the venv, and remove pip
        from the final image.
        ```
      </PromptBlock>

      <PromptBlock>
        ```txt Add dev hot-reload theme={null}
        Add a docker-compose.override.yml for local development that mounts
        the source directory and runs Django's dev server with auto-reload
        instead of gunicorn.
        ```
      </PromptBlock>

      <PromptBlock>
        ```txt Add CI docker build theme={null}
        Create a GitHub Actions workflow that builds the Docker image on
        every PR, runs pytest inside the container, and pushes to ECR on
        merge to main. Tag with the git SHA.
        ```
      </PromptBlock>
    </Step>

    <Step title="Review the PR with Devin Review">
      Once Devin opens the PR, use [Devin Review](https://app.devin.ai/review?utm_source=docs\&utm_medium=use-case-gallery) to review the Docker configuration. Devin Review can catch security issues (running as root, exposed secrets), missing best practices (no `.dockerignore`, no health checks), and inconsistencies with your existing infrastructure.
    </Step>
  </Steps>
</div>
