Skip to content

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