Deploying the example FastAPI app on Railway
I recently took on a project that involves FastAPI and hosting it on Railway. The official guide wasn’t to my liking, so I decided to tweak it and share what I ended up doing here.
Project files
Our FastAPI project will use the FastAPI example app from the landing page.
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
The dependencies for this project are the default fastapi[standard]1. The following requirements.txt file is the same as using pip install -r requirements.txt, but we need to put it in a file so that Railway knows what dependencies to install.
# requirements.txt
fastapi[standard]
Railway deployment configuration
Given that project, we now need to define a railway.json file to configure how Railway should deploy our project. We will be using the railway CLI, if you haven’t installed it already.
{
"$schema": "https://railway.com/railway.schema.json",
"build": {
"builder": "RAILPACK"
},
"deploy": {
"startCommand": "uvicorn main:app --host 0.0.0.0 --port ${PORT:-8000}"
}
}
This railway.json file controls how Railway will deploy our application. You can review the reference document here.
The $schema definition isn’t functional, but it does allow your editor to better evaluate your configuration. If you use PyCharm or VSCode, this is extremely helpful.
The builder configuration is the default of RAILPACK. We don’t have to specify this, but I’ve included it because it’s one of the reasons I didn’t like Railway’s FastAPI template2. From Railpack.com:
Railpack builds and deploys Python applications with support for various package managers and dependency management tools.
The startCommand is the piece we need to define3. We’re telling Railway to serve our application, use uvicorn, which is a dependency of fastapi[standard], to serve our application. Because the file that defines our FastAPI app is main.py, the startCommand uses main:app. main defines the module (or file) and app is defined from app = FastAPI().
Railway commands to deploy
At this point we should be able to deploy the project. This uses the railway CLI, so please install it if you haven’t installed it already.
# One-time command to set up your railway project
railway init
# Upload and run your project
railway up
# Make your project publicly accessible via a random domain
railway domain
The railway domain command will output a URL that you can use to access your project on the web.
Once you’ve deployed your app the first time, you will no longer need init and domain. However, you will need to continue to run railway up each time you want to deploy a new version of your project.
What was missing from the Railway docs
There was a lot of helpful information tucked away in the Railpack docs that I had to go seek out. If this is the default option, I think it’s fair to assume it’s the suggested option. If it is, it’s worth highlighting some of that information within the framework and language-specific guides.
If you’re using the railway CLI and writing your config as code, I suggest visiting the Railpack documentation as well.
Wrapping things up
I feel like I’m going to like Railway. It looks extremely configurable. I’m guessing the challenge will be finding the information I need at the right moments to know what to do and when.
-
Yes, this could be
fastapi[standard-no-fastapi-cloud-cli], but that’s a lot and annoying. ↩ -
The existing example template uses nixpacks, which is a deprecated solution from Railway. ↩
-
Technically, we could skip setting this
startCommandsince Railway should detect that we havefastapianduvicorninstalled and it’ll default touvicorn main:app --host 0.0.0.0 --port ${PORT:-8000}. ↩
Written by Tim Schilling
Django 6.x Steering Council member, maintainer of django-debug-toolbar and django-simple-history, and a professional software engineer since 2009. These days, I mentor developers one-on-one.