Skip to content

Fly.io Deployment

Deploy Django Keel projects globally with Fly.io's edge network.

Overview

Fly.io runs your application close to users worldwide with:

  • Global edge deployment - Deploy to 30+ regions
  • Automatic HTTPS - Free SSL certificates
  • PostgreSQL included - Managed Postgres clusters
  • Redis included - Managed Redis
  • Free tier - Generous free allowance
  • Near-instant deploys - Deploy in seconds

Prerequisites

  • Fly.io account (free tier available)
  • Fly CLI installed
  • flyio selected in deployment_targets during generation

What's Generated

A ready-to-use fly.toml at the project root, plus helper scripts in deploy/flyio/:

  • deploy.sh - deployment automation
  • setup_env.sh - interactive secrets setup (walks you through fly secrets set)
  • manage_db.sh - database management utilities

Installation

Install Fly CLI

# macOS
brew install flyctl

# Linux
curl -L https://fly.io/install.sh | sh

# Windows
powershell -Command "iwr https://fly.io/install.ps1 -useb | iex"

Authenticate

fly auth login

Quick Start

1. Launch Application

From your project directory:

fly launch

Django Keel already generated a fly.toml, so let fly launch reuse the existing configuration when prompted. Fly.io will:

  1. Prompt for app name and region
  2. Create PostgreSQL database (optional)
  3. Create Redis instance (optional)

2. Configure fly.toml

The generated fly.toml looks like this (Django Keel builds from the project's Dockerfile):

app = "your_project"
primary_region = "iad"  # Washington D.C. - change as needed

[build]

[deploy]
  release_command = "python manage.py migrate --noinput"

[env]
  DJANGO_SETTINGS_MODULE = "config.settings.prod"
  PORT = "8000"

[http_service]
  internal_port = 8000
  force_https = true
  auto_stop_machines = "stop"
  auto_start_machines = true
  min_machines_running = 1
  processes = ["app"]

  [http_service.concurrency]
    type = "connections"
    hard_limit = 25
    soft_limit = 20

[[http_service.checks]]
  grace_period = "10s"
  interval = "30s"
  method = "GET"
  timeout = "5s"
  path = "/health/"

[[vm]]
  size = "shared-cpu-1x"
  memory = "256mb"

[[statics]]
  guest_path = "/app/staticfiles"
  url_prefix = "/static/"

3. Set Secrets

The interactive deploy/flyio/setup_env.sh script can walk you through this. Manually:

# Required
fly secrets set DJANGO_SECRET_KEY=$(python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())')

fly secrets set DEBUG=False
fly secrets set DJANGO_ALLOWED_HOSTS=your-app.fly.dev

# If using AWS S3
fly secrets set AWS_ACCESS_KEY_ID=your-key
fly secrets set AWS_SECRET_ACCESS_KEY=your-secret
fly secrets set AWS_STORAGE_BUCKET_NAME=your-bucket

# If using Sentry
fly secrets set SENTRY_DSN=your-sentry-dsn

# If using Stripe
fly secrets set STRIPE_LIVE_PUBLIC_KEY=pk_live_...
fly secrets set STRIPE_LIVE_SECRET_KEY=sk_live_...
fly secrets set STRIPE_LIVE_MODE=True

4. Deploy

fly deploy

Your app will be live at https://your-app.fly.dev

Database Setup

Create PostgreSQL

fly postgres create

Options: - Name: your-app-db - Region: Same as your app - Configuration: Development (1GB RAM, 10GB disk)

Attach Database

fly postgres attach your-app-db

This sets the DATABASE_URL secret automatically.

Create Redis

fly redis create

Options: - Plan: Free (256MB) - Region: Same as your app

Attach Redis

fly redis attach your-redis

This sets the REDIS_URL secret automatically.

Multi-Region Deployment

Deploy to multiple regions for low latency worldwide:

# Add regions
fly regions add ams  # Amsterdam
fly regions add gru  # São Paulo
fly regions add syd  # Sydney

# Scale to 2 machines per region
fly scale count 2

Fly.io automatically routes users to the nearest region.

Adding Background Workers

Celery Worker and Beat

Add process groups to fly.toml so worker and beat machines run from the same image:

[processes]
  app = "gunicorn config.wsgi:application --bind 0.0.0.0:8000"
  worker = "celery -A config worker -l info"
  beat = "celery -A config beat -l info"

Make sure [http_service] keeps processes = ["app"] so only web machines receive traffic, then:

fly deploy
fly scale count app=2 worker=1 beat=1

Volumes for Persistent Storage

If not using S3:

# Create volume
fly volumes create data --size 10  # 10GB

# Update fly.toml
[[mounts]]
  source = "data"
  destination = "/data"

Update Django settings:

MEDIA_ROOT = "/data/media"

Custom Domains

Add Domain

fly certs create yourdomain.com
fly certs create www.yourdomain.com

Configure DNS

Add records to your DNS provider:

A     @     66.241.124.123  # Fly.io IP (check dashboard)
AAAA  @     2a09:8280:1::1  # IPv6
CNAME www   your-app.fly.dev

Fly.io automatically provisions SSL certificates.

Monitoring

View Logs

# Real-time logs
fly logs

# Last 100 lines
fly logs --lines 100

Metrics

# Dashboard
fly dashboard

# CLI metrics
fly status
fly vm status

Monitoring Dashboard

Access at: https://fly.io/apps/your-app/monitoring

  • Request rate
  • Response time
  • Error rate
  • CPU/Memory usage

Scaling

Vertical Scaling

# List available VM sizes
fly platform vm-sizes

# Scale to shared-cpu-2x (2 CPU, 4GB RAM)
fly scale vm shared-cpu-2x

VM Sizes: - shared-cpu-1x: 1 CPU, 256MB RAM (free tier) - shared-cpu-2x: 2 CPU, 4GB RAM - dedicated-cpu-1x: 1 vCPU, 2GB RAM - dedicated-cpu-2x: 2 vCPU, 4GB RAM

Horizontal Scaling

# Scale to 3 instances
fly scale count 3

# Scale per region
fly scale count 2 --region sjc
fly scale count 2 --region ams

Auto Start/Stop

The generated fly.toml uses Fly's machine auto start/stop:

[http_service]
  auto_stop_machines = "stop"
  auto_start_machines = true
  min_machines_running = 1

Machines stop when idle and start on incoming traffic. Raise min_machines_running to keep more machines warm.

Health Checks

Fly.io uses your /health/ endpoint (already configured in the generated fly.toml):

[[http_service.checks]]
  grace_period = "10s"
  interval = "30s"
  method = "GET"
  timeout = "5s"
  path = "/health/"

Django Keel provides this endpoint by default.

Zero-Downtime Deployments

Fly.io handles this automatically:

  1. Deploys new version
  2. Waits for health checks to pass
  3. Routes traffic to new version
  4. Terminates old version

Troubleshooting

Build Fails

Error: "No Dockerfile found"

# Fly.io expects Dockerfile
# Django Keel provides one
ls Dockerfile  # Verify it exists

Error: "requirements.txt not found"

# For uv projects, create requirements.txt
uv pip compile pyproject.toml -o requirements.txt

Database Connection

Error: "could not connect to database"

# Check DATABASE_URL is set
fly secrets list | grep DATABASE

# Test connection
fly ssh console
python manage.py dbshell

Memory Issues

Error: "OOMKilled"

# Check memory usage
fly vm status

# Scale up
fly scale vm shared-cpu-2x  # 4GB RAM

SSL Certificate Issues

Error: "certificate verification failed"

# Check certificate status
fly certs show yourdomain.com

# Recreate certificate
fly certs remove yourdomain.com
fly certs create yourdomain.com

Cost Optimization

Free Tier Limits

  • 3 shared-cpu-1x VMs (256MB RAM each)
  • 3GB PostgreSQL storage
  • 160GB outbound transfer
  • Free HTTPS certificates

Tips

  1. Use shared-cpu for dev/staging
  2. Auto-scale down during low traffic
  3. Optimize images - Reduce Docker image size
  4. Use volumes instead of S3 for small files
  5. Monitor usage - Set billing alerts

Production Checklist

  • [ ] Set DEBUG=False
  • [ ] Configure ALLOWED_HOSTS with your domain
  • [ ] Set strong DJANGO_SECRET_KEY
  • [ ] Enable Sentry for error tracking
  • [ ] Set up database backups
  • [ ] Configure custom domain with SSL
  • [ ] Test Celery workers (if using)
  • [ ] Configure auto-scaling
  • [ ] Set up monitoring alerts
  • [ ] Enable database replication for critical apps

Comparison with Other Platforms

Feature Fly.io Render Heroku
Regions 30+ 7 2 (US/EU)
Free Tier 3 VMs, 3GB DB 750 hrs/month No free tier
Auto-scaling ✅ Yes ✅ Yes ✅ Yes
SSL ✅ Free ✅ Free ✅ Free
Pricing $1.94+/month $7+/month $5-$25+/month

Best Practices

  1. Deploy to multiple regions - Low latency worldwide
  2. Use volumes for persistent data (if not using S3)
  3. Monitor health checks - Ensure endpoints respond
  4. Enable auto-scaling - Handle traffic spikes
  5. Regular backups - Snapshot PostgreSQL regularly
  6. Use Fly CLI - Faster than web dashboard
  7. Keep fly.toml in version control

Further Reading

Support