2. Localhost vs production
Your app runs on localhost:5000. Your own computer provides everything it needs. When you deploy it, several things change, and some of them need your attention.
Some of what you gain is automatic. Railway gives you a public address, keeps the app running around the clock, and serves it over HTTPS without extra setup.
| Concern | Localhost | Railway gives you |
|---|---|---|
| Access | Private: 127.0.0.1, your machine only |
Public: https://your-app.up.railway.app |
| Uptime | Stops when you close your laptop | Runs continuously, with automatic restarts |
| Encryption | Plain HTTP | HTTPS, provisioned for you |
The other half needs more care. Your laptop was quietly doing several jobs on your behalf. Those details do not carry over automatically, so you have to configure them again on Railway. If you miss one, the app may fail even though the code itself is fine.
| Concern | Localhost | Railway needs from you |
|---|---|---|
| Storage | Local disk, persists by default | A persistent /data volume for SQLite |
| Secrets | .env file on your machine |
Environment variables in the Railway dashboard |
| OAuth redirect | http://127.0.0.1:5000/callback |
https://your-app.up.railway.app/callback |
| Debug mode | Useful while developing locally | Turned off for production |
The second table is where deployments usually break. Production does not inherit your laptop's environment. When your code lands on Railway, it runs in a clean container. It does not know about your local database file, your local .env file, or your localhost Spotify callback unless you configure those pieces again.
Most first deployment errors are not Python errors. They are missing setup errors.
- If Railway cannot find your Spotify credentials, the app cannot complete OAuth.
-
If Spotify still points to
127.0.0.1, the live login flow redirects to your laptop instead of your deployed app. - If SQLite writes to the wrong path, your data may disappear when the container restarts.
- If debug mode is left on, the app exposes development behaviour in production.
The goal of this chapter is to make each of those hidden local details explicit before they turn into confusing deployment failures.
Platform choice: Railway
You have three good platform options: Railway, Render, and PythonAnywhere. Each has strengths. For this chapter, we're deploying to Railway because it offers the smoothest experience for Flask applications with SQLite databases.
Railway detects Python projects automatically, provides persistent storage with one click, and keeps your app running without the cold-start delays some free hosting tiers introduce. Its Hobby plan includes $5 of monthly usage, enough for a small portfolio project if you keep an eye on usage. The deployment workflow mirrors professional CI/CD practices: push to GitHub, Railway deploys automatically.
Other platforms work too. If you need PostgreSQL eventually, Render includes it. If you don't have a credit card, PythonAnywhere requires none. The deployment patterns you learn transfer across platforms; Railway just gets you deployed fastest with the fewest obstacles, which is what matters for a portfolio piece you want a recruiter to click today, not next week.
Section 3 starts the production-prep work: environment-based config, fail-fast secret validation, the Procfile start command and requirements.txt Railway needs to build your container.