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.examplewith 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:
- Open Keychain Access (Spotlight → “Keychain Access”).
- In the sidebar, select System keychain.
- In the Category list, select Certificates.
- Search for “Caddy Local Authority” (there may be multiple, including ECC Intermediate or older dates).
- Right-click each outdated Caddy Local Authority certificate → Delete.
- If prompted, enter your macOS admin password to confirm.
- (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:
- https://localhost → serves
./static-sites/main. - https://admin.localhost → Django admin portal.
- https://portal.localhost → Django volunteer portal.
- https://admin.localhost/dj-admin → Django’s built-in admin.
- https://developer.localhost → MkDocs developer docs site.
- https://api.localhost → OpenAPI (static).
- https://grafana.localhost → Grafana.
- https://prometheus.localhost → Prometheus.
- http://localhost:5050 → pgAdmin.
- http://localhost:8025 → Mailpit.
9) Create superusers & log in (roles)¶
-
Admin portal access
-
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-adminfirst, you may already be authenticated onadmin.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
.envvalues: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
- https://developer.localhost → Developer docs.
- https://api.localhost → API static site (OpenAPI).
13) Observability¶
Prometheus¶
- Go to https://prometheus.localhost (or http://localhost:9090).
- Status → Target Health: targets for caddy, otelcol, postgres, redis should be UP.
Grafana¶
- Go to https://grafana.localhost (or http://localhost:3000).
- Log in with
.envGRAFANA_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 URLsportal.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:
- Refresh Mailpit UI at http://localhost:8025
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.