FastAPI has changed how developers build Python web APIs with its speed and simplicity, but getting your app ready for production takes more than just putting it in a container.
You need to consider performance, security, scalability, and maintaining a smooth-running app when real users start hitting it hard. A basic Docker setup won't cut it when your startup idea takes off, or your boss asks you to handle Black Friday traffic.
This guide will show you exactly how to deploy FastAPI with Docker the right way. You'll learn the practices that separate hobby projects from production systems that actually work.
1. Start with a solid Dockerfile foundation
Before you worry about fancy optimizations, ensure you get the basics right. A good Dockerfile is the foundation of everything else you'll build on top of it.
The FastAPI documentation shows you a simple approach that works great for most applications. Let's start there and understand why each line matters.
Here's the basic structure you should use:
While this may look straightforward, there’s an important optimization happening here. Copying the requirements file and installing dependencies first allows Docker to cache that layer.
Docker builds your image in layers and caches each step. When you change your Python code, Docker doesn't need to reinstall all your dependencies - it just reuses the cached layer. This saves you tons of time during development.
Your project structure should look like this:
Always use the exec form for your CMD instruction. Write CMD ["fastapi", "run", "app/main.py", "--port", "80"] instead of CMD fastapi run app/main.py --port 80. The exec form lets FastAPI shut down properly when you stop the container.
If you're running behind a proxy like Nginx or Traefik, add the --proxy-headers flag:
This tells FastAPI to trust the headers from your proxy so it knows when requests are coming through HTTPS.
2. Handle environment configuration properly
Hardcoding settings in your FastAPI app is a recipe for disaster. You'll end up with different versions of your code for development and production, or worse, you'll accidentally commit API keys to Git.
Environment variables solve this problem. FastAPI works great with Pydantic settings that automatically load from environment variables.
Set up your configuration like this:
Create a .env file for your local development:
Never commit your .env file to Git. Add it to your .gitignore:
Instead, create a .env.example file that shows other developers what variables they need:
In your Docker Compose file, you can load environment variables easily:
This approach keeps your secrets safe and lets you use the same code everywhere. Change your environment variables, not your code, when you deploy to different environments.
3. Set up proper health checks
You need to know when your FastAPI app is healthy, especially when Docker or Kubernetes is managing your containers. A simple health check endpoint can save you hours of debugging.
Add this to your FastAPI app:
Configure Docker to use your health check:
The health check runs every 30 seconds. If it fails 3 times in a row, Docker marks the container as unhealthy. You can configure your orchestrator to restart unhealthy containers automatically.
For more complex apps, check your database, Redis, or other critical services in your health check. But keep it simple - health checks should be fast and reliable.
4. Optimize for Docker layer caching
Docker’s layer caching can greatly speed up your builds, but it depends on how your Dockerfile is structured. Even small adjustments can be the difference between a quick 30-second build and waiting several minutes.
Here's the key principle: put things that change often at the bottom of your Dockerfile. Put things that rarely change at the top.
Your requirements.txt file doesn't change very often. Your Python code changes all the time. So install requirements first, then copy your code:
When you change your Python code and rebuild, Docker reuses the layer with your installed packages. This saves huge amounts of time during development.
You can also use a .dockerignore file to avoid copying unnecessary files:
This keeps your build context small and your builds fast.
For even better caching, you can separate your development and production dependencies:
5. Handle database connections and migrations
Your FastAPI app probably needs a database. In containerized environments, you need to handle database connections carefully and make sure your migrations run at the right time.
First, make sure your database is ready before your app starts. Use depends_on with health checks in Docker Compose:
For database migrations, you have two good options. You can run them in a separate container before your app starts:
Or you can run them when your app starts:
The separate container approach is safer for production because it prevents multiple app instances from running migrations at the same time.
6. Configure proper logging
Good logging is essential for production apps. You need to see what's happening, especially when things go wrong.
FastAPI uses Python's logging module. Configure it to output structured logs that are easy to parse:
Configure Docker to handle log rotation so your logs don't fill up your disk:
In production, consider sending your logs to a centralized logging service like CloudWatch, Datadog, or the ELK stack.
7. Prepare for production scaling
When your app gets popular, you'll need to handle more traffic. Plan for this from the beginning.
For simple deployments, you can run multiple worker processes in a single container:
But for serious production deployments, you're better off running one process per container and scaling at the container level:
Use a reverse proxy like Nginx to handle static files and SSL termination:
Configure your Nginx to proxy requests to your FastAPI containers:
This setup lets Nginx handle static files efficiently while your FastAPI containers focus on processing API requests.
Final thoughts
Building a production-ready FastAPI application with Docker isn't just about writing a Dockerfile. You need to consider caching, configuration, health checks, logging, and scaling from the outset.
Start with the basics - a solid Dockerfile, proper environment configuration, and basic health checks. Then add complexity as needed.
For deeper learning, check out the FastAPI Docker documentation for official guidance, Docker's best practices for containerization tips, and the Docker Compose documentation for orchestration details.