Deploy to Fly.io
Deploy your Motia app to Fly.io with Upstash Redis for global edge deployment
Fly.io runs your app on fast micro-VMs close to your users. Combined with Upstash Redis, you get a globally distributed Motia backend.
This guide walks you through deploying a Motia app to Fly.io with production Redis.
What you'll get: A containerized Motia app running on Fly.io with Upstash Redis for state, events, streams, and cron locking.
Example Project: Follow along with the Todo App example - a complete deployment-ready Motia app with Redis configuration.
Prerequisites
Before you start:
- A Fly.io account (free tier works)
- Fly CLI installed
- Docker running locally (for testing)
- A Motia project ready to deploy
Install the Fly CLI:
Login:
Quick Start
Add Upstash Redis
Follow the prompts to create a Redis instance. Choose a region close to your app.
Upstash Redis on Fly requires a credit card on file, even for free tier usage.
Set environment variables
The Redis URL is automatically attached when you create Redis with flyctl redis create.
Get your URL
Your app is live at: https://my-motia-app.fly.dev
Project Setup
Update Your Start Script
Fly injects the port via environment variables. Update your package.json:
The --host 0.0.0.0 is important - Fly needs your app to listen on all interfaces.
Configure Redis
Create a production config.yaml with Redis adapters for all modules. Fly sets REDIS_URL when you run flyctl redis create:
The ${REDIS_URL:redis://localhost:6379} syntax uses environment variable interpolation with a default value. Fly auto-provisions this variable when you attach Upstash Redis.
Fly.io vs Railway
| Feature | Fly.io | Railway |
|---|---|---|
| Global regions | 30+ regions | Limited regions |
| Redis | Upstash (external) | Built-in Redis |
| Pricing | Pay-per-use | Usage-based |
| CLI | flyctl | railway |
| Best for | Edge deployment | Simple setup |
Choose Fly.io if you need low-latency responses globally. Choose Railway for simpler Redis setup.
Common Commands
Troubleshooting
App Not Listening on Expected Address
Symptom: Fly warns "app is not listening on the expected address"
Fix: Make sure your start script includes --host 0.0.0.0:
Redis Connection Refused
Symptom: Logs show ECONNREFUSED 127.0.0.1:6379
Cause: App is trying to connect to local Redis instead of Upstash.
Fix:
- Check if
REDIS_URLis set:flyctl secrets list - Create Redis if missing:
flyctl redis create - Verify your config parses the URL correctly
TLS Connection Errors
Symptom: Redis connection fails with TLS/SSL errors
Cause: Upstash requires TLS (rediss://), but your config isn't enabling it.
Fix: Make sure your config enables TLS when the protocol is rediss://:
Machine Keeps Restarting
Symptom: App restarts repeatedly in logs
Common causes:
- Unhandled exceptions during startup
- Missing environment variables
- Port binding issues
Debug: Check logs for the actual error:
Scaling Globally
Fly makes global deployment easy. Add machines in different regions:
With Redis configured, all machines share state automatically. Users connect to the nearest machine for lowest latency.