Skip to main content
Fly.io is a platform for running full-stack apps globally. CampusBite includes a pre-configured fly.toml for easy deployment.

Prerequisites

1

Install flyctl

Install the Fly.io CLI:
curl -L https://fly.io/install.sh | sh
2

Sign up and log in

fly auth signup
# or if you already have an account
fly auth login

Deployment steps

1

Set your app name

Edit fly.toml and change the app name:
fly.toml
app = 'your-unique-app-name'
primary_region = 'bom'  # or your preferred region
App names must be globally unique across all Fly.io users. Choose a name like campusbite-yourschool or cb-yourname.
2

Create the app and volume

Run these commands once:
fly apps create your-unique-app-name
fly volumes create db --size 1 --region bom
The volume is mounted at /data (configured in fly.toml) and stores:
  • Uploaded files (menu images, store logos)
  • Any persistent data needed by the application
3

Set secrets

Configure environment variables as Fly.io secrets:
fly secrets set \
  JWT_SECRET=$(openssl rand -base64 48) \
  JWT_REFRESH_SECRET=$(openssl rand -base64 48) \
  MONGODB_URI="mongodb+srv://username:[email protected]/campusbite" \
  FRONTEND_URL="https://your-unique-app-name.fly.dev" \
  APP_URL="https://your-unique-app-name.fly.dev"
Optional SMTP secrets for email:
fly secrets set \
  SMTP_HOST=smtp.gmail.com \
  SMTP_PORT=587 \
  [email protected] \
  SMTP_PASS=your-app-password \
  [email protected]
Optional checkout token secret:
fly secrets set CHECKOUT_TOKEN_SECRET=$(openssl rand -base64 48)
4

Deploy

fly deploy
This will:
  1. Build the Docker image (multi-stage build)
  2. Push the image to Fly.io’s registry
  3. Create a new VM with your app
  4. Run health checks
  5. Route traffic to the new VM
5

Verify deployment

fly status
fly logs
Check the health endpoint:
curl https://your-unique-app-name.fly.dev/api/health
Expected response:
{
  "success": true,
  "message": "CampusBite API is running.",
  "timestamp": "2026-03-04T10:30:45.123Z"
}

Configuration reference

CampusBite’s fly.toml is optimized for low-traffic campus deployments:
fly.toml
app = 'campus-bite'
primary_region = 'bom'

[build]

[env]
  UPLOAD_DIR = "/data/uploads"

[http_service]
  internal_port = 8080
  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

[mounts]
  source = "db"
  destination = "/data"

Key settings

app
string
Your unique app name. Must be globally unique across Fly.io.
primary_region
string
default:"bom"
Region code for your primary deployment. Common options:
  • bom - Mumbai, India
  • sin - Singapore
  • iad - Ashburn, Virginia (US East)
  • lhr - London, UK
See Fly.io regions for the full list.
[env].UPLOAD_DIR
string
default:"/data/uploads"
Directory for uploaded files. Maps to the mounted volume.
[http_service].internal_port
number
default:"8080"
Port your app listens on (must match PORT in Dockerfile).
[http_service].auto_stop_machines
string
default:"stop"
Automatically stops machines when not receiving traffic. Ideal for low-traffic campus apps.
[http_service].auto_start_machines
boolean
default:"true"
Automatically starts machines when receiving traffic. Enables zero-config scaling.
[http_service].min_machines_running
number
default:"0"
Minimum machines to keep running. Set to 0 for cost savings on low-traffic apps. Increase to 1 for always-on availability.
[[vm]].memory
string
default:"1gb"
RAM allocated per VM. Options: 256mb, 512mb, 1gb, 2gb, 4gb, 8gb.
[mounts].source
string
Volume name (must match the volume created with fly volumes create).
[mounts].destination
string
Mount path inside the container. CampusBite expects /data.

Managing your deployment

View logs

# Live tail
fly logs

# Filter by level
fly logs --level error

Scale resources

# Increase memory
fly scale memory 2048

# Always keep 1 machine running
fly scale count 1

# Scale to zero when idle (default)
fly scale count 0

Update secrets

# Update a single secret
fly secrets set MONGODB_URI="new-connection-string"

# List all secrets
fly secrets list

# Remove a secret
fly secrets unset SMTP_HOST
Updating secrets triggers a new deployment with zero-downtime.

SSH into your VM

fly ssh console
Useful for debugging:
# Check disk usage
df -h

# View uploaded files
ls -lh /data/uploads

# Check environment variables
env | grep MONGODB

Access MongoDB from Fly.io

If using MongoDB Atlas, ensure Fly.io IPs are whitelisted:
  1. Get your app’s outbound IP:
    fly ips list
    
  2. Add the IPv4 and IPv6 addresses to Atlas Network AccessIP Access List
Alternatively, allow all IPs (0.0.0.0/0) for testing (not recommended for production).

Continuous deployment

Deploy automatically on every push to main:
1

Generate a Fly.io deploy token

fly tokens create deploy
Copy the token.
2

Add token to GitHub secrets

Go to your repository → SettingsSecrets and variablesActionsNew repository secret:
  • Name: FLY_API_TOKEN
  • Value: (paste the token)
3

Create GitHub Actions workflow

Create .github/workflows/deploy.yml:
name: Deploy to Fly.io

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: superfly/flyctl-actions/setup-flyctl@master
      - run: flyctl deploy --remote-only
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

Troubleshooting

Deployment fails with “health checks failed”

Solution: Check logs for startup errors:
fly logs --level error
Common causes:
  • Missing required environment variables (JWT_SECRET, MONGODB_URI, etc.)
  • MongoDB connection failure
  • Port mismatch (app must listen on port 8080)

App crashes immediately after deploy

Solution: Verify all required secrets are set:
fly secrets list
Ensure these are present:
  • JWT_SECRET
  • JWT_REFRESH_SECRET
  • MONGODB_URI
  • FRONTEND_URL

Volume not mounting

Solution: Ensure volume name matches fly.toml:
# List volumes
fly volumes list

# Should show a volume named "db" in your region
If missing:
fly volumes create db --size 1 --region bom

CORS errors

Solution: Verify FRONTEND_URL matches your frontend deployment:
# Update secret
fly secrets set FRONTEND_URL="https://your-frontend-url.com"

Cost optimization

Fly.io’s free tier includes:
  • Up to 3 shared-cpu-1x 256mb VMs
  • 3GB persistent volume storage
  • 160GB outbound data transfer
To minimize costs:
  1. Use auto-stop/start: Already configured in fly.toml
    auto_stop_machines = 'stop'
    auto_start_machines = true
    min_machines_running = 0
    
  2. Optimize volume size: Start with 1GB and increase if needed
    fly volumes create db --size 1
    
  3. Monitor usage:
    fly dashboard
    
For campus deployments with less than 1000 active users, the default configuration should stay within free tier limits.

Build docs developers (and LLMs) love