-
Notifications
You must be signed in to change notification settings - Fork 0
Deployment Guide
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.
-
Docker ≥ 24 and Docker Compose v2 (
docker compose— no hyphen) - Git
- (Optional) Python 3.11+ if you want to run backend tests outside Docker
git clone https://github.com/bounswe/bounswe2026group2.git
cd bounswe2026group2Copy the example and fill in any secrets you need:
cp backend/.env.example backend/.envThe 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 |
./localrun.sh
# or equivalently:
docker compose up --buildlocalrun.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) |
On first run (or after new migrations are added):
docker compose exec backend alembic upgrade headcurl http://localhost:8000/health
# → {"status": "ok"}| 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 |
| 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 |
| Variable | Default | Description |
|---|---|---|
CORS_ORIGINS |
http://localhost:3000 |
Comma-separated list of allowed origins |
| 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 |
| 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 |
| 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) |
| 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 |
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/# Backend
cd backend && ruff check . && ruff format --check .
# Frontend
cd frontend && npm run lintThe production environment runs on Render as a Docker-based web service backed by a managed Postgres database and Supabase S3 for object storage.
| 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/
|
- Push to
maintriggers CI (ci.yml). - On CI success,
deploy.ymldeploys to Render via webhook. - Render rebuilds the Docker image and runs
alembic upgrade headbefore starting the new container.
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=1is required on the free Render tier (512 MB RAM) to avoid OOM errors from concurrent request handling.
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.
| 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
- Lab 1 Report (12/02/2026)
- Lab 2 Report (19/02/2026)
- Lab 3 Report (26/02/2026)
- Lab 4 Report (05/03/2026)
- Lab 5 Report (12/03/2026)
- Lab 6 Report (26/03/2026)
- Lab 7 Report (02/04/2026)
- Lab 8 Report (16/04/2026)
- Lab 9 Report (30/04/2026)
- Lab 10 Report (07/05/2026)
- Weekly Meeting 1 (14.02.2026)
- Weekly Meeting 2 (21.02.2026)
- Weekly Meeting 3 (28.02.2026)
- Weekly Meeting 4 (07.03.2026)
- Weekly Meeting 5 (14.03.2026)
- Weekly Meeting 6 (19.03.2026)
- Weekly Meeting 7 (28.03.2026)
- Weekly Meeting 8 (05.04.2026)
- Weekly Meeting 9 (11.04.2026)
- Weekly Meeting 10 (18.04.2026)
- Weekly Meeting 11 (02.05.2026)
- Weekly Meeting 12 (09.05.2026)
- Customer Meeting 1 (18.02.2026)
- Stakeholder Meeting 1 (09.03.2026)
- Milestone Review Meeting (05.04.2026)
- MVP Test Meeting (08.04.2026)
- Customer Meeting 2 (30.04.2026)