Skip to content

Latest commit

 

History

History
317 lines (243 loc) · 8.16 KB

File metadata and controls

317 lines (243 loc) · 8.16 KB

Deployment

StudyBuddy is configured for deployment on Render as a Docker-backed Django application with managed PostgreSQL. The deployment is defined as code in:

render.yaml

Live MVP URL:

https://studybuddy-django-app.onrender.com

Repository:

https://github.com/AAdewunmi/StudyBuddy-Study-Planner-Project

Why Render

Render is appropriate for the StudyBuddy SaaS MVP because it gives the project a low-operations production path without requiring custom cloud infrastructure. The application already has a Docker runtime, PostgreSQL persistence, strict environment-driven production settings, and a deployment health endpoint. Render matches that shape with Docker web services, managed PostgreSQL, Blueprint as code, health checks, and environment variable and secret injection.

For an MVP, this keeps the deployment surface small: the repository defines the application runtime and production contract, while Render handles the platform work needed to host it. That lets the project validate the product workflow, data model, and operational checks before introducing more complex cloud infrastructure.

Platform Boundary

The app and repository own:

  • Django application code and tests
  • Dockerfile
  • render.yaml
  • database migrations
  • static collection and WhiteNoise static file configuration
  • /health/ endpoint behavior
  • production settings and runtime contract

Render owns:

  • web service hosting
  • routing traffic to the container PORT
  • TLS termination and certificate management
  • deploy orchestration
  • managed PostgreSQL lifecycle
  • environment variable and secret injection
  • health check execution

The app intentionally does not own:

  • manual server provisioning
  • PostgreSQL host operating-system management
  • TLS certificate provisioning
  • load balancer setup

Runtime Shape

The production container is defined by:

Dockerfile

The Render service and database are defined by:

render.yaml

The image uses:

  • Python 3.13 slim
  • config.settings.production
  • Gunicorn
  • WhiteNoise compressed manifest static files
  • PORT, defaulting to 8000

The Docker build collects static assets into staticfiles/ with production settings and build-time placeholder values. Runtime secrets are still provided by Render.

The container command is:

gunicorn config.wsgi:application --bind 0.0.0.0:${PORT}

Render runs database migrations before each deploy with:

python manage.py migrate --noinput

Production Settings

Production settings live in:

config/settings/production.py

Production settings are intentionally environment-driven. The application fails fast if required production values are missing.

Required environment variables:

DJANGO_SECRET_KEY
DJANGO_ALLOWED_HOSTS
DATABASE_URL
DJANGO_DEFAULT_FROM_EMAIL
DJANGO_EMAIL_HOST

Recommended environment variables:

DJANGO_SETTINGS_MODULE=config.settings.production
DJANGO_CSRF_TRUSTED_ORIGINS=https://studybuddy-django-app.onrender.com
DJANGO_SECURE_SSL_REDIRECT=True
DATABASE_SSL_REQUIRE=True
DJANGO_SECURE_HSTS_SECONDS=31536000
DJANGO_LOG_LEVEL=INFO
DJANGO_EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
DJANGO_EMAIL_PORT=587
DJANGO_EMAIL_USE_TLS=True
DJANGO_EMAIL_USE_SSL=False
DJANGO_EMAIL_TIMEOUT=10
RELEASE_SHA=<optional explicit release identifier>

Render provides DATABASE_URL from the managed PostgreSQL service declared in render.yaml. DJANGO_SECRET_KEY and email provider-specific values are declared with sync: false, so Render prompts for them during Blueprint creation instead of storing secrets in the repository. Render also provides RENDER_GIT_COMMIT at runtime; StudyBuddy uses that value for the /health/ release field when RELEASE_SHA is not explicitly set.

Render Blueprint

The Blueprint creates:

  • a Docker web service named studybuddy-django-app
  • a managed PostgreSQL database named studybuddy-postgres
  • explicit free Render plans for both resources
  • an HTTP health check at /health/
  • a pre-deploy migration command
  • non-secret production environment variables
  • a DATABASE_URL value derived from the managed database
  • DJANGO_SECRET_KEY and email provider prompts through Render's secret injection flow

The service uses autoDeployTrigger: checksPass, so Render waits for linked Git checks before deploying commits.

Blueprint-managed environment values:

DJANGO_SETTINGS_MODULE=config.settings.production
DJANGO_ALLOWED_HOSTS=studybuddy-django-app.onrender.com
DJANGO_CSRF_TRUSTED_ORIGINS=https://studybuddy-django-app.onrender.com
DATABASE_URL=<from studybuddy-postgres connectionString>
DATABASE_SSL_REQUIRE=True
DJANGO_SECURE_SSL_REDIRECT=True
DJANGO_SECURE_HSTS_SECONDS=31536000
DJANGO_LOG_LEVEL=INFO
DJANGO_EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
DJANGO_EMAIL_PORT=587
DJANGO_EMAIL_USE_TLS=True
DJANGO_EMAIL_USE_SSL=False
DJANGO_EMAIL_TIMEOUT=10

Secret or generated values:

DJANGO_SECRET_KEY=<prompted by Render because sync: false>
DATABASE_URL=<generated by the managed Render PostgreSQL database>
DJANGO_EMAIL_HOST=<provided by the email provider>
DJANGO_EMAIL_HOST_USER=<provided by the email provider>
DJANGO_EMAIL_HOST_PASSWORD=<provided by the email provider>
DJANGO_DEFAULT_FROM_EMAIL=<verified sender address>
DJANGO_SERVER_EMAIL=<optional server error sender address>
RENDER_GIT_COMMIT=<provided by Render for the deployed commit>

Do not use .env.example credentials in production. They are local-only placeholders for Docker Compose development. Keep production secret values in Render, not in the repository.

Static Files

Production static files use Django's STORAGES setting with:

whitenoise.storage.CompressedManifestStaticFilesStorage

The Docker image runs collectstatic during build. To verify static collection locally:

docker compose exec -T web env \
  DJANGO_SECRET_KEY='local-deploy-check-only-long-random-looking-secret-1234567890' \
  DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1 \
  DATABASE_URL=postgres://studybuddy:studybuddy@db:5432/studybuddy_local \
  DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://127.0.0.1:8000 \
  DATABASE_SSL_REQUIRE=False \
  DJANGO_DEFAULT_FROM_EMAIL=local-deploy-check@example.com \
  DJANGO_EMAIL_HOST=localhost \
  python manage.py collectstatic --noinput --settings=config.settings.production

Expected local receipt:

0 static files copied to '/app/staticfiles', 128 unmodified, 357 post-processed.

The exact copied/unmodified counts can change when static assets change.

Health Check

StudyBuddy exposes a deployment health endpoint:

GET /health/

Successful response:

{
  "status": "ok",
  "service": "studybuddy",
  "release": "local",
  "checks": {
    "database": "ok"
  }
}

If the database check fails, the endpoint returns HTTP 503 with:

{
  "status": "degraded",
  "service": "studybuddy",
  "release": "local",
  "checks": {
    "database": "unavailable"
  }
}

Local Deployment Checks

Run a production-settings deployment check locally through Docker Compose:

docker compose exec -T web env \
  DJANGO_SECRET_KEY='local-deploy-check-only-long-random-looking-secret-1234567890' \
  DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1 \
  DATABASE_URL=postgres://studybuddy:studybuddy@db:5432/studybuddy_local \
  DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost:8000,http://127.0.0.1:8000 \
  DATABASE_SSL_REQUIRE=False \
  DJANGO_DEFAULT_FROM_EMAIL=local-deploy-check@example.com \
  DJANGO_EMAIL_HOST=localhost \
  python manage.py check --deploy --settings=config.settings.production

Expected clean receipt:

System check identified no issues (0 silenced).

Run the health endpoint test:

docker compose exec -T web env \
  DJANGO_SETTINGS_MODULE=config.settings.test \
  TEST_DATABASE_URL=postgres://studybuddy:studybuddy@db:5432/studybuddy_test \
  pytest tests/test_health_check.py -q

Expected receipt:

3 passed

CI Relationship

GitHub Actions validates the project with:

  • Django system checks
  • migration drift checks
  • migrations
  • Ruff
  • Black
  • isort
  • Docker image build
  • pytest with coverage
  • Codecov upload

See Continuous Integration for the full CI quality gate.