Skip to content

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