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:
| Flag | Behavior |
|---|---|
./deploy.sh | Normal deploy (uses cache) |
./deploy.sh --rebuild | Full rebuild without Docker cache |
./deploy.sh --lite | Rebuild with cache (alias for --rebuild-lite) |
./deploy.sh --force | Recreate containers and reload configuration without rebuilding |
./deploy.sh --restart | Restart Flo without rebuilding |
./deploy.sh --rebuild-nginx | Rebuild Nginx without cache |
./deploy.sh --all | Start 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:
- SDK stage — Builds the .NET backend
- Node stage — Builds the Angular frontend
- 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.htmlfor 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
/healthat internal port10001 - PostgreSQL —
pg_isreadycommand
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