# Fly Io

> Use when deploying to Fly.io or configuring flyctl, fly.toml, Fly.io secrets, databases, or GitHub Actions.

- Skill: `fellowship-dev/fly-io` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add fellowship-dev/fly-io`
- Raw SKILL.md: https://api.skillmd.com/api/skills/fellowship-dev/fly-io/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: fellowship-dev (https://skillmd.com/u/fellowship-dev)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/fellowship-dev/fly-io

---


# Fly.io

This skill provides comprehensive guidance for deploying applications to Fly.io, including initial setup, database management, CI/CD integration, and production operations.

## When to Use

This skill activates automatically when working with:

- Fly.io-specific tasks ("fly.io", "deploy to fly", "fly.io setup", "flyctl")
- Fly.io configuration ("fly.toml", "fly app", "fly launch")
- Fly.io database operations ("fly postgres", "fly pg")
- Fly.io environment management ("fly secrets", "fly env")
- Fly.io monitoring ("fly logs", "fly status", "fly dashboard")
- Fly.io GitHub Actions ("fly deploy github action", "flyctl in CI/CD")

## Core Principles

1. **Security First**: Always encrypt sensitive data, never commit unencrypted secrets
2. **Database Safety**: Use proxying for database access, maintain regular backups
3. **Environment Isolation**: Separate development, staging, and production configurations
4. **Cost Awareness**: Optimize resource allocation, monitor usage regularly
5. **Automation**: Leverage CI/CD for consistent, repeatable deployments
6. **Monitoring**: Implement comprehensive logging and health checks

## Prerequisites

Before beginning Fly.io deployment:

1. **Fly.io Account**: Active account with credit card attached (required for PostgreSQL)
2. **CLI Installation**: `flyctl` command-line tool installed and authenticated
3. **Environment Variables**: Project-specific configuration ready
4. **Application Ready**: Application configured for production deployment

## Instructions

### 1. Initial Setup and Authentication

**Install Fly.io CLI:**

```bash
# Standard installation
curl -L https://fly.io/install.sh | sh

# Add to PATH (adjust based on your environment)
export FLYCTL_INSTALL="/home/user/.fly"
export PATH="$FLYCTL_INSTALL/bin:$PATH"

# Verify installation
flyctl version
```

**Authenticate with Fly.io:**

```bash
flyctl auth login
```

This opens a browser for authentication. After successful login, your credentials are stored locally.

### 2. Environment Variable Management

**Encryption for Security:**

Fly.io deployments should use encrypted environment files to protect sensitive data:

```bash
# Decrypt environment file (requires ENV_PASS variable)
openssl enc -aes-256-cbc -pbkdf2 -salt -d -in .env.enc -out .env -k $ENV_PASS

# After modifying .env, re-encrypt
openssl enc -aes-256-cbc -pbkdf2 -salt -in .env -out .env.enc -k $ENV_PASS

# For production environment
openssl enc -aes-256-cbc -pbkdf2 -salt -in .env -out .env.enc.prod -k $ENV_PASS
```

**IMPORTANT Security Notes:**
- Never commit unencrypted `.env` files
- Store `ENV_PASS` securely (GitHub secrets, password manager)
- Use different encryption passwords for staging/production
- Add `.env` to `.gitignore` if not already present

### 3. Database Setup (PostgreSQL)

**Create PostgreSQL Database:**

```bash
# Create new PostgreSQL database on Fly.io
flyctl postgres create

# When prompted, use naming convention: {project-name}-db
# Example: myapp-db
```

**Important database creation options:**
- Choose region closest to your users
- Select appropriate resource tier (start small, scale up)
- Note the database credentials provided
- Database name typically matches app name + "-db" suffix

**Attach Database to Application:**

```bash
# Attach database to your application
flyctl pg attach {project-name}-db -a {project-name}

# If attachment fails, set DATABASE_URL manually
flyctl secrets set DATABASE_URL=postgres://user:pass@host:5432/dbname -a {project-name}
```

**Verify database connection:**

```bash
# Check database status
flyctl status -a {project-name}-db

# Test connection via proxy
flyctl proxy 5433 -a {project-name}-db
# In another terminal: psql -h localhost -p 5433 -U {username} -d {dbname}
```

### 4. Application Creation and Configuration

**Launch New Application:**

```bash
# Create application (don't deploy yet)
flyctl launch

# Follow prompts:
# - Choose app name (matches ENV_NAME variable)
# - Select region (same as database for best performance)
# - Choose resource allocation
# - Decline immediate deployment (configure first)
```

**Configure Application:**

After `flyctl launch`, review and customize `fly.toml`:

```toml
app = "your-app-name"

[build]
  # Adjust based on your application type

[env]
  PORT = "8080"
  HOST = "::"
  # Add other non-secret environment variables

[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0
```

**Set Secrets:**

```bash
# Set individual secrets
flyctl secrets set APP_KEYS="your-app-keys" -a {project-name}
flyctl secrets set API_TOKEN_SALT="your-salt" -a {project-name}

# Import from .env file (ensure it's decrypted first)
flyctl secrets import -a {project-name} < .env
```

### 5. Deployment Workflow

**Pre-Deployment Checklist:**

- [ ] Environment variables configured (production values)
- [ ] Database created and attached
- [ ] `fly.toml` reviewed and customized
- [ ] Secrets set via `flyctl secrets`
- [ ] Application builds successfully locally
- [ ] Dependencies are production-ready

**Initial Deployment:**

```bash
# Deploy application
flyctl deploy -a {project-name}

# Monitor deployment
flyctl logs -a {project-name}

# Check status
flyctl status -a {project-name}
```

**Post-Deployment Verification:**

```bash
# Open application in browser
flyctl open -a {project-name}

# Check health endpoint
curl https://{project-name}.fly.dev/health

# Review logs for errors
flyctl logs -a {project-name}
```

### 6. Domain and SSL Configuration

**Add Custom Domain:**

```bash
# Add certificate for custom domain
flyctl certs create yourdomain.com -a {project-name}

# Check certificate status
flyctl certs show yourdomain.com -a {project-name}
```

**DNS Configuration:**

1. Point your domain's A/AAAA records to Fly.io
2. Wait for DNS propagation
3. Verify certificate issuance
4. Update `APP_URL` environment variable to custom domain
5. Re-deploy with updated configuration

### 7. Database Management

**PostgreSQL Dump (Backup):**

```bash
# Standard dump format (custom format, compressed)
pg_dump -Fc --no-acl --no-owner -h HOST -U USERNAME -d DATABASE_NAME > dump_file.dump

# Example for local database
pg_dump -Fc --no-acl --no-owner -h localhost -U postgres -d strapi > backup.dump
```

**PostgreSQL Restore:**

```bash
# Standard restore
pg_restore --verbose --clean --no-acl --no-owner -h HOST -p PORT -U USERNAME -d "DATABASE_NAME" dump_file.dump

# Example for local database
pg_restore --verbose --clean --no-acl --no-owner -h localhost -U postgres -d strapi backup.dump
```

**Upload Local Database to Fly.io:**

```bash
# 1. Export local database
pg_dump -Fc --no-acl --no-owner -h localhost -U postgres -d {dbname} > latest.dump

# 2. Proxy production database (run in separate terminal)
flyctl proxy 5433 -a {project-name}-db

# 3. In another terminal, restore to production
DB_USER={check_fly_dashboard}  # Get from Fly.io dashboard
DB_NAME={check_fly_dashboard}   # Get from Fly.io dashboard
pg_restore --verbose --clean --no-acl --no-owner -h localhost -p 5433 -U $DB_USER -d "$DB_NAME" latest.dump

# 4. Clean up
rm latest.dump
```

**Download Production Database from Fly.io:**

```bash
# 1. Proxy production database (run in separate terminal)
flyctl proxy 5433 -a {project-name}-db

# 2. In another terminal, dump production database
DB_USER={check_fly_dashboard}
DB_NAME={check_fly_dashboard}
pg_dump -Fc --no-acl --no-owner -h localhost -p 5433 -U $DB_USER -d "$DB_NAME" > production.dump

# 3. Restore to local database
pg_restore --verbose --clean --no-acl --no-owner -h localhost -U postgres -d {dbname} production.dump

# 4. Clean up
rm production.dump
```

**Database Proxy for Direct Access:**

```bash
# Proxy database to local port
flyctl proxy 5433 -a {project-name}-db

# Connect with psql
psql -h localhost -p 5433 -U {username} -d {dbname}

# Connect with GUI tools (PgAdmin, DBeaver, etc.)
# Host: localhost
# Port: 5433
# User: {from Fly.io dashboard}
# Database: {from Fly.io dashboard}
```

### 8. GitHub Actions CI/CD Integration

**Setup Continuous Deployment:**

1. Generate Fly.io API token:
   ```bash
   flyctl auth token
   ```

2. Add GitHub repository secrets:
   - `FLY_API_TOKEN`: Your Fly.io API token
   - `ENV_PASS`: Environment encryption password (if using encrypted .env)

3. Create `.github/workflows/deploy.yml`:

```yaml
name: Deploy to Fly.io

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Fly.io CLI
        uses: superfly/flyctl-actions/setup-flyctl@master

      - name: Decrypt environment variables
        run: |
          openssl enc -aes-256-cbc -pbkdf2 -salt -d \
            -in .env.enc.prod -out .env -k ${{ secrets.ENV_PASS }}

      - name: Deploy to Fly.io
        run: flyctl deploy --remote-only
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
```

**Best Practices for CI/CD:**
- Use `--remote-only` flag to build on Fly.io servers
- Separate staging and production workflows
- Implement smoke tests after deployment
- Configure Slack/Discord notifications for deployment status
- Use GitHub environments for approval gates on production deploys

### 9. Monitoring and Troubleshooting

**Essential Monitoring Commands:**

```bash
# Check application status
flyctl status -a {project-name}

# View real-time logs
flyctl logs -a {project-name}

# View logs with filtering
flyctl logs -a {project-name} --region iad

# Check database status
flyctl status -a {project-name}-db

# View metrics
flyctl metrics -a {project-name}

# SSH into application
flyctl ssh console -a {project-name}
```

**Common Issues and Solutions:**

1. **Port Configuration Problems:**
   - Verify `fly.toml` internal_port matches application PORT
   - Ensure HOST is set to "::" for IPv6 compatibility
   - Check APP_URL matches deployed domain/URL

2. **Database Connection Failures:**
   - Verify DATABASE_URL format and credentials
   - Check database is in same region as app
   - Test connection via proxy: `flyctl proxy 5433 -a {project-name}-db`
   - Review database logs: `flyctl logs -a {project-name}-db`

3. **Environment Variable Issues:**
   - Verify secrets are set: `flyctl secrets list -a {project-name}`
   - Check production values in .env before encryption
   - Ensure ENV_PASS is correct when decrypting
   - Re-import secrets after changes: `flyctl secrets import`

4. **Deployment Failures:**
   - Review build logs: `flyctl logs -a {project-name}`
   - Check Dockerfile or buildpack configuration
   - Verify all dependencies in package.json/requirements.txt
   - Test build locally before deploying
   - Increase build timeout if needed

5. **Application Crashes:**
   - Check logs for error messages: `flyctl logs -a {project-name}`
   - Verify health check endpoint is responding
   - Review resource allocation (memory, CPU)
   - Check for database connection issues
   - SSH into container to debug: `flyctl ssh console -a {project-name}`

### 10. Scaling and Performance

**Vertical Scaling (Resource Allocation):**

```bash
# Scale VM resources
flyctl scale vm shared-cpu-1x --memory 512 -a {project-name}

# Available VM sizes:
# shared-cpu-1x (256MB, 512MB, 1GB, 2GB)
# dedicated-cpu-1x (2GB, 4GB, 8GB)
# dedicated-cpu-2x (4GB, 8GB, 16GB)
```

**Horizontal Scaling (Machine Count):**

```bash
# Scale number of machines
flyctl scale count 2 -a {project-name}

# Auto-scaling configuration in fly.toml:
[http_service]
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 0  # Scale to zero when idle
```

**Regional Deployment:**

```bash
# Add machines in multiple regions
flyctl regions add iad lhr syd -a {project-name}

# View current regions
flyctl regions list -a {project-name}

# Remove regions
flyctl regions remove lhr -a {project-name}
```

### 11. Cost Optimization

**Strategies for Reducing Costs:**

1. **Auto-stop/Auto-start**: Enable in fly.toml to scale to zero during idle periods
2. **Right-size Resources**: Start with smallest VM size, scale up only if needed
3. **Database Optimization**: Choose appropriate PostgreSQL tier, monitor usage
4. **Regional Strategy**: Deploy only in regions with active users
5. **Monitoring**: Set up billing alerts in Fly.io dashboard

**Cost Monitoring:**

```bash
# View current usage and costs
flyctl dashboard

# Check resource allocation
flyctl status -a {project-name}
flyctl status -a {project-name}-db
```

## Supporting Files

This skill includes supporting templates:

- **fly.toml.template**: Basic Fly.io configuration template
- **deployment-checklist.md**: Pre-deployment verification checklist
- **github-actions-workflow.yml**: Sample CI/CD workflow

Access these via: `.claude/skills/flyio-deployment/templates/`

## Common Deployment Patterns

### Pattern 1: Initial Production Deployment

```bash
# 1. Setup and authenticate
flyctl auth login

# 2. Create database
flyctl postgres create  # Use {project-name}-db

# 3. Launch application
flyctl launch  # Don't deploy yet

# 4. Attach database
flyctl pg attach {project-name}-db -a {project-name}

# 5. Set secrets
flyctl secrets import -a {project-name} < .env

# 6. Deploy
flyctl deploy -a {project-name}

# 7. Verify
flyctl open -a {project-name}
flyctl logs -a {project-name}
```

### Pattern 2: Database Migration

```bash
# Export from source
pg_dump -Fc --no-acl --no-owner -h {source-host} -U {user} -d {db} > migration.dump

# Proxy Fly.io database
flyctl proxy 5433 -a {project-name}-db  # Separate terminal

# Import to Fly.io
pg_restore --verbose --clean --no-acl --no-owner \
  -h localhost -p 5433 -U {user} -d {db} migration.dump

# Verify
flyctl ssh console -a {project-name}
# Inside container: run database query to verify data
```

### Pattern 3: Rollback Deployment

```bash
# List recent releases
flyctl releases -a {project-name}

# Rollback to previous version
flyctl releases rollback -a {project-name}

# Or rollback to specific version
flyctl releases rollback {version-number} -a {project-name}

# Monitor rollback
flyctl logs -a {project-name}
```

## Security Best Practices

1. **Environment Encryption**: Always encrypt .env files before committing
2. **Database Access**: Use database proxying instead of exposing publicly
3. **API Tokens**: Store Fly.io API tokens in GitHub secrets, never in code
4. **SSL Certificates**: Always enable force_https in fly.toml
5. **Database Backups**: Implement automated backup strategy (S3, external storage)
6. **Secrets Rotation**: Regularly rotate API tokens, database passwords, encryption keys
7. **Access Control**: Use Fly.io organizations for team access management
8. **Network Security**: Configure firewall rules, restrict database access

## Validation Checklist

Before considering deployment complete:

- [ ] Application accessible via public URL
- [ ] Health check endpoint responding correctly
- [ ] Database connection working
- [ ] All environment variables set correctly
- [ ] SSL certificate issued and active
- [ ] Logs showing no errors
- [ ] CI/CD pipeline configured and tested
- [ ] Database backups configured
- [ ] Monitoring and alerting set up
- [ ] Documentation updated with deployment URLs and procedures

## Real-World Gotchas (learned the hard way)

### High-Risk Account Lock
Fly.io flags dormant accounts as "high risk". Creating apps/DBs fails with:
> Your account has been marked as high risk. Please go to https://fly.io/high-risk-unlock

**Fix**: Must visit the URL in a browser logged into the Fly account. CLI cannot bypass this.

### Auto-Stop Is Aggressive
With `auto_stop_machines = "stop"` and `min_machines_running = 0`, machines stop after ~60s idle. Some frameworks take 5-10s to cold boot. This means:
- First request after idle gets a slow response (wake + boot)
- Rapid sequential API calls may hit a stopped machine between calls
- **Always hit `/_health` and wait for 204 before running batch operations**

### Fly Creates 2 Machines by Default
Even with `min_machines_running = 0`, `fly deploy` creates 2 machines for "HA". For dev/staging, destroy the second one:
```bash
fly status --app <app>  # Find both machine IDs
fly machine stop <second-id> --app <app>
fly machine destroy <second-id> --app <app> --force
```

### DATABASE_SSL Must Be False on Internal Network
`fly postgres attach` sets `DATABASE_URL` with `sslmode=disable`. If your app has a `DATABASE_SSL` env var, set it to `false`:
```toml
[env]
  DATABASE_SSL = "false"
```

### DNS + Cert Setup (Cloudflare)
When using Cloudflare for DNS:
1. **Records must be unproxied** (`proxied=false`) — Fly needs direct traffic for TLS termination
2. **Check for stale records** — old deployments may have left proxied A/AAAA records. Delete them first.
3. **ACME CNAME** — `fly certs setup` gives you a `_acme-challenge` CNAME target. Create/update it for Let's Encrypt validation.
4. Cert issuance is fast (~30s) once DNS is correct.

### Deploy Flag: No `--region`
Use `--primary-region` or set `primary_region` in `fly.toml`. The flag `--region` does not exist on `fly deploy`.

### Bash Token Piping Bug
Long API tokens (256+ chars) cause empty stdout when piped through bash variable expansion + curl. Workarounds:
- Store token in a file: `echo -n "TOKEN" > /tmp/.token && TK=$(cat /tmp/.token)`
- Use curl with `-o file` instead of piping
- Use Python `urllib.request` (most reliable in sandbox environments)

## Additional Resources

- [Fly.io Official Documentation](https://fly.io/docs/)
- [Fly.io CLI Reference](https://fly.io/docs/flyctl/)
- [Fly.io Pricing](https://fly.io/docs/about/pricing/)
- [Fly.io Community Forum](https://community.fly.io/)
- [GitHub Actions Integration](https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/)

## Notes

- Always test deployments in staging environment before production
- Keep flyctl CLI updated: `flyctl version update`
- Monitor Fly.io status page for platform incidents
- Join Fly.io community for support and best practices
- Consider using Fly.io's managed PostgreSQL for production workloads

