Complete Fly.io Deployment Guide for Rails App
A beginner-friendly, step-by-step guide to deploying Ruby on Rails applications to Fly.io with Supabase database, custom domain, and email setup.
Table of Contents
- Prerequisites
- Part 1: Install Fly CLI
- Part 2: Set Up Supabase Database
- Part 3: Deploy to Fly.io (Testing)
- Part 4: Create Admin User
- Part 5: Deploy to Production
- Part 6: Buy and Connect Domain
- Part 7: Set Up Email Forwarding
- Part 8: Managing Your App
- Troubleshooting
Prerequisites
Before starting, ensure you have:
- ✅ A Rails application ready to deploy
- ✅ Git installed and your code committed
- ✅ AWS S3 bucket set up for file storage (or similar)
- ✅ Terminal/Command line access
- ✅ Credit/debit card for domain purchase (~$10/year)
Cost Estimate:
- Fly.io hosting: $5-10/month
- Supabase database: FREE
- Domain name: ~$10/year
- Email: FREE (forwarding)
- Total: ~$70-130/year
Part 1: Install Fly CLI
Step 1: Install Fly.io Command Line Tool
For macOS/Linux:
curl -L https://fly.io/install.sh | sh
For Windows (PowerShell):
powershell -Command "iwr https://fly.io/install.ps1 -useb | iex"
Step 2: Sign Up / Login
# Sign up for new account
fly auth signup
# Or login if you have an account
fly auth login
Follow the browser prompts to complete authentication.
Step 3: Verify Installation
fly version
You should see the Fly CLI version number.
Part 2: Set Up Supabase Database
Why Supabase?
- ✅ FREE tier (500MB database)
- ✅ Automatic backups
- ✅ Great dashboard
- ✅ Saves $38/month vs Fly Postgres
Step 1: Create Supabase Account
- Go to: https://supabase.com
- Click "Start your project"
- Sign up with GitHub, Google, or Email
Step 2: Create Organization (First Time)
- Enter organization name (e.g., "My Company")
- Click "Create organization"
Step 3: Create New Project
Fill in the form:
-
Name:
your-app-name-prod
(e.g., name-website-prod) -
Database Password: Click refresh icon to generate
- IMPORTANT: Copy and save this password!
- Example:
Kx9mP#vL2nQ8wR5t
-
Region: Choose closest to your users
- Southeast Asia (Singapore)
- West EU (Ireland)
- East US (North Virginia)
- Pricing Plan: FREE (already selected)
Click "Create new project" and wait 1-2 minutes.
Step 4: Get Database Connection String
- Click ⚙️ Settings (left sidebar)
- Click "Database"
- Scroll to "Connection string"
- Click "URI" tab
- Copy the connection string
- Replace
[YOUR-PASSWORD]
with your actual password from Step 3
Example final URL:
postgresql://postgres.abcdefghijklmno:Kx9mP#vL2nQ8wR5t@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres
Save this URL - you'll need it soon!
Part 3: Deploy to Fly.io (Testing)
Step 1: Navigate to Your Project
cd /path/to/your-rails-app
Step 2: Initialize Fly.io App
fly launch --no-deploy
You'll be asked several questions:
Question 1: Copy existing fly.toml?
Answer: N
(No) - Start fresh
Question 2: App name
Enter: your-app-test
(e.g., name-website-test)
Question 3: Organization
Select: Personal (or your organization)
Question 4: Region
Select: Closest to you or your users (e.g., sin
for Singapore)
Question 5: Tweak settings?
Answer: Y
(Yes)
Step 3: Configure Settings in Browser
A browser window opens with configuration form:
Fill in:
-
App name:
your-app-test
(already filled) - Organization: Personal (already selected)
- Region: Keep your selection
-
Internal port:
8080
-
VM Memory:
2GB
(select from dropdown) -
Postgres Provider: Select
none
(we're using Supabase) -
Tigris Object Storage: Select
Disabled
(using S3) -
Redis: Keep
none
-
Sentry: Keep
Disabled
Click "Confirm Settings"
Step 4: Set Environment Secrets
Back in your terminal, set your secrets:
Generate SECRET_KEY_BASE:
rails secret
Copy the output.
Set Database URL (from Supabase):
fly secrets set DATABASE_URL="postgresql://postgres.abc:yourpassword@aws-0-region.pooler.supabase.com:6543/postgres" --app your-app-test
Set Rails Secret:
fly secrets set SECRET_KEY_BASE="paste-secret-from-rails-secret-command" --app your-app-test
Set AWS S3 Credentials (if using S3):
fly secrets set AWS_ACCESS_KEY_ID="your-aws-key" --app your-app-test
fly secrets set AWS_SECRET_ACCESS_KEY="your-aws-secret" --app your-app-test
fly secrets set AWS_REGION="your-region" --app your-app-test
fly secrets set BUCKET_NAME="your-bucket-name" --app your-app-test
fly secrets set AWS_ENDPOINT_URL_S3="your-s3-endpoint" --app your-app-test
Verify All Secrets:
fly secrets list --app your-app-test
You should see all your secrets listed (shows digests, not actual values).
Step 5: Update fly.toml Configuration
Open fly.toml
in your editor and ensure it looks like this:
app = 'your-app-test'
primary_region = 'sin'
console_command = '/rails/bin/rails console'
[build]
[env]
PORT = '8080'
RAILS_ENV = 'production'
RAILS_LOG_TO_STDOUT = 'true'
[processes]
app = './bin/rake litestream:run ./bin/rails server'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[http_service.checks]]
interval = '10s'
timeout = '2s'
grace_period = '5s'
method = 'GET'
path = '/up'
protocol = 'http'
tls_skip_verify = false
[http_service.checks.headers]
X-Forwarded-Proto = 'https'
[[vm]]
memory = '2gb'
cpu_kind = 'shared'
cpus = 1
Step 6: Deploy Your App
fly deploy --app your-app-test
Wait 3-5 minutes for the build and deployment to complete.
Watch logs in another terminal:
fly logs --app your-app-test
Step 7: Run Database Migrations
fly ssh console --app your-app-test -C "rails db:migrate"
Step 8: Test Your App
fly open --app your-app-test
Your app opens at: https://your-app-test.fly.dev
✅ Test Environment is Live!
Part 4: Create Admin User
Step 1: SSH into Your App
fly ssh console --app your-app-test
Step 2: Start Rails Console
./bin/rails console
Or:
bundle exec rails console
Step 3: Create Admin
Admin.create!(
email: 'admin@example.com',
password: 'TestPassword123!',
password_confirmation: 'TestPassword123!'
)
Note: Replace Admin
with User
if your model is called User. Adjust fields based on your model.
Step 4: Verify Admin Created
Admin.last
Should show your newly created admin.
Step 5: Exit
exit # Exit Rails console
exit # Exit SSH
Alternative: One-Line Command
From your local terminal:
fly ssh console --app your-app-test -C "./bin/rails runner \"Admin.create!(email: 'admin@example.com', password: 'TestPassword123!', password_confirmation: 'TestPassword123!')\""
Step 6: Test Admin Login
- Visit your app:
https://your-app-test.fly.dev/admin
- Login with credentials you just created
- Verify everything works ✅
Part 5: Deploy to Production
Now that testing works, let's create a production environment.
Step 1: Create Production App
fly launch --no-deploy
Configuration:
- App name:
your-app-prod
- Region: Same as test or closer to users
- Tweak settings:
Y
(Yes)
Step 2: Configure Production Settings
In the browser form:
-
App name:
your-app-prod
- Region: Choose production region
-
Internal port:
8080
-
VM Memory:
2GB
(more for production) -
Postgres:
none
(using Supabase) -
Tigris:
Disabled
Click "Confirm Settings"
Step 3: Create Production Supabase Database
Option A: Use Same Database
- Use the same Supabase database for production
- Good for small projects
- Cost: FREE
Option B: Create Separate Database (Recommended)
- Go to Supabase dashboard
- Create new project:
your-app-prod
- Get new DATABASE_URL
- Keep test and production data separate ✅
Step 4: Set Production Secrets
# Generate NEW secret for production
rails secret
# Set database URL
fly secrets set DATABASE_URL="your-production-supabase-url" --app your-app-prod
# Set NEW secret key base
fly secrets set SECRET_KEY_BASE="new-secret-from-rails-secret" --app your-app-prod
# Copy AWS credentials (or use different production bucket)
fly secrets set AWS_ACCESS_KEY_ID="your-aws-key" --app your-app-prod
fly secrets set AWS_SECRET_ACCESS_KEY="your-aws-secret" --app your-app-prod
fly secrets set AWS_REGION="your-region" --app your-app-prod
fly secrets set BUCKET_NAME="your-production-bucket" --app your-app-prod
fly secrets set AWS_ENDPOINT_URL_S3="your-s3-endpoint" --app your-app-prod
# Verify
fly secrets list --app your-app-prod
Step 5: Update Production fly.toml
Open fly.toml
and update:
app = 'your-app-prod'
primary_region = 'sin'
[env]
PORT = '8080'
RAILS_ENV = 'production'
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = 'suspend'
auto_start_machines = true
min_machines_running = 1 # Keep 1 always running in production
[[vm]]
memory = '2gb'
cpu_kind = 'shared'
cpus = 2 # More CPU for production
Step 6: Deploy Production
fly deploy --app your-app-prod
Wait for deployment to complete.
Step 7: Run Migrations
fly ssh console --app your-app-prod -C "rails db:migrate"
Step 8: Create Production Admin
fly ssh console --app your-app-prod -C "./bin/rails runner \"Admin.create!(email: 'admin@yourdomain.com', password: 'StrongProductionPassword123!', password_confirmation: 'StrongProductionPassword123!')\""
Save these credentials securely!
Step 9: Test Production App
fly open --app your-app-prod
Visit: https://your-app-prod.fly.dev
✅ Production is Live!
Part 6: Buy and Connect Domain
Step 1: Buy Domain from Namecheap
Why Namecheap?
- ✅ Cheapest overall (~$10/year)
- ✅ FREE privacy protection forever
- ✅ FREE email forwarding
- ✅ Easy DNS management
- ✅ Great support
Purchase Process:
- Go to: https://www.namecheap.com
- Search for your desired domain
- Add to cart
- At checkout:
- ✅ Check: WhoisGuard (Privacy Protection) - FREE
- ❌ Uncheck: PremiumDNS
- ❌ Uncheck: Email hosting
- ❌ Uncheck: Website builder
- ❌ Uncheck: SSL certificate
- Complete purchase (~$10-13)
Step 2: Get Fly.io IP Addresses
# Get your production app's IPs
fly ips list --app your-app-prod
Output shows:
VERSION IP TYPE REGION
v4 66.241.125.202 public global
v6 2a09:8280:1::a0:a7a4:0 public global
Copy both IPs - you'll need them next!
Step 3: Add Domain to Fly.io
# Add main domain
fly certs add yourdomain.com --app your-app-prod
# Add www subdomain
fly certs add www.yourdomain.com --app your-app-prod
Example:
fly certs add name.com --app name-website-prod
fly certs add www.noblmed.com --app name-website-prod
Step 4: Configure DNS in Namecheap
Login to Namecheap:
- Go to: https://www.namecheap.com
- Click "Sign In"
- Enter credentials
Go to Domain Management:
- Click "Domain List" (left sidebar)
- Find your domain
- Click "Manage" button
Open Advanced DNS:
- Click "Advanced DNS" tab
Delete Old Records:
Look for and DELETE any existing:
- A Records with @ or blank host
- CNAME Records with @ or blank host
- Parking page records
Add New DNS Records:
Record 1 - A Record (IPv4):
Type: A Record
Host: @
Value: 66.241.125.202 (your IPv4 from Step 2)
TTL: Automatic
Click ✅ to save.
Record 2 - AAAA Record (IPv6):
Type: AAAA Record
Host: @
Value: 2a09:8280:1::a0:a7a4:0 (your IPv6 from Step 2)
TTL: Automatic
Click ✅ to save.
Record 3 - CNAME Record (WWW):
Type: CNAME Record
Host: www
Target: your-app-prod.fly.dev. (note the dot at end!)
TTL: Automatic
Click ✅ to save.
Save All Changes:
Scroll to bottom and click "SAVE ALL CHANGES"
Step 5: Wait for DNS Propagation
Wait 10-30 minutes for DNS changes to spread worldwide.
Check DNS Status:
# Check main domain
dig yourdomain.com
# Check www subdomain
dig www.yourdomain.com
Or check online: https://www.whatsmydns.net/
Step 6: Verify SSL Certificates
After 15-20 minutes:
# Check main domain certificate
fly certs show yourdomain.com --app your-app-prod
# Check www certificate
fly certs show www.yourdomain.com --app your-app-prod
When ready, you'll see:
The certificate for yourdomain.com has been issued.
Status: Ready ✅
Step 7: Test Your Domain
Open browser and visit:
Both should:
- ✅ Load your website
- ✅ Show 🔒 padlock (secure HTTPS)
- ✅ Work perfectly!
🎉 Your Custom Domain is Live!
Part 7: Set Up Email Forwarding
Step 1: Enable Email Forwarding
In Namecheap (still on your domain's management page):
- Look for "Mail Settings" or "Email Forwarding" section
- Click "Add Forwarder" or "Enable Email Forwarding"
Step 2: Add Email Forwards
Create forwarding rules:
Example forwards:
How to add each:
- Click "Add Forwarder"
-
Alias: Enter
admin
(without @domain) - Forward to: Enter your Gmail
- Click "Add" or ✅
- Repeat for other addresses
Step 3: Save Email Settings
Click "Save Changes"
Step 4: Test Email Forwarding
- Send test email to: admin@yourdomain.com
- Check your Gmail inbox
- Should receive within 1-2 minutes ✅
Step 5: Set Up Gmail "Send As" (Optional)
To reply from admin@yourdomain.com:
- Open Gmail
- Click ⚙️ Settings → "See all settings"
- Click "Accounts and Import" tab
- Find "Send mail as:"
- Click "Add another email address"
- Enter:
- Name: Your Name / Company
- Email: admin@yourdomain.com
- Uncheck "Treat as an alias"
- Click "Next Step"
- For SMTP, you need paid email service (Google Workspace, Zoho, etc.)
Note: Free forwarding only receives emails. To send, you need:
- Google Workspace ($6/month)
- Zoho Mail (free for up to 5 users)
- Or reply from your Gmail (recipient sees Gmail address)
Part 8: Managing Your App
View App Status
# Check if app is running
fly status --app your-app-prod
# List all machines
fly machine list --app your-app-prod
# View recent logs
fly logs --app your-app-prod
Stop App (When Not in Use)
To save costs when not needed:
# Stop all machines
fly scale count 0 --app your-app-prod
What happens:
- ✅ App stops completely
- ✅ No compute charges (only storage)
- ✅ Visitors see "503 Service Unavailable"
- ✅ Database and files remain safe
Start App Again
# Start 1 machine
fly scale count 1 --app your-app-prod
What happens:
- ✅ App starts in 10-30 seconds
- ✅ Website works normally
- ✅ Everything restored
Auto-Stop Configuration
Edit fly.toml
to automatically stop when idle:
[http_service]
auto_stop_machines = 'stop' # Auto-stop when no traffic
auto_start_machines = true # Auto-start when visitor arrives
min_machines_running = 0 # Allow stopping completely
Benefits:
- ✅ Automatically stops after 5 minutes of no traffic
- ✅ Automatically starts when someone visits
- ✅ Saves money on low-traffic sites
- ⚠️ First visitor waits ~10 seconds for startup
For always-on production:
[http_service]
auto_stop_machines = false
auto_start_machines = true
min_machines_running = 1 # Always keep 1 running
Restart App
fly apps restart your-app-prod
View Dashboard
fly dashboard --app your-app-prod
Opens web dashboard in browser.
SSH into App
fly ssh console --app your-app-prod
Run Rails Console
fly ssh console --app your-app-prod -C "./bin/rails console"
Update App
After making code changes:
# Commit changes to git
git add .
git commit -m "Your changes"
# Deploy update
fly deploy --app your-app-prod
# Watch deployment
fly logs --app your-app-prod
Scale Resources
# Add more memory
fly scale memory 4096 --app your-app-prod
# Add more machines
fly scale count 2 --app your-app-prod
# Change VM type
fly scale vm shared-cpu-2x --app your-app-prod
View Costs
fly orgs show
Shows your current usage and estimated costs.
Troubleshooting
Issue: "Database connection failed"
Solution:
# Verify DATABASE_URL is set
fly secrets list --app your-app-prod
# Re-set if needed
fly secrets set DATABASE_URL="your-correct-url" --app your-app-prod
# Restart app
fly apps restart your-app-prod
Issue: "DNS not resolving"
Solution:
- Check DNS records in Namecheap
- Verify IPs match:
fly ips list --app your-app-prod
- Wait 30 more minutes for propagation
- Clear browser cache or try incognito
Issue: "SSL certificate not issued"
Solution:
# Check certificate status
fly certs show yourdomain.com --app your-app-prod
# If stuck, remove and re-add
fly certs remove yourdomain.com --app your-app-prod
fly certs add yourdomain.com --app your-app-prod
# Wait 15 minutes
Issue: "App not starting"
Solution:
# Check logs for errors
fly logs --app your-app-prod
# Common issues:
# - Missing SECRET_KEY_BASE
# - Wrong DATABASE_URL
# - Port configuration (should be 8080)
# Verify secrets
fly secrets list --app your-app-prod
# Check fly.toml port settings
cat fly.toml | grep port
Issue: "Can't create admin"
Solution:
# SSH into app
fly ssh console --app your-app-prod
# Check your model name
./bin/rails runner "puts User.column_names"
# or
./bin/rails runner "puts Admin.column_names"
# Create with correct fields
./bin/rails console
Admin.create!(email: 'test@example.com', password: 'password123', password_confirmation: 'password123')
Issue: "File uploads not working"
Solution:
# Verify S3 credentials are set
fly secrets list --app your-app-prod
# Should see:
# - AWS_ACCESS_KEY_ID
# - AWS_SECRET_ACCESS_KEY
# - AWS_REGION
# - BUCKET_NAME
# - AWS_ENDPOINT_URL_S3
# Test S3 connection in Rails console
fly ssh console --app your-app-prod -C "./bin/rails console"
# Then test upload
Issue: "Out of memory errors"
Solution:
# Check current memory
fly status --app your-app-prod
# Increase memory
fly scale memory 4096 --app your-app-prod
# Or reduce memory if overprovisioned
fly scale memory 1024 --app your-app-prod
Issue: "Emails not forwarding"
Solution:
- Check Namecheap email forwarding settings
- Verify Gmail isn't marking as spam
- Wait 30 minutes for DNS changes
- Test with different email provider
Get Help
# Fly.io community forum
# https://community.fly.io
# Fly.io documentation
# https://fly.io/docs
# View all Fly commands
fly help
# Get help for specific command
fly help deploy
Quick Command Reference
Deployment
# Initialize app
fly launch --no-deploy
# Deploy app
fly deploy --app your-app-name
# Deploy with specific fly.toml
fly deploy --app your-app-name --config fly.toml
App Management
# View status
fly status --app your-app-name
# View logs
fly logs --app your-app-name
# Restart app
fly apps restart your-app-name
# Stop app
fly scale count 0 --app your-app-name
# Start app
fly scale count 1 --app your-app-name
# Open app in browser
fly open --app your-app-name
SSH & Console
# SSH into app
fly ssh console --app your-app-name
# Run Rails console
fly ssh console --app your-app-name -C "./bin/rails console"
# Run one-off command
fly ssh console --app your-app-name -C "rails db:migrate"
Secrets
# List secrets
fly secrets list --app your-app-name
# Set secret
fly secrets set KEY="value" --app your-app-name
# Unset secret
fly secrets unset KEY --app your-app-name
Domain & SSL
# Add domain
fly certs add yourdomain.com --app your-app-name
# Check certificate
fly certs show yourdomain.com --app your-app-name
# List all certificates
fly certs list --app your-app-name
# Remove certificate
fly certs remove yourdomain.com --app your-app-name
Scaling
# Scale number of machines
fly scale count 2 --app your-app-name
# Scale memory
fly scale memory 2048 --app your-app-name
# Change VM type
fly scale vm shared-cpu-2x --app your-app-name
Database
# Run migrations
fly ssh console --app your-app-name -C "rails db:migrate"
# Create database
fly ssh console --app your-app-name -C "rails db:create"
# Seed database
fly ssh console --app your-app-name -C "rails db:seed"
# Reset database (CAREFUL!)
fly ssh console --app your-app-name -C "rails db:reset"
Cost Breakdown
Monthly Costs
Service | Cost | Notes |
---|---|---|
Fly.io Compute | $5-10/month | Depends on usage |
Supabase Database | FREE | Up to 500MB |
AWS S3 Storage | $1-5/month | Depends on files |
Domain Name | ~$1/month | $10-13/year |
Email Forwarding | FREE | Via Namecheap |
SSL Certificates | FREE | Via Let's Encrypt |
Total | $7-16/month | $84-192/year |
Ways to Save
- Use auto-stop: App stops when not in use
- Use free tier Supabase: Instead of paid Postgres
- Share S3 bucket: Use same bucket for test/prod
- Buy domain for multiple years: Slight discount
- Use email forwarding: Instead of Google Workspace
Next Steps
After completing this guide, you can:
- Add monitoring: Set up Sentry or error tracking
- Add analytics: Google Analytics, Plausible, etc.
- Set up CI/CD: GitHub Actions for auto-deploy
- Add custom email: Google Workspace or Zoho
- Scale up: Add more machines, memory, regions
- Add CDN: Cloudflare for better performance
- Set up backups: Automate database backups
- Add staging: Create staging environment
Summary
You've learned how to:
✅ Install Fly CLI and set up account
✅ Create FREE Supabase database
✅ Deploy Rails app to Fly.io (test and production)
✅ Create admin users
✅ Buy and connect custom domain
✅ Set up FREE email forwarding
✅ Manage, stop, and start your app
✅ Troubleshoot common issues
Your app is now live with:
- Custom domain with SSL
- Professional email addresses
- Free database
- Scalable hosting
- All for ~$7-16/month!
Congratulations! 🎉
Support & Resources
- Fly.io Docs: https://fly.io/docs
- Fly.io Community: https://community.fly.io
- Supabase Docs: https://supabase.com/docs
- Namecheap Support: https://www.namecheap.com/support
- Rails Guides: https://guides.rubyonrails.org
Made with ❤️ for beginners
Top comments (0)