Deployment Guide¶
Deploy the backend and frontend to production infrastructure.
Architecture Overview¶
Deployment consists of three components:
- Backend — ASP.NET Core 10 API serving on port 8080
- Frontend — Vite-built React SPA served via Nginx on port 8080
- Database — PostgreSQL (external, provisioned separately)
- Object Storage — Cloudflare R2 for marker images and profile pictures
- Email — Resend for transactional emails
Prerequisites¶
Before deploying, you need:
- PostgreSQL database connection string
- Cloudflare R2 bucket with access keys
- Resend API key for sending emails
- Cloudflare Turnstile site key and secret key (for registration and forgot-password CAPTCHA)
- Domain name with DNS pointing to your server
- Sentry DSN (optional, for error tracking)
Environment Variables¶
Both services use environment variables for configuration. See Environment Variables for the complete list.
Backend¶
Required variables:
| Variable | Purpose |
|---|---|
ConnectionStrings__DefaultConnection | PostgreSQL connection string |
AllowedOrigin | Frontend URL (CORS) |
Security__Secret | JWT signing key (min 64 chars) |
Security__Issuer | JWT issuer |
Security__Audience | JWT audience |
ObjectStorage__Endpoint | R2 S3 endpoint URL |
ObjectStorage__AccessKeyId | R2 access key ID |
ObjectStorage__SecretAccessKey | R2 secret access key |
ObjectStorage__BucketName | R2 bucket name |
Integrations__Email__Resend__ApiKey | Resend API key |
Integrations__Email__Resend__From__Email | Sender email |
Integrations__Email__Resend__From__Name | Sender name |
Integrations__Turnstile__SecretKey | Turnstile secret key |
Frontend¶
Build-time VITE_* variables are injected via Docker build args. The only required variable is VITE_API_URL, pointing to the backend API. See Frontend Environment for optional variables.
At runtime, the frontend Docker container substitutes VITE_API_URL into nginx.conf via envsubst.
Building and Running¶
Docker Build¶
Both services have Dockerfiles for containerized deployment:
Backend:
# Multi-stage: build with dotnet SDK, run with aspnet runtime
# Exposes port 8080
# Timezone: Europe/Prague
# Runs as non-root app user
Frontend:
# 3-stage: build (Node), optional Sentry source maps, serve (Nginx)
# Exposes port 8080
# VITE_API_URL injected at runtime via envsubst into nginx.conf
# Optional SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT build args
Docker Compose (Local Only)¶
The root docker-compose.yml runs MinIO for local object storage development. It is not intended for production use.
See Dev Environment Setup for local development.
CI/CD Pipeline¶
The project uses GitHub Actions with three workflows:
| Workflow | Trigger | Checks |
|---|---|---|
| Backend | Push/PR to main (API changes) | Build with warn-as-error, unit tests + coverage, integration tests + coverage, vulnerability scan |
| Frontend | Push/PR to main (Client changes) | npm ci, audit, lint, type-check, unit tests, build |
| Security | Always on push/PR to main | Gitleaks secret scanning |
Both backend and frontend workflows filter paths to skip irrelevant changes (markdown, dockerignore, etc.). See CI/CD for pipeline details.
Nginx Configuration¶
The frontend Docker image includes an nginx.conf with:
- Gzip compression for CSS, JS, JSON, SVG, fonts
- Client max body size: 10 MB
- Caching: 1 year for
/assets/(immutable), 24h for images, no-cache for everything else - Security headers: X-Frame-Options, X-Content-Type-Options, HSTS, Referrer-Policy, Permissions-Policy
- CSP Report-Only: Restricts scripts, styles, images, fonts, connections, workers, frames
- SPA routing: Falls back all routes to
/index.html
The CSP allows connections to the API URL, Sentry, Cloudflare Turnstile, and R2 storage. The VITE_API_URL is substituted at container startup.
Object Storage¶
Cloudflare R2 stores marker images and profile pictures. Configuration includes:
- S3-compatible API endpoint, access key, secret key, bucket name
- Presigned URL expiration: 5 minutes for upload, 60 minutes for download
- Max file size and allowed MIME types (configurable)
For local development, MinIO replaces R2 via docker-compose. See Integrations for details.
Monitoring¶
Sentry error tracking is optional on both frontend and backend:
- Frontend: Sentry React SDK, source map uploads during Docker build
- Backend: Sentry.AspNetCore, configured via
Integrations__Sentry__*env vars - Trace sample rate: 0.1
Related¶
- Environment Variables — All configuration values
- Integrations — Third-party services
- CI/CD — Automated deployment pipelines