Deploying Flask to Railway
Deploying Flask to Railway
Railway is the fastest way to host a Flask + PostgreSQL app. You get managed PostgreSQL, automatic deployments from GitHub, and a generous free tier. This tutorial takes you from a working local app to a live URL.
What You Need Before Starting
- Your Flask app running locally with
python app.py - A
requirements.txtwith all your dependencies - A Railway account at railway.app (free)
- Your code in a GitHub repository
Step 1: Add a Procfile
Railway needs to know how to start your app in production. Create a Procfile in your project root:
web: gunicorn app:app
app:app means: in app.py, run the app Flask object. Install gunicorn if you have not already:
pip install gunicorn
pip freeze > requirements.txt
Step 2: Test gunicorn Locally
Before deploying, confirm gunicorn starts your app correctly:
gunicorn app:app
Visit http://127.0.0.1:8000. If it works locally, it will work on Railway.
Step 3: Create a Railway Project
- Go to railway.app → New Project → Deploy from GitHub repo
- Select your repository
- Railway detects the
Procfileand starts building
Your app will fail on first deploy — that is expected, because there is no database yet.
Step 4: Add a PostgreSQL Service
In your Railway project dashboard:
- Click New → Database → Add PostgreSQL
- Railway provisions a managed PostgreSQL instance in seconds
Step 5: Get Your Database Credentials
Click your PostgreSQL service → Connect tab. You will see individual connection variables:
PGHOST → copy this as DB_HOST
PGPORT → copy this as DB_PORT
PGDATABASE → copy this as DB_NAME
PGUSER → copy this as DB_USER
PGPASSWORD → copy this as DB_PASSWORD
Do not use the DATABASE_URL connection string — set each variable individually so your existing DB_CONFIG in app.py works without modification.
Step 6: Set Environment Variables
In your Flask service (not the PostgreSQL service) → Variables tab, add:
DB_HOST = (value from PGHOST)
DB_PORT = (value from PGPORT)
DB_NAME = (value from PGDATABASE)
DB_USER = (value from PGUSER)
DB_PASSWORD = (value from PGPASSWORD)
SECRET_KEY = (generate with: python -c "import secrets; print(secrets.token_hex(32))")
FLASK_ENV = production
RESEND_API_KEY = (your Resend key, if used)
Your app will redeploy automatically after saving.
Step 7: Run Your Migration
To create tables on the Railway database, run your migration script against Railway's database from your local machine:
# Temporarily set env vars pointing to Railway DB
export DB_HOST=your-railway-host
export DB_PORT=your-railway-port
export DB_NAME=your-railway-db-name
export DB_USER=your-railway-user
export DB_PASSWORD=your-railway-password
python migrate.py
Or use the Railway CLI to run it directly on the server:
npm install -g @railway/cli
railway login
railway run python migrate.py
Step 8: Check Your Deployment
Railway shows build and runtime logs in the Deployments tab. Common issues:
| Error | Fix |
|---|---|
ModuleNotFoundError |
Missing package in requirements.txt — run pip freeze > requirements.txt |
Connection refused (DB) |
Wrong DB_HOST or DB_PORT — double-check from Railway Connect tab |
gunicorn: not found |
Add gunicorn to requirements.txt |
| 502 Bad Gateway | App crashed on startup — check logs for traceback |
Step 9: Add a Custom Domain
Railway provides a .up.railway.app URL by default. To use your own domain:
- Go to your service → Settings → Domains → Custom Domain
- Add your domain and follow the DNS instructions (typically a CNAME record)
- Railway provisions an SSL certificate automatically
Environment: Production vs Development
Make sure your Flask app does not run in debug mode in production:
if __name__ == "__main__":
debug_mode = os.environ.get("FLASK_ENV") == "development"
app.run(debug=debug_mode)
gunicorn ignores the app.run() call entirely — it manages its own workers. But this protects you if someone runs python app.py in production accidentally.
AI Prompt That Generated This
"Create a Procfile for a Flask app using gunicorn. Show how to set individual PostgreSQL environment variables (DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD) in Railway's dashboard from the Connect panel, without using DATABASE_URL. Include a checklist of common deployment errors."
Next Steps
- Set up GitHub Actions to run pytest tests before every deploy
- Add Rate Limiting with a Redis service on Railway for distributed limits
Was this helpful?
Get More Flask Vibe Tutorials
Join 1,000+ developers getting weekly Flask tips and AI-friendly code patterns.
No spam. Unsubscribe anytime.