Skip to content

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:

cd backend && pip install uv && uv sync --frozen --no-dev

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.UvicornWorker for 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:

cd backend && uv run celery -A config worker -l info

Processes async tasks (interest accrual, dunning, statement generation, etc.). For the beat scheduler, add a separate service:

cd backend && uv run celery -A config beat -l info

Frontend Static Sites

Both frontend apps build to static files served via Render's static site hosting:

Admin Dashboard

cd frontend/admin && npm install && npm run build
# Output: frontend/admin/dist/

Borrower Portal

cd frontend/borrower && npm install && npm run build
# Output: frontend/borrower/dist/

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:

cd backend && uv run python manage.py migrateschema -as

This migrates all schemas (public + static + dynamic). For large deployments with many tenants, consider:

  • Running migrations separately from the deploy
  • Using --parallel flag: 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