Skip to content

Backend Deployment - Verify Dev Deployment

This page walks you through manual verification of the Django + Docker dev stack. Works on macOS/Linux with Docker Desktop (or Docker Engine).


0) Prerequisites

  • Docker running.
  • Ports free: 80, 443, 2019, 3000, 4317, 4318, 5050, 8000, 8001, 8025, 8889, 9090, 3100, 3200.
  • macOS only (for local TLS): you’ll trust Caddy’s local CA in step 3.

1) Project bootstrap (env + secrets)

You can start without changing any values: the defaults in env.dev.example work as-is for local development.

# Copy defaults into .env (you can keep them as-is to get started)
make env
# (Optional) Helpers if/when you want to generate strong values:
make dj-secret
make token-32
make token-64
make caddy-hash-password

Required variables for dev are already present in env.dev.example with working defaults (DB, Django, health, Grafana, docs, domains).


2) Build & start the dev stack

# Build all dev images
make build

# Start everything in the background
make up-detach

Docker lifecycle controls (handy now):

# List running containers (dev only)
make ps

# Follow logs
make logs

# Restart just Django
make dj-restart

# Stop dev environment
make down

# Clean volumes (DANGEROUS: deletes DB etc.)
make clean

# Remove orphan containers
make prune-orphans

3) (macOS) Trust local TLS

make caddy-mac-trust

3.1 Remove old Caddy certificates (optional, cleanup)

If you’ve trusted previous local Caddy CAs and want to clean them up:

  1. Open Keychain Access (Spotlight → “Keychain Access”).
  2. In the sidebar, select System keychain.
  3. In the Category list, select Certificates.
  4. Search for “Caddy Local Authority” (there may be multiple, including ECC Intermediate or older dates).
  5. Right-click each outdated Caddy Local Authority certificate → Delete.
  6. If prompted, enter your macOS admin password to confirm.
  7. (If you removed the current one by mistake) re-run:
make caddy-mac-trust

4) Verify containers (health & notes)

make ps

Example output (dev):

mow-backend-caddy-1               caddy:2.10.2-alpine   ...  Up 2 hours (unhealthy)  80/tcp 443/tcp 2019/tcp
mow-backend-django-1              mow-backend-django    ...  Up 2 hours (healthy)    8000/tcp
mow-backend-grafana-1             grafana/grafana:12.1.0 ... Up 2 hours             3000/tcp
...

Why Caddy shows “unhealthy” even though it works: The container’s healthcheck uses a command that isn’t available in the base image, so the healthcheck fails while Caddy still serves traffic normally. It’s safe to proceed.


5) Quick health checks (Django)

# Internal (container → Django) — expected 200
make dj-test-health-internal

# External via Caddy with token — expected 200
make dj-test-health-external

# External w/o token — expected 403
make dj-test-health-denied

6) Database migrations & static

Dev entrypoint already runs migrations and collectstatic, but you can reconfirm:

# Migrate (safe to re-run)
make dj-migrate

7) Verify Postgres is up

Use the Makefile helper:

make pg-conninfo

Example:

You are connected to database "ok_db" as user "change-me-in-production" via socket in "/var/run/postgresql" at port "5432".

8) Open everything at once (macOS)

Fastest sanity sweep:

make open-all

Then manually check each site:


9) Create superusers & log in (roles)

  1. Admin portal access

  2. Create a superuser:

make dj-createsuperuser
  • Go to https://admin.localhost/dj-admin, log in as that user.
  • In the Django admin UI, edit that user and change ROLE to ADMIN. Now they can access https://admin.localhost (admin portal). (If you logged into dj-admin first, you may already be authenticated on admin.localhost.)

  • Portal access

  • Create a second superuser (defaults to VOLUNTEER, which is correct for the portal).

  • Use that account to test https://portal.localhost.

10) pgAdmin (DB UI)

  • Open: http://localhost:5050
  • Log in with .env values: PGADMIN_DEFAULT_EMAIL / PGADMIN_DEFAULT_PASSWORD.
  • Add a Server:

  • Host: postgres

  • Port: 5432
  • Username/Password: from your .env
  • You should see the application DB and tables created by migrations.

11) Mailpit (email catcher)

  • Open: http://localhost:8025
  • Any emails sent by the app show up here (e.g., password reset). Nothing is actually sent externally in dev.

12) Developer docs & API docs

# Generate OpenAPI files into ./api/
make dj-openapi

# Trigger a docs build
make mk-build

13) Observability

Prometheus

Grafana

  • Go to https://grafana.localhost (or http://localhost:3000).
  • Log in with .env GRAFANA_USER / GRAFANA_PASSWORD.
  • Connections → Data Sources: Prometheus, Loki, Tempo, PostgreSQL are preconfigured and working.
  • Dashboards: Find the Starter folder and open a dashboard to see data.
  • Explore (Loki): run
{service_name="mow-backend-django-1"}

You should see JSON-formatted logs from the Django container. * Explore (Tempo): run

{resource.service.name = "mow-backend-django-1"}

You should see traces from Django (OTLP gRPC → otelcol:4317 → Tempo).


14) Portal hostname routing

The host router maps:

  • admin.localhost → admin portal URLs
  • portal.localhost → volunteer portal URLs

You can enumerate URLs via:

make dj-ext-show-urls
make dj-ext-show-host-urls

15) Table listing (psql)

Check tables exist using the Makefile helper:

make pg-tables

16) Django shell (quick sanity)

make dj-shell

Run inside the shell:

from mow.apps.accounts.models import User
print(f"Total users: {User.objects.count()}")

from django.conf import settings
print(f"Debug mode: {settings.DEBUG}")

exit()

Expected:

  • Shell opens without errors.
  • Commands run successfully.
  • User count ≥ 1 if you created a superuser.

16.1 Send a test email (Mailpit)

make dj-ext-test-email

Manual Steps:

Expected Result:

  • Email appears in the Mailpit inbox
  • You can view the email content

17) Tests, lint, coverage (optional but handy)

# Lint
make dj-lint

# Safe autofix
make dj-lint-fix

# Format
make dj-format

# Unit tests
make dj-test

# Coverage (console)
make dj-coverage

# Coverage HTML → ./django/src/htmlcov/index.html
make dj-coverage-html

18) Troubleshooting notes

  • Caddy shows “unhealthy” but serves fine: expected with the current healthcheck vs. base image tools; OK to proceed.
  • Health endpoints: token required externally; denied (403) without it.
  • No traces/logs: generate traffic (open admin/portal), then re-check Explore in Grafana.

< Backend Deployment - AWS EC2 Deploy from GitHub Actions

Next: Backend Deployment - Release Please Setup >