Deploy FastAPI to Railway with this DockerFile
By Justin

Railway is an effective way to deploy FastAPI into production. This blog post serves as a simple how-to guide so you can do the same.
But, why the guide?
FastAPI uses a Python runtime. Some hosting providers support Python out of the box. Some do not. Containers (e.g. Docker Containers) are a simple way to package up any runtime along with any application. I talked about this more in the Deploy Django to Railway with Docker guide.
Railway supports containers by both building and deploying them in one place. This makes the whole process far more seamless and easier to manage.
Two key aspects of this guide are:
1 - Create a Railway Account
Sign up for railway our referral link or directly at railway.com.
2 - Minimal FastAPI project setup
This project is meant to be minimal to mostly focus on the Dockerfile since it's the critical piece of the deployment.
bash
# Create project directory
mkdir -p ~/dev/deploy-fastapi
cd ~/dev/deploy-fastapi
# macos/linux: Create and activate a virtual environment
python3 -m venv venv
source venv/bin/activate
# windows: Create and activate a virtual environment
c:\Python312\python.exe -m venv venv
.\venv\Scripts\activate
# Create requirements.txt
echo "fastapi>=0.115.8" >> requirements.txt
echo "uvicorn" >> requirements.txt
echo "gunicorn" >> requirements.txt
# install requirements
pip install pip --upgrade
pip install -r requirements.txt
# Start the django project
mkdir -p src
cd src
echo "" > main.py
In src/main.py add the following:
python
from typing import Union
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: Union[str, None] = None):
return {"item_id": item_id, "q": q}
@app.get("/healthz/")
def health_check_endpoint():
return {"status": "ok"}
Yup, this is a minimal FastAPI app directly from the FastAPI docs with a health check endpoint.
3 - Create Dockerfile
Now that we have a FastAPI project, we need to create a Dockerfile to containerize our application.
First create a blank Dockerfile in the root of your project.
bash
cd ~/dev/deploy-fastapi
echo "" >> Dockerfile
As a reminder, our project currently has:
- requirements.txt in the root directory next to Dockerfile
- src/ is the directory with FastAPI code
- We'll may use the Python Image Library (pip install pillow) and Postgres at some point in the future of this FastAPI project we'll ensure we're setup correctly for those dependencies
With this in mind, here's the Dockerfile:
dockerfile
# Set the python version as a build-time argument
# with Python 3.12 as the default
ARG PYTHON_VERSION=3.12-slim-bullseye
FROM python:${PYTHON_VERSION}
# Create a virtual environment
RUN python -m venv /opt/venv
# Set the virtual environment as the current location
ENV PATH=/opt/venv/bin:$PATH
# Upgrade pip
RUN pip install --upgrade pip
# Set Python-related environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# Install os dependencies for our mini vm
RUN apt-get update && apt-get install -y \
# for postgres
libpq-dev \
# for Pillow
libjpeg-dev \
# for CairoSVG
libcairo2 \
# other
gcc \
&& rm -rf /var/lib/apt/lists/*
# Create the mini vm's code directory
RUN mkdir -p /code
# Set the working directory to that same code directory
WORKDIR /code
# Copy the requirements file into the container
COPY requirements.txt /tmp/requirements.txt
# copy the project code into the container's working directory
COPY ./src /code
# Install the Python project requirements
RUN pip install -r /tmp/requirements.txt
# database isn't available during build
# run any other commands that do not need the database
# such as:
# RUN python manage.py collectstatic --noinput
# set the FastAPI project main module
ARG PROJ_NAME="main"
# create a bash script to run the FastAPI project
# this script will execute at runtime when
# the container starts and the database is available
RUN printf "#!/bin/bash\n" > ./paracord_runner.sh && \
printf "RUN_PORT=\"\${PORT:-8000}\"\n\n" >> ./paracord_runner.sh && \
printf "gunicorn ${PROJ_NAME}:app --workers 2 --worker-class uvicorn.workers.UvicornWorker --bind \"[::]:\$RUN_PORT\"\n" >> ./paracord_runner.sh
# make the bash script executable
RUN chmod +x paracord_runner.sh
# Clean up apt cache to reduce image size
RUN apt-get remove --purge -y \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Run the FastAPI project via the runtime script
# when the container starts
CMD ./paracord_runner.sh
This Dockerfile is nearly identical to the Dockerfile we used in the Deploy Django to Railway with Docker except modified for FastAPI.
A couple important points:
- paracord_runner.sh is a bash script that runs after the container is built and thus all environment variables are available (or at least should be available). The DATABASE_URL is the key environment variable here but other production-required environment variables are also needed when this script runs.
- We use gunicorn to run the FastAPI project. Gunicorn is a production-ready WSGI HTTP server that can be used to run Django, FastAPI, and many other Python web applications.
- We bind gunicorn to [::]:$RUN_PORT instead of 0.0.0.0:$RUN_PORT to support both IPv4 and IPv6 communication. If you want Django private, this is key. Railway currently only supports IPv6 for private communication between services.
- We use the UvicornWorker class to run the FastAPI project. Uvicorn is a production-ready ASGI server that can be used in conjunction with gunicorn to run FastAPI, Django, and many other Python web applications.
Create railway.json
Using Railway's Config as Code, we need to ensure this Dockerfile is being use to build our container and run our application.
To do this, create railway.json in the root of your project:
bash
cd ~/dev/deploy-fastapi
echo "" >> railway.json
In railway.json, add the following:
json
{
"build": {
"builder": "DOCKERFILE",
"dockerfilePath": "./Dockerfile",
"watchPatterns": [
"requirements.txt",
"src/**",
"railway.json",
"Dockerfile"
]
},
"deploy": {
"healthcheckPath": "/healthz/",
"healthcheckTimeout": 300,
"startupTimeout": 300,
"restartPolicyType": "always",
"restartPolicyMaxRetries": 10
}
}
At this point, we have the foundation in place to deploy a FastAPI project to Railway. All you have to do now is commit your code to a git repository and connect Railway to that repository. Railway will build and deploy your app.
Need to deploy Django?
Check out the Deploy Django to Railway with Docker guide.