Deployment¶
The LMS deploys to Render.com with separate services for the API, worker, frontend apps, database, and Redis.
Service Architecture¶
| Service | Type | Purpose |
|---|---|---|
lms-api |
Web service (Python) | Django API server (gunicorn + uvicorn) |
lms-worker |
Background worker | Celery worker for async tasks |
lms-frontend |
Static site | Admin dashboard (React/Vite) |
lms-portal |
Static site | Borrower portal (React/Vite) |
lms-db |
PostgreSQL 16+ | Primary database |
lms-redis |
Redis | Celery broker + result backend |
Configuration lives in render.yaml at the project root.
API Service¶
Build command:
Start command:
cd backend && uv run gunicorn config.asgi:application \
-k uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:$PORT
- Uses ASGI via
uvicorn.workers.UvicornWorkerfor async support - Port is dynamically assigned by Render via
$PORT - Health check:
GET /api/v1/health
Celery Worker¶
Build command: Same as API service.
Start command:
Processes async tasks (interest accrual, dunning, statement generation, etc.). For the beat scheduler, add a separate service:
Frontend Static Sites¶
Both frontend apps build to static files served via Render's static site hosting:
Admin Dashboard¶
Borrower Portal¶
Both use SPA routing (/* → /index.html) so React Router handles client-side navigation.
Environment Variables¶
| Variable | Required | Description |
|---|---|---|
DJANGO_SETTINGS_MODULE |
Yes | config.settings.production |
SECRET_KEY |
Yes | Django secret key (auto-generated) |
DATABASE_URL |
Yes | PostgreSQL connection string |
REDIS_URL |
Yes | Redis connection string |
ALLOWED_HOSTS |
Yes | Comma-separated domains (e.g., .onrender.com) |
SECURE_SSL_REDIRECT |
No | Default True in production |
PROVIDER_ENCRYPTION_KEY |
Yes | Fernet key for provider credential encryption |
Additional provider-specific variables (SendGrid API key, Twilio credentials, S3 bucket, etc.) are configured as needed.
Database Migrations on Deploy¶
Migrations should run as part of the deploy process:
This migrates all schemas (public + static + dynamic). For large deployments with many tenants, consider:
- Running migrations separately from the deploy
- Using
--parallelflag:migrateschema -ds --parallel - Scheduling during maintenance windows for schema-altering migrations
Production Security¶
The production.py settings enable:
DEBUG = False- HTTPS-only cookies (
SESSION_COOKIE_SECURE,CSRF_COOKIE_SECURE) - HSTS headers (1-year max-age, include subdomains, preload)
- XSS and content-type sniffing protection
- SSL redirect
See Also¶
- Environment Variables --- Complete variable reference
- Docker --- Local development with Docker
- Database Management --- Migration operations