FastAPI-приложение работает локально через uvicorn main:app --reload. Этот режим — для разработки: --reload следит за изменениями файлов и не рассчитан на нагрузку. Чтобы приложение отвечало в продакшене круглосуточно, его нужно правильно запустить и где-то разместить. Разберём, как запускать FastAPI в проде, какие есть варианты хостинга и как задеплоить.
ASGI-сервер для продакшена
FastAPI — это ASGI-приложение, и в проде его запускает ASGI-сервер. Вариантов два.
Один процесс — uvicorn:
uvicorn main:app --host 0.0.0.0 --port 8000
Без --reload. Один процесс держит один поток событий — этого достаточно для умеренной нагрузки.
Несколько процессов — gunicorn с uvicorn-воркерами:
gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 --bind 0.0.0.0:8000
-w 4 поднимает четыре рабочих процесса и использует все ядра. Ориентир по числу воркеров — 2 × ядра + 1. Это рекомендуемый способ для прода.
Сам ASGI-сервер не стоит выставлять напрямую в интернет — перед ним ставят reverse proxy (nginx) для TLS и домена. Как это настроить — в статье про nginx и SSL.
Куда размещать: три варианта
VPS (Beget, Timeweb, Hetzner). Полный контроль. Минус — Python, gunicorn под supervisor, база, бэкапы, nginx и SSL настраиваются и обслуживаются вручную.
Managed-платформа (PaaS). Деплой по git push: платформа собирает проект, держит процесс живым, рядом managed-база, домен и SSL — автоматически.
Serverless (облачные функции). FastAPI можно запустить в Lambda через адаптер Mangum, но cold start и лимиты времени выполнения делают это неудобным для постоянной нагрузки и фоновых задач. Для долгоживущего API-сервиса подходит плохо.
Подготовка к деплою
Зафиксировать зависимости в requirements.txt (pip freeze > requirements.txt).
Вынести конфигурацию в переменные окружения. Секреты и строку подключения к базе нельзя держать в коде. Удобно через pydantic-settings:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
secret_key: str
settings = Settings() # читает DATABASE_URL и SECRET_KEY из окружения
Подробнее о хранении ключей — в статье про секреты и .env.
Не забыть про миграции базы. Схему накатывают миграциями (обычно Alembic), а не вручную. Команда alembic upgrade head должна выполняться при каждом деплое — до старта приложения.
Деплой на VPS
git clone https://github.com/user/myapi.git /opt/myapi
cd /opt/myapi
python -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/alembic upgrade head
Запускать gunicorn руками нельзя — процесс умрёт с закрытием сессии. Нужен супервизор с автозапуском; минимальный systemd-сервис:
[Service]
WorkingDirectory=/opt/myapi
ExecStart=/opt/myapi/.venv/bin/gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 --bind 127.0.0.1:8000
EnvironmentFile=/opt/myapi/.env
Restart=always
--bind 127.0.0.1 — приложение слушает только localhost, наружу его отдаёт nginx. Полная настройка supervisor — в статье как держать процесс 24/7.
Деплой на managed-платформе
Короче: подключить репозиторий, задать переменные окружения (DATABASE_URL, SECRET_KEY) в панели, указать команду запуска (gunicorn с uvicorn-воркерами) и сделать git push. Платформа определит Python-проект, установит зависимости, запустит процесс и будет держать его живым. Managed Postgres подключается через DATABASE_URL, домен и SSL поднимаются автоматически. Миграции удобно вешать на стадию деплоя.
Фоновые задачи и статика
Не всякую работу стоит делать прямо в обработчике запроса. Короткую задачу после ответа (отправить письмо, записать в лог) удобно вынести в BackgroundTasks FastAPI. Но тяжёлые и длительные задачи (обработка видео, рассылка) так делать нельзя: они держат воркер и блокируют запросы. Для них нужен отдельный фоновый воркер с очередью — Celery или RQ поверх Redis.
Статику (загруженные файлы, медиа) на нескольких воркерах и тем более нескольких инстансах нельзя хранить на локальном диске процесса — она будет видна не всем. Файлы кладут в объектное хранилище (S3-совместимое), а раздачу статики фронтенда поручают reverse proxy или CDN.
Проверка после деплоя
Запущенное приложение стоит проверить явно, а не считать рабочим по факту деплоя:
- открыть автодокументацию по
/docs— если она отвечает, ASGI-сервер поднялся; - дёрнуть простой эндпоинт и убедиться, что приходит
200, а не502от reverse proxy; - посмотреть логи старта: подключение к базе, выполненные миграции, отсутствие исключений.
Полезно завести лёгкий /health-эндпоинт, который проверяет доступность базы. Его используют reverse proxy и мониторинг, чтобы понимать, что приложение живо не только как процесс, но и функционально.
Частые грабли
--reloadв проде. Режим разработки оставляет лишний процесс-наблюдатель и течёт по памяти под нагрузкой.- Один воркер под нагрузкой. Один процесс обрабатывает запросы по очереди; для параллелизма нужны воркеры gunicorn.
- Блокирующий вызов в
async-эндпоинте. Синхронный запрос к базе илиtime.sleepвнутриasync defблокирует весь event loop. Тяжёлую синхронную работу выносят вrun_in_executorили делают эндпоинт обычнымdef. - Забытые миграции. Код задеплоен, а схема старая — приложение падает на старте.
alembic upgrade head— часть деплоя. - CORS. Фронтенд на другом домене не достучится до API без настроенного
CORSMiddleware.
Чеклист
- Прод запускается
gunicornс uvicorn-воркерами, без--reload. - Число воркеров подобрано под ядра (
2 × ядра + 1). - ASGI-сервер слушает localhost, наружу отдаёт reverse proxy с SSL.
DATABASE_URLи секреты — в переменных окружения, не в коде.- Миграции (
alembic upgrade head) выполняются при деплое. - Процесс под супервизором с автоперезапуском.
- Настроены бэкапы базы.
Managed-платформа закрывает большую часть этого списка по умолчанию: процесс держится сам, reverse proxy с SSL и managed Postgres — из коробки, миграции вешаются на деплой. На Hostim FastAPI-приложение разворачивается по git push, стек определяется автоматически, а база подключается через DATABASE_URL без ручной настройки сервера.