Skip to content

Deployment Guide

mustafaozturkmen edited this page May 12, 2026 · 1 revision

Deployment Guide — Local History Story Map

Last updated: 2026-05-12 (Final Milestone)

This guide covers how to run the project locally with Docker Compose and how it is deployed to production on Render.


Prerequisites

  • Docker ≥ 24 and Docker Compose v2 (docker compose — no hyphen)
  • Git
  • (Optional) Python 3.11+ if you want to run backend tests outside Docker

Local Development Setup

1. Clone the repository

git clone https://github.com/bounswe/bounswe2026group2.git
cd bounswe2026group2

2. Create the environment file

Copy the example and fill in any secrets you need:

cp backend/.env.example backend/.env

The defaults work for a fully local run (MinIO + local Postgres). External service keys are optional — features degrade gracefully when they are absent:

Variable Required for Degrades to
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET Google OAuth login OAuth buttons hidden/error
OPENAI_API_KEY Audio transcription Transcription skipped silently
GEMINI_API_KEY AI auto-tagging on story create Auto-tagging skipped silently

3. Start all services

./localrun.sh
# or equivalently:
docker compose up --build

localrun.sh checks for port conflicts, bootstraps .env if missing, waits for Postgres to be ready, and starts all services.

Service URL
Backend API http://localhost:8000
Swagger UI http://localhost:8000/docs
ReDoc http://localhost:8000/redoc
Frontend http://localhost:3000
MinIO console http://localhost:9001 (minioadmin / minioadmin)

4. Apply database migrations

On first run (or after new migrations are added):

docker compose exec backend alembic upgrade head

5. Verify health

curl http://localhost:8000/health
# → {"status": "ok"}

Environment Variables Reference

Database

Variable Default Description
DATABASE_URL postgresql://postgres:postgres@db:5432/localhistory PostgreSQL connection string. Use @localhost:5432 when running outside Docker.
LOG_SQL false Log all SQL statements (very verbose — development only)
LOG_LEVEL INFO Python logging level

Object Storage

Variable Default Description
STORAGE_ENDPOINT http://minio:9000 S3-compatible endpoint. Change to http://localhost:9000 when running backend outside Docker.
STORAGE_ACCESS_KEY minioadmin Storage access key
STORAGE_SECRET_KEY minioadmin Storage secret key
STORAGE_REGION us-east-1 S3 region
STORAGE_PUBLIC_URL http://localhost:9000 Public URL for media links returned in API responses
STORAGE_BUCKET_IMAGES images
STORAGE_BUCKET_AUDIO audio
STORAGE_BUCKET_VIDEOS videos

CORS

Variable Default Description
CORS_ORIGINS http://localhost:3000 Comma-separated list of allowed origins

JWT

Variable Default Description
JWT_SECRET_KEY your-secret-key Change this in production. Used to sign/verify all tokens.
JWT_ALGORITHM HS256
JWT_EXPIRE_MINUTES 30 Token TTL in minutes

Google OAuth

Variable Default Description
GOOGLE_CLIENT_ID (empty) OAuth 2.0 client ID from Google Cloud Console
GOOGLE_CLIENT_SECRET (empty) OAuth 2.0 client secret
GOOGLE_REDIRECT_URI http://localhost:8000/auth/google/callback Must be registered in Google Cloud Console. Change to the production domain when deploying.
FRONTEND_GOOGLE_CALLBACK_URL http://localhost:3000/oauth-callback.html Frontend URL that receives the JWT from the OAuth callback

Speech-to-Text (OpenAI Whisper API)

Variable Default Description
OPENAI_API_KEY (empty) OpenAI API key. Leave empty to disable audio transcription.
AI_WHISPER_MODEL whisper-1 Whisper model ID (only whisper-1 is currently available via the API)

AI Auto-Tagging (Google Gemini)

Variable Default Description
GEMINI_API_KEY (empty) Google Gemini API key. Leave empty to disable AI auto-tagging.
AI_TAGGING_MODEL gemini-2.5-flash Gemini model used for tag generation

Running Tests

Tests run against a separate in-memory or test database — they never touch the development database.

# From bounswe2026group2/backend/
pytest tests/unit/
pytest tests/integration/
pytest tests/api/

# Frontend unit tests
cd frontend && npm test

# Playwright UAT (requires running services)
cd frontend && npx playwright test tests/uat/

Linting

# Backend
cd backend && ruff check . && ruff format --check .

# Frontend
cd frontend && npm run lint

Production Deployment (Render)

The production environment runs on Render as a Docker-based web service backed by a managed Postgres database and Supabase S3 for object storage.

Services

Service Type Notes
localhistorymap-backend Web service (Docker) Runs uvicorn app.main:app
localhistorymap-db Managed PostgreSQL Render-managed; URL injected as DATABASE_URL
Frontend / Nginx Static site Served from frontend/

Deployment flow

  1. Push to main triggers CI (ci.yml).
  2. On CI success, deploy.yml deploys to Render via webhook.
  3. Render rebuilds the Docker image and runs alembic upgrade head before starting the new container.

Required Render environment variables

Set these in the Render dashboard under the backend service's Environment tab:

DATABASE_URL=postgresql://...  (injected by Render Postgres)
JWT_SECRET_KEY=<strong-random-secret>
CORS_ORIGINS=https://localhistorymap.app
STORAGE_ENDPOINT=https://<supabase-project>.supabase.co/storage/v1/s3
STORAGE_ACCESS_KEY=<supabase-access-key>
STORAGE_SECRET_KEY=<supabase-secret-key>
STORAGE_PUBLIC_URL=https://<supabase-project>.supabase.co/storage/v1/object/public
GOOGLE_CLIENT_ID=<google-oauth-client-id>
GOOGLE_CLIENT_SECRET=<google-oauth-client-secret>
GOOGLE_REDIRECT_URI=https://localhistorymap-backend.onrender.com/auth/google/callback
FRONTEND_GOOGLE_CALLBACK_URL=https://localhistorymap.app/oauth-callback.html
OPENAI_API_KEY=<openai-key>
GEMINI_API_KEY=<gemini-key>
WEB_CONCURRENCY=1

Note: WEB_CONCURRENCY=1 is required on the free Render tier (512 MB RAM) to avoid OOM errors from concurrent request handling.

Manual release

A GitHub Release can be triggered manually via the Actions tab → release.yml. It requires a tag (e.g., v1.0.0) and release_name input, runs full CI, deploys, and publishes a GitHub Release with JUnit test summaries.


Common Issues

Problem Fix
relation "stories" does not exist during migration Stale Postgres volume: docker compose down -v && ./localrun.sh
Multiple head revisions Alembic error Two migrations pointing to the same parent. Add an empty merge migration. Run alembic heads before pushing.
Backend .env uses db:5432 but you want to run outside Docker Change DATABASE_URL to use localhost:5432 and STORAGE_ENDPOINT to http://localhost:9000
Port already in use localrun.sh checks with docker ps --filter publish=<port>. Kill the container holding it.
gradlew Permission denied in Android CI git update-index --chmod=+x frontend/android/gradlew
bcrypt crash with >=5.0.0 bcrypt is pinned to 4.2.1 in requirements.txt. Do not upgrade.
Team Members

Milestones


Lab Reports


Weekly Meetings

Other Meetings


Templates

Scenarios

Use Case Diagrams

Class Diagram

Sequence Diagrams

Requirements


Implementation

Clone this wiki locally