Fly.io Deployment¶
Production deployment guide for MeisterBill on Fly.io
Overview¶
MeisterBill is deployed to Fly.io with the following architecture:
- Web App - Nuxt 3 frontend (SSR)
- API - Hono backend with Bun runtime
- Gotenberg - Standalone PDF generation service
All services run as separate Fly.io apps connected via Fly.io's internal networking (.internal domain).
Prerequisites¶
# Install Fly.io CLI
brew install flyctl
# Login to Fly.io
fly auth login
# Verify authentication
fly auth whoami
Service Deployments¶
1. Gotenberg (PDF Service)¶
Gotenberg is a Chrome-based HTML-to-PDF conversion service used by the API for invoice PDF generation.
Configuration: packages/infra/fly/gotenberg.toml
Dockerfile: packages/infra/docker/Dockerfile.gotenberg
Deploy:
fly deploy \
--config ./packages/infra/fly/gotenberg.toml \
--dockerfile ./packages/infra/docker/Dockerfile.gotenberg
Key Settings:
- App: meisterbill-gotenberg
- Region: dfw (Dallas)
- Internal Port: 3000
- Auto-scaling: 0-N machines (stops when idle)
- VM Size: shared-cpu-1x
- Concurrency: 20-25 requests
Internal URL: http://meisterbill-gotenberg.internal:3000
Verify Deployment:
fly status --app meisterbill-gotenberg
fly logs --app meisterbill-gotenberg
2. API (Backend)¶
Hono backend API with Bun runtime. Connects to Gotenberg for PDF generation.
Configuration: packages/infra/fly/api.toml
Dockerfile: packages/infra/docker/Dockerfile.api
Deploy:
fly deploy \
--config ./packages/infra/fly/api.toml \
--dockerfile ./packages/infra/docker/Dockerfile.api
Required Secrets:
# Set Supabase credentials
fly secrets set SUPABASE_URL=https://your-project.supabase.co --app meisterbill-api
fly secrets set SUPABASE_KEY=your-service-key --app meisterbill-api
# Set Gotenberg internal URL
fly secrets set GOTENBERG_URL=http://meisterbill-gotenberg.internal:3000 --app meisterbill-api
Verify Deployment:
fly status --app meisterbill-api
fly logs --app meisterbill-api
# Test API health
curl https://api.meister-bill.com/health
3. Web (Frontend)¶
Nuxt 3 SSR frontend application.
Configuration: packages/infra/fly/web.toml
Dockerfile: packages/infra/docker/Dockerfile.web
Deploy:
fly deploy \
--config ./packages/infra/fly/web.toml \
--dockerfile ./packages/infra/docker/Dockerfile.web
Required Secrets:
# Set API URL
fly secrets set NUXT_PUBLIC_API_URL=https://api.meister-bill.com --app meisterbill-web
Verify Deployment:
fly status --app meisterbill-web
fly logs --app meisterbill-web
# Test web app
curl https://meister-bill.com
Deployment Order¶
For a full deployment, follow this order:
# 1. Deploy Gotenberg (no dependencies)
fly deploy --config ./packages/infra/fly/gotenberg.toml \
--dockerfile ./packages/infra/docker/Dockerfile.gotenberg
# 2. Deploy API (depends on Gotenberg)
fly secrets set GOTENBERG_URL=http://meisterbill-gotenberg.internal:3000 --app meisterbill-api
fly deploy --config ./packages/infra/fly/api.toml \
--dockerfile ./packages/infra/docker/Dockerfile.api
# 3. Deploy Web (depends on API)
fly secrets set NUXT_PUBLIC_API_URL=https://api.meister-bill.com --app meisterbill-web
fly deploy --config ./packages/infra/fly/web.toml \
--dockerfile ./packages/infra/docker/Dockerfile.web
Internal Networking¶
Fly.io provides private networking between apps in the same organization via .internal domains:
- Gotenberg:
http://meisterbill-gotenberg.internal:3000 - API:
http://meisterbill-api.internal:3001
Benefits: - ✅ No public exposure of internal services - ✅ Encrypted internal communication - ✅ Zero network latency within same region - ✅ No additional networking configuration needed
Environment Variables¶
API Required Secrets¶
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your-service-role-key
GOTENBERG_URL=http://meisterbill-gotenberg.internal:3000
PORT=3001
Web Required Secrets¶
NUXT_PUBLIC_API_URL=https://api.meister-bill.com
Set secrets:
fly secrets set KEY=value --app app-name
List secrets:
fly secrets list --app app-name
Monitoring & Logs¶
View logs in real-time:
fly logs --app meisterbill-api
fly logs --app meisterbill-web
fly logs --app meisterbill-gotenberg
Check app status:
fly status --app meisterbill-api
SSH into machine:
fly ssh console --app meisterbill-api
View machine metrics:
fly dashboard meisterbill-api
Scaling¶
Manual scaling:
# Scale to 2 machines
fly scale count 2 --app meisterbill-api
# Change VM size
fly scale vm shared-cpu-2x --app meisterbill-api
Auto-scaling configuration (in *.toml):
[http_service]
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0 # Scale to zero when idle
Troubleshooting¶
PDF Generation Fails¶
Symptom: API logs show "Cannot connect to Gotenberg"
Solution:
# Check if Gotenberg is running
fly status --app meisterbill-gotenberg
# Check Gotenberg logs
fly logs --app meisterbill-gotenberg
# Verify GOTENBERG_URL secret
fly secrets list --app meisterbill-api
# Restart API machines
fly machine restart --app meisterbill-api
Service Not Starting¶
Check build logs:
fly logs --app meisterbill-api
Common issues: - ❌ Missing secrets (SUPABASE_URL, SUPABASE_KEY) - ❌ Wrong Dockerfile path - ❌ Build failures (check dependencies)
Connection Issues¶
Verify internal networking:
# SSH into API machine
fly ssh console --app meisterbill-api
# Test Gotenberg connectivity
curl http://meisterbill-gotenberg.internal:3000/health
Rollback¶
List releases:
fly releases --app meisterbill-api
Rollback to previous version:
fly releases rollback --app meisterbill-api
CI/CD Integration¶
Deployment is automated via GitHub Actions / Gitea CI:
Workflow location: .gitea/workflows/deploy.yml
Manual trigger:
# Push to main branch triggers deployment
git push origin main
Cost Optimization¶
Free tier limits: - 3 shared-cpu-1x VMs (1GB RAM) - 160GB transfer/month - 3GB persistent volume storage
Auto-scaling to zero: - Gotenberg, API, and Web scale to 0 when idle - First request auto-starts machines (~1-2 second cold start) - Saves cost during low traffic periods
Monitoring costs:
fly dashboard
Additional Resources¶
Last updated: 2025-10-08