This document covers deploying API Forge to Fly.io's Kubernetes Service (FKS), including compatibility analysis with our existing Helm-based deployment and future CLI design considerations.
Fly.io Kubernetes Service (FKS) is Fly.io's managed Kubernetes offering that integrates with their global Anycast network. It provides a different deployment model than traditional Kubernetes clusters (GKE, EKS, AKS), with some unique advantages and constraints.
Status: FKS support is planned but not yet implemented. This document captures research and design decisions for future development.
| Aspect | Standard K8s (GKE, EKS, AKS) | Fly.io FKS |
|---|---|---|
| Ingress | Ingress resource + Ingress Controller (nginx) | Not used - Fly's Anycast proxy handles routing |
| LoadBalancer | Cloud provider provisions external IP | Maps directly to Fly.io's edge network |
| TLS Certificates | cert-manager + Let's Encrypt | Automatic - Fly provisions and renews certs |
| DNS | Manual - point domain to LB IP | Automatic *.fly.dev + custom domain support |
| Global Distribution | Multi-region requires complex setup | Built-in - deploy to 30+ regions easily |
| Scaling | HPA, node autoscaling | Fly Machines with automatic scaling |
- Automatic TLS - No cert-manager, no Let's Encrypt configuration needed
- Global Anycast - Traffic routed to nearest region automatically
- DDoS Protection - Built into Fly's edge network
- Automatic DNS -
yourapp.fly.devdomains provisioned automatically - Custom Domains - Simple CNAME setup with automatic certificate issuance
- ❌ Ingress resources
- ❌ Ingress Controllers (nginx, traefik, etc.)
- ❌ cert-manager
- ❌ External DNS controllers
- ❌ Cloud provider load balancer integrations
On FKS, you expose services using type: LoadBalancer which Fly.io intercepts:
apiVersion: v1
kind: Service
metadata:
name: app
annotations:
# Fly-specific annotations
fly.io/app: my-fastapi-app
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: 8000
selector:
app.kubernetes.io/name: appFly.io then:
- Creates a Fly App if it doesn't exist
- Provisions
my-fastapi-app.fly.devdomain - Issues TLS certificate automatically
- Routes global traffic through Anycast
metadata:
annotations:
fly.io/app: my-fastapi-app
fly.io/domains: "api.example.com,api.mycompany.io"Then add a CNAME record:
api.example.com CNAME my-fastapi-app.fly.dev
Fly automatically issues certificates for custom domains.
| API Forge Feature | Standard K8s | FKS Compatibility |
|---|---|---|
| Helm chart deployment | ✅ Works | ✅ Works (FKS is standard K8s) |
| PostgreSQL StatefulSet | ✅ Works | |
| Redis Deployment | ✅ Works | |
| Temporal | ✅ Works | ✅ Works |
--ingress flag |
Creates Ingress | ❌ Should create LoadBalancer instead |
--ingress-host |
Sets Ingress host | Should set Fly app name |
--ingress-tls-secret |
References K8s Secret | ❌ Not needed |
--ingress-tls auto |
Uses cert-manager | ❌ Not needed |
| NetworkPolicies | ✅ Works | ✅ Works |
| PersistentVolumeClaims | ✅ Works | ✅ Works (Fly Volumes) |
-
Ingress → LoadBalancer Service
- Replace Ingress resource with LoadBalancer Service
- Add Fly-specific annotations
- Remove Ingress Controller dependency
-
TLS Configuration
- Remove cert-manager setup
- Remove TLS secret references
- Fly handles all certificate management
-
Database Considerations
- Could use in-cluster PostgreSQL (works but not recommended)
- Better: Use Fly Postgres (managed, with replicas)
- Fly Postgres uses their own clustering solution
-
Redis Considerations
- Could use in-cluster Redis (works)
- Alternative: Upstash Redis (Fly partnership, serverless)
# CLI detects cluster type and adapts
uv run api-forge-cli deploy up k8s --ingress --ingress-host myapp
# On FKS: Creates LoadBalancer Service with Fly annotations
# On standard K8s: Creates Ingress + optional cert-managerPros: Single command, automatic adaptation Cons: Complex logic, harder to debug, surprises users
# Explicit Fly.io target
uv run api-forge-cli deploy up fly --app myapp --region ord
# Standard Kubernetes
uv run api-forge-cli deploy up k8s --ingress --ingress-host api.example.comPros: Clear intent, simpler implementation, Fly-specific optimizations Cons: Another target to maintain
Separate targets are better because:
- Fly has unique features - Machines, regions, Fly Postgres are Fly-specific
- Simpler Helm chart - No conditionals for "is this FKS?"
- Better UX - Users explicitly choose their target
- Fly CLI integration - Can leverage
flyctlwhere appropriate - Different defaults - FKS might skip in-cluster Postgres entirely
# Setup Fly.io (one-time)
uv run api-forge-cli deploy setup fly
# - Verifies flyctl is installed
# - Authenticates with Fly.io
# - Creates Fly organization if needed
# Deploy to Fly Kubernetes
uv run api-forge-cli deploy up fly \
--app my-fastapi-app \
--region ord \
--postgres fly # Use Fly Postgres (recommended)
--redis upstash # Use Upstash Redis (optional)
# Or with in-cluster databases (not recommended for production)
uv run api-forge-cli deploy up fly \
--app my-fastapi-app \
--postgres in-cluster \
--redis in-cluster
# Status
uv run api-forge-cli deploy status fly --app my-fastapi-app
# Teardown
uv run api-forge-cli deploy down fly --app my-fastapi-appPhase 1: Basic FKS Support
- Deploy app and worker to FKS
- Use LoadBalancer Service for external access
- In-cluster PostgreSQL and Redis (same as standard K8s)
Phase 2: Fly-Native Services
- Fly Postgres integration
- Upstash Redis integration
- Fly Volumes for persistent storage
Phase 3: Advanced Features
- Multi-region deployment
- Fly Machines autoscaling
- Fly.io metrics integration
# values.yaml
app:
# Standard K8s ingress (existing)
ingress:
enabled: false
# ... existing config
# Fly.io specific (new)
fly:
enabled: false
app: ""
regions: ["ord"]
domains: []# templates/services/app-fly.yaml
{{- if .Values.app.fly.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.app.name | default "app" }}
namespace: {{ .Values.global.namespace }}
annotations:
fly.io/app: {{ .Values.app.fly.app | required "app.fly.app is required" }}
{{- if .Values.app.fly.domains }}
fly.io/domains: {{ .Values.app.fly.domains | join "," | quote }}
{{- end }}
labels:
{{- include "api-forge.labels" . | nindent 4 }}
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: {{ .Values.app.service.port | default 8000 }}
name: https
selector:
app.kubernetes.io/name: {{ .Values.app.name | default "app" }}
{{- end }}| Platform | TLS Strategy | CLI Flag |
|---|---|---|
| Minikube | None (HTTP) or self-signed | --ingress (no TLS) |
| Standard K8s | cert-manager + Let's Encrypt | --ingress --ingress-tls auto |
| Standard K8s | Manual certificate | --ingress --ingress-tls-secret name |
| AWS EKS | ACM certificate | --ingress + ACM annotation |
| GKE | Google-managed cert | --ingress + ManagedCertificate |
| Fly.io FKS | Automatic (Fly-managed) | deploy up fly (TLS automatic) |
| Platform | Recommended PostgreSQL | Recommended Redis |
|---|---|---|
| Development | Docker Compose (local) | Docker Compose (local) |
| Minikube | In-cluster StatefulSet | In-cluster Deployment |
| Standard K8s | In-cluster or managed (RDS, Cloud SQL) | In-cluster or managed |
| Fly.io FKS | Fly Postgres (managed) | Upstash Redis or in-cluster |
- Test locally with
deploy up dev - Test on Minikube with
deploy up k8s - Deploy to FKS with
deploy up fly
- Export data from existing PostgreSQL
- Create Fly Postgres cluster
- Import data to Fly Postgres
- Deploy app to FKS with
--postgres fly - Update DNS to point to Fly
- FKS is relatively new - Some features may change
- Fly Postgres clustering - Different from standard PostgreSQL HA
- Temporal on Fly - May need special consideration for workflows
- Cost model - Fly charges differently than traditional cloud
- Kubernetes Deployment Guide - Standard K8s deployment
- Ingress Configuration - Ingress and TLS for standard K8s
- Docker Dev Environment - Local development