Skip to main content

Single-Tenant Deployment

Single-tenant deployment uses Docker Compose with Nginx as a reverse proxy. This is the simplest setup for running one Flo instance.

Prerequisites​

  • Docker & Docker Compose v2
  • SSL certificates (Cloudflare Origin or Let's Encrypt)
  • PostgreSQL 17 (included in Docker Compose)

Quick Start​

# Clone and configure
git clone https://github.com/team-ledges/Flo
cd Flo
cp .env.example .env
nano .env # Fill in required values

# Deploy
./deploy.sh

Deployment Script​

The deploy.sh script handles the full deployment:

FlagBehavior
./deploy.shNormal deploy (uses cache)
./deploy.sh --rebuildFull rebuild without Docker cache
./deploy.sh --liteRebuild with cache (alias for --rebuild-lite)
./deploy.sh --forceRecreate containers and reload configuration without rebuilding
./deploy.sh --restartRestart Flo without rebuilding
./deploy.sh --rebuild-nginxRebuild Nginx without cache
./deploy.sh --allStart all services, including PostgreSQL

Docker Compose Architecture​

┌─────────────────────────────────┐
│ Nginx (443/80) │
│ SSL termination, rate limiting │
│ Security headers, static files │
├─────────────────────────────────┤
│ Flo.BE + Flo.FE │
│ .NET 10 API + Angular 21 SPA │
│ Port 10001 (internal) │
├─────────────────────────────────┤
│ PostgreSQL 17 │
│ Port 5432 (internal) │
└─────────────────────────────────┘

The Dockerfile uses a multi-stage build:

  1. SDK stage — Builds the .NET backend
  2. Node stage — Builds the Angular frontend
  3. Runtime stage — Alpine image with compiled output

Nginx serves the Angular SPA for all frontend routes and proxies /api/ requests to the .NET backend.

Nginx Configuration​

nginx.conf.template includes:

  • SSL — Cloudflare Origin certificates
  • Security headers — HSTS, CSP, X-Frame-Options, Referrer-Policy
  • Rate limiting — Per-IP limits on auth and public API endpoints
  • File blocking — Blocks access to .git, .env, .yaml, and other sensitive files
  • Upload proxy — Serves uploaded files from /uploads/
  • SPA routing — Falls back to index.html for Angular routes

Local builds and published images​

For the single-tenant path, ./deploy.sh builds the image on the server. The Dockerfile includes the frontend by default.

CI also publishes images to GHCR, but CI builds set INCLUDE_FRONTEND=false: the frontend may be deployed separately to Cloudflare Pages. Do not automatically replace the local build with the GHCR image if Nginx is expected to serve the UI as well.

The current tags are latest (master), develop-latest, the branch name, and <branch>-<short-sha>. Verify image availability and the frontend hosting strategy before using a published image.

Health Checks​

Docker Compose defines health checks for the application and database:

  • Flo app — HTTP check on /health at internal port 10001
  • PostgreSQL — pg_isready command

Nginx depends on the Flo app reaching healthy status, but the current Compose file does not define a separate Nginx health check.

Monitoring​

# Container status
sudo docker ps

# Application logs
sudo docker compose logs -f

# Flo app logs only
sudo docker logs -f flo

# Resource usage
sudo docker stats

Updating​

cd ~/Flo
git pull
./deploy.sh --lite # Rebuild with cache

For a full clean rebuild:

./deploy.sh --rebuild