βοΈ Fly.io Deployment Guide for MCP Gateway¶
This guide covers the complete deployment workflow for the MCP Gateway on Fly.io, including common troubleshooting steps.
Overview¶
Fly.io is a global app platform for running containers close to your users, with built-in TLS, persistent volumes, and managed Postgres support. It offers a generous free tier and automatic HTTPS with fly.dev subdomains.
1 - Prerequisites¶
Requirement | Details |
---|---|
Fly.io account | Sign up |
Fly CLI | Install via Homebrew: brew install flyctl or see Fly docs |
Docker or Podman | For local image builds (optional) |
Containerfile | The included Containerfile with psycopg2-binary support |
2 - Quick Start (Recommended)¶
2.1 Initialize Fly project¶
This creates a new Fly app without deploying immediately.2.2 Create and attach Fly Postgres¶
# Create postgres (choose Development configuration for testing)
fly postgres create --name your-app-db --region yyz
# Note the connection details from the output, you'll need the password
2.3 Set secrets¶
# Set authentication secrets
fly secrets set JWT_SECRET_KEY=$(openssl rand -hex 32)
fly secrets set BASIC_AUTH_USER=admin BASIC_AUTH_PASSWORD=your-secure-password
# Set database URL (CRITICAL: use postgresql:// not postgres://)
fly secrets set DATABASE_URL="postgresql://postgres:YOUR_PASSWORD@your-app-db.flycast:5432/postgres"
β οΈ Important: Always use postgresql://
scheme, not postgres://
. The latter causes SQLAlchemy dialect loading errors.
2.4 Deploy the app¶
3 - Containerfile Requirements¶
Ensure your Containerfile explicitly installs PostgreSQL dependencies:
# Create virtual environment, upgrade pip and install dependencies
RUN python3 -m venv /app/.venv && \
/app/.venv/bin/python3 -m pip install --upgrade pip setuptools pdm uv && \
/app/.venv/bin/python3 -m pip install psycopg2-binary && \
/app/.venv/bin/python3 -m uv pip install ".[redis]"
The explicit psycopg2-binary
installation is required because uv may not properly install optional dependencies.
4 - fly.toml Configuration¶
Your fly.toml
should look like this:
app = "your-app-name"
primary_region = "yyz"
[build]
dockerfile = "Containerfile"
[env]
HOST = "0.0.0.0"
PORT = "4444"
[http_service]
internal_port = 4444
force_https = true
auto_stop_machines = "stop"
auto_start_machines = true
min_machines_running = 0
processes = ["app"]
[[vm]]
memory = "1gb"
cpu_kind = "shared"
cpus = 1
Note: Don't put secrets like DATABASE_URL
in fly.toml
- use fly secrets set
instead.
5 - Testing Your Deployment¶
5.1 Check app status¶
5.2 Test endpoints¶
# Health check (no auth required)
curl https://your-app-name.fly.dev/health
# Protected endpoints (require auth)
curl -u admin:your-password https://your-app-name.fly.dev/docs
curl -u admin:your-password https://your-app-name.fly.dev/tools
5.3 Expected responses¶
- Health:
{"status":"healthy"}
- Protected endpoints without auth:
{"detail":"Not authenticated"}
- Protected endpoints with auth: JSON response with data
6 - Troubleshooting¶
Common Issue 1: SQLAlchemy postgres dialect error¶
Solutions: 1. Ensure psycopg2-binary
is explicitly installed in Containerfile 2. Use postgresql://
not postgres://
in DATABASE_URL 3. Rebuild with fly deploy --no-cache
Common Issue 2: Database connection refused¶
Solutions: 1. Verify DATABASE_URL format: postgresql://postgres:PASSWORD@your-db.flycast:5432/postgres
2. Check postgres app is running: fly status -a your-app-db
3. Verify password matches postgres creation output
Common Issue 3: Machines not updating¶
Solutions:
# Force machine updates
fly machine list
fly machine update MACHINE_ID --image your-new-image
# Or restart all machines
fly scale count 0
fly scale count 1
7 - Production Considerations¶
Security¶
- Change default
BASIC_AUTH_PASSWORD
to a strong password - Consider using JWT tokens for API access
- Enable Fly's private networking for database connections
Scaling¶
# Scale to multiple machines for HA
fly scale count 2
# Scale machine resources
fly scale memory 2gb
Monitoring¶
8 - Clean Deployment Script¶
For a completely fresh deployment:
#!/bin/bash
set -e
APP_NAME="your-app-name"
DB_NAME="${APP_NAME}-db"
REGION="yyz"
PASSWORD=$(openssl rand -base64 32)
echo "π Deploying MCP Gateway to Fly.io..."
# Create app
fly launch --name $APP_NAME --no-deploy --region $REGION
# Create postgres
fly postgres create --name $DB_NAME --region $REGION
# Set secrets
fly secrets set JWT_SECRET_KEY=$(openssl rand -hex 32)
fly secrets set BASIC_AUTH_USER=admin
fly secrets set BASIC_AUTH_PASSWORD=$PASSWORD
# Get postgres password and set DATABASE_URL
echo "β οΈ Set your DATABASE_URL manually with the postgres password:"
echo "fly secrets set DATABASE_URL=\"postgresql://postgres:YOUR_PG_PASSWORD@${DB_NAME}.flycast:5432/postgres\""
# Deploy
echo "ποΈ Ready to deploy. Run: fly deploy"
9 - Additional Resources¶
Success indicators: - β
fly status
shows machines as "started" - β
/health
endpoint returns {"status":"healthy"}
- β
Protected endpoints require authentication - β
No SQLAlchemy errors in logs