Fly.io has become a favorite platform for deploying full-stack applications, APIs, and background services. Its global anycast network, generous free tier, and developer-friendly CLI make it easy to get apps into production. But once your app is on Fly.io, how do you know it stays up?
This guide covers setting up uptime monitoring for Fly.io deployments using Vigilmon.
How Fly.io Deployments Can Fail
Fly.io is reliable, but failures happen at multiple layers:
- Machine crashes: Your app process panics or runs out of memory
- Deployment rollouts: A bad deploy rolls out and crashes on startup
- Scaling events: New machines spin up with wrong environment variables
- Regional failures: Specific Fly.io regions become unreachable
- Volume mount issues: Persistent volumes fail to attach
- Resource limits: CPU/memory limits cause OOM kills
Fly.io has internal health checks that restart machines — but they don't alert your team. Vigilmon provides the external monitoring and notifications layer.
Step 1: Add a Health Check Endpoint to Your App
Every app running on Fly.io should expose a /health endpoint. Here are examples for common stacks:
Node.js / Express
app.get('/health', (req, res) => {
res.json({ status: 'ok', region: process.env.FLY_REGION });
});
Python / FastAPI
@app.get("/health")
async def health():
return {"status": "ok", "region": os.getenv("FLY_REGION")}
Go
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"status": "ok",
"region": os.Getenv("FLY_REGION"),
})
})
Ruby on Rails
# config/routes.rb
get '/health', to: proc { [200, {'Content-Type' => 'application/json'}, ['{"status":"ok"}']] }
Note: FLY_REGION is a built-in Fly.io environment variable — great for debugging multi-region issues.
Step 2: Configure Fly.io Internal Health Checks
Add health checks to your fly.toml:
[[services]]
internal_port = 8080
protocol = "tcp"
[[services.http_checks]]
interval = 15000
timeout = 2000
grace_period = "5s"
method = "get"
path = "/health"
protocol = "http"
restart_limit = 3
These internal checks restart your machine if it fails. But they don't notify anyone — that's Vigilmon's job.
Step 3: Set Up Vigilmon External Monitoring
- Sign up at vigilmon.online (free plan available)
- Click New Monitor
- Configure:
-
URL:
https://yourapp.fly.dev/health - Type: HTTP
- Interval: 60 seconds
- Expected status: 200
-
URL:
- Add alert channels:
- Email: default
- Slack: Settings → Notifications → Add Slack webhook
- Save
Vigilmon now monitors your Fly.io app from outside the infrastructure.
Multi-Region Monitoring
Fly.io can run your app in multiple regions. Vigilmon monitors from a single point, but you can add monitors for each region's health:
https://yourapp.fly.dev/health (global anycast)
If you have region-pinned endpoints, monitor each one separately:
https://iad.yourapp.fly.dev/health (US East)
https://lhr.yourapp.fly.dev/health (UK)
https://sin.yourapp.fly.dev/health (Singapore)
Common Fly.io Failure Scenarios and How Vigilmon Catches Them
Deployment Failure
You push a new deploy. The new machine starts, but your app crashes on startup (e.g., missing SECRET_KEY env var). The old machine is killed. Fly.io doesn't roll back automatically by default.
Vigilmon catches the 500 errors or timeouts within the next check interval and alerts you immediately.
OOM Kill
Your app runs out of memory (e.g., memory leak after days of uptime). The Fly.io machine is OOM-killed. Fly.io restarts it, but there's a gap.
Vigilmon detects the gap and alerts your team — even if the machine recovers before you notice.
Volume Mount Failure
If your app uses a persistent volume and the volume fails to mount, your app may start but fail on any disk access. Health checks that verify disk writes catch this:
// Test disk access in health check
func healthHandler(w http.ResponseWriter, r *http.Request) {
// Try writing to a file on the mounted volume
err := os.WriteFile("/data/.healthcheck", []byte("ok"), 0644)
if err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
json.NewEncoder(w).Encode(map[string]string{"status": "error", "volume": err.Error()})
return
}
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}
Setting Up Alerts
Slack
Connect your Vigilmon account to Slack:
- Settings → Notifications → Add Slack
- Enter your Slack webhook URL
- Choose the channel (e.g.,
#fly-alerts)
Webhook (for PagerDuty or custom tools)
- Settings → Notifications → Add Webhook
- Enter your PagerDuty Events API endpoint
- Vigilmon POSTs JSON on state changes
Public Status Page
Vigilmon generates a public status page. Add it to your Fly.io app's response headers or landing page:
https://status.vigilmon.online/your-org
Cost Comparison
Fly.io monitoring options:
| Approach | Cost | Who gets notified | Reaction |
|---|---|---|---|
| Fly.io internal checks | Free | Nobody | Restarts machine |
| Fly.io metrics | Free | Nobody | Observability only |
| Vigilmon | Free tier | Your team | Instant alert |
Fly.io's built-in checks are excellent at restarting failed machines. Vigilmon is what tells your team something happened.
Summary
Fly.io is a great platform, but external monitoring is still essential. Set up Vigilmon in 3 steps:
-
Add a
/healthendpoint to your app -
Configure
fly.tomlinternal checks for automatic restarts - Add Vigilmon external monitoring so your team gets alerted
Top comments (0)