¿Por qué autoalojar FastAPI en un VPS?
FastAPI obtiene todo su rendimiento del modelo ASGI asíncrono: conexiones WebSocket persistentes, streaming de respuestas y llamadas concurrentes a otros servicios. Las plataformas serverless suelen cortar las conexiones largas y facturan cada petición, lo que penaliza a una API de alto caudal o en tiempo real. En un VPS, usted lanza Uvicorn gobernado por Gunicorn con tantos workers como núcleos, mantiene las conexiones abiertas todo el tiempo que haga falta y coloca Nginx al frente para el TLS y el buffering. También controla el pool de conexiones hacia PostgreSQL y la caché Redis, dos palancas esenciales cuando sube la carga — el caudal realmente sostenido se mide sobre su propio tráfico.
Beneficios concretos de un FastAPI autoalojado
- WebSockets y Server-Sent Events sin cortes de conexión ni timeout impuesto.
- Ajuste fino de los workers Uvicorn/Gunicorn según el número de núcleos del VPS.
- Ningún arranque en frío (cold start): el proceso permanece activo entre dos peticiones.
- Documentación Swagger y ReDoc servida internamente, accesible o protegida a su conveniencia.
- Integración directa con un pool asíncrono de PostgreSQL (asyncpg) y Redis.
- Coste fijo sea cual sea el número de llamadas a la API, ideal para un backend de producción.
Requisitos de hardware, software y versiones
Una API FastAPI es ligera: 1 vCPU y 1 GB de RAM bastan para empezar, pero apunte a 2 vCPU y 2 GB en cuanto añada una base de datos y una caché en el mismo VPS. Para una carga sostenida (>100 req/s), prevea al menos 4 vCPU y 4 GB.
Versiones recomendadas en 2025: Python 3.11+ (3.12 para el máximo rendimiento), FastAPI 0.115+ (soporte completo de Pydantic v2 y anotaciones Python 3.10+), Uvicorn 0.30+ (correcciones de estabilidad del event loop). Instale con Docker usando la imagen python:3.12-slim, o desde el repositorio oficial con pip install fastapi[standard]>=0.115 uvicorn[standard]>=0.30 gunicorn>=22.0.
En cuanto a infraestructura: Docker Engine 24+, Docker Compose v2 (plugin integrado, docker compose), Nginx 1.24+ como reverse proxy, y un dominio apuntado al VPS para el certificado TLS. Ubuntu 22.04/24.04 LTS es la base recomendada; Debian 12 también funciona.
Desplegar FastAPI paso a paso
Inicializar el VPS y la aplicación
Conéctese por SSH, instale Docker, clone su repositorio y cree un
.envdesde la plantilla.env.exampleincluida en su proyecto. Este archivo debe contener como mínimo:DATABASE_URL=postgresql+asyncpg://user:password@db:5432/appdb REDIS_URL=redis://redis:6379/0 SECRET_KEY=changeme-in-production CORS_ORIGINS=["https://yourdomain.com"] ENVIRONMENT=productionCompruebe que su punto de entrada expone efectivamente
app = FastAPI()y que la aplicación carga su configuración desde variables de entorno (nunca codificadas en duro).Contenedorizar con un Dockerfile multistage
El multistage separa la fase de build (compilación de dependencias, generación de wheels) de la imagen de runtime, reduciendo el tamaño final entre un 60 y un 80% y eliminando las herramientas de compilación de la superficie de ataque:
# ── Stage 1 : build ────────────────────────────────────── FROM python:3.12-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --upgrade pip && \ pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt # ── Stage 2 : runtime ──────────────────────────────────── FROM python:3.12-slim WORKDIR /app COPY --from=builder /wheels /wheels RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels COPY . . EXPOSE 8000 CMD ["gunicorn", "main:app", "-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000", "--workers", "4", "--timeout", "120", "--keep-alive", "5"]Añada un endpoint
/healthen su aplicación para los healthchecks:@app.get("/health") async def health(): return {"status": "ok"}Ajustar los workers de Gunicorn de forma óptima
La fórmula
(2 × núcleos) + 1es el punto de partida para workers CPU-bound. Como FastAPI es ASGI/asíncrono, cada worker gestiona múltiples peticiones de forma concurrente mediante el event loop — para cargas típicas I/O-bound (llamadas a DB, HTTP externas), el rangonúcleos + 1a2 × núcleoses más adecuado.Pase el valor mediante una variable de entorno para ajustarlo sin reconstruir la imagen:
WEB_CONCURRENCY=4 # en .env o docker-compose.ymlCMD ["gunicorn", "main:app", "-k", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000", "--workers", "${WEB_CONCURRENCY:-4}"]Bajo carga elevada, supervise el uso de CPU por worker (
docker stats) y el número de peticiones en espera en los logs de Gunicorn (--access-logfile -) antes de aumentar el número de workers.Orquestar los servicios con Docker Compose
En
docker-compose.yml, declare el servicioapi, un serviciodbPostgreSQL y un servicioredis. Utilicedepends_onconcondition: service_healthypara que la API espere a que la base de datos esté lista antes de arrancar:services: api: build: . env_file: .env ports: ["8000:8000"] depends_on: db: condition: service_healthy redis: condition: service_started healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s retries: 3 db: image: postgres:16-alpine environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: appdb volumes: ["pgdata:/var/lib/postgresql/data"] healthcheck: test: ["CMD", "pg_isready", "-U", "user"] interval: 10s retries: 5 redis: image: redis:7-alpine volumes: pgdata:Colocar Nginx como reverse proxy
Configure un bloque de servidor con
proxy_pass http://api:8000y, para los WebSockets, añada las cabeceras necesarias. Desactive el buffering para el streaming si es necesario:server { listen 443 ssl; server_name api.yourdomain.com; location / { proxy_pass http://api:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_buffering off; } }Asegurar con un certificado TLS
Obtenga un certificado Let's Encrypt mediante Certbot o un contenedor companion, fuerce el HTTPS y redirija el HTTP. Recuerde restringir el acceso a
/docsy/openapi.jsonen producción si la API no es pública.Lanzar y probar
Arranque con
docker compose up -d, compruebe el estado mediante el endpoint/healthque habrá añadido, y pruebe la documentación interactiva. Vigile los registros de Uvicorn para ajustar el número de workers bajo carga real.
Para las tareas largas (envío de correos, procesamiento de imágenes, llamadas externas lentas), no bloquee el event loop: derívelas a un worker en segundo plano con BackgroundTasks para los casos sencillos, o a Celery/ARQ con Redis para los procesamientos pesados. Una API que responde rápido y delega el trabajo asíncrono se mantiene reactiva incluso con alta concurrencia.
Base de datos y migraciones
Para las aplicaciones FastAPI en producción, asyncpg combinado con SQLAlchemy 2.x (modo async) ofrece el mejor equilibrio entre rendimiento y mantenibilidad. Configure el pool de conexiones según la carga esperada:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
engine = create_async_engine(
DATABASE_URL,
pool_size=10, # conexiones persistentes
max_overflow=20, # conexiones adicionales bajo pico
pool_timeout=30, # segundos antes de error
pool_recycle=1800, # reciclado tras 30 min para evitar timeouts PostgreSQL
echo=False,
)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)Inyecte la sesión en sus rutas mediante una dependencia FastAPI:
async def get_db():
async with AsyncSessionLocal() as session:
yield sessionPara las migraciones de esquema, Alembic es la herramienta estándar. Inicialícelo en su proyecto (alembic init migrations) y configure alembic.ini para apuntar a su DATABASE_URL. Los comandos esenciales:
# Generar una migración desde sus modelos SQLAlchemy
alembic revision --autogenerate -m "add_users_table"
# Aplicar las migraciones pendientes
alembic upgrade head
# Verificar el estado actual
alembic currentEn producción, ejecute alembic upgrade head antes de arrancar la API — añádalo como comando de inicio en su docker-compose.yml o en un script de arranque separado. Nunca ejecute las migraciones desde múltiples instancias simultáneamente.
CI/CD y despliegues automatizados
Un pipeline sencillo de GitHub Actions es suficiente para la mayoría de los proyectos FastAPI: build de la imagen, push a un registry y despliegue en el VPS por SSH con zero-downtime.
# .github/workflows/deploy.yml
name: Deploy FastAPI
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build & push Docker image
run: |
docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: deploy
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /opt/myapp
export IMAGE_TAG=${{ github.sha }}
docker compose pull api
docker compose up -d --no-deps api
docker compose exec api alembic upgrade headLa opción --no-deps en docker compose up reinicia únicamente el contenedor api sin tocar db ni redis, lo que garantiza el zero-downtime para los servicios auxiliares. Para despliegues más robustos con rollback instantáneo, etiquete sus imágenes con el SHA del commit y conserve las últimas 3 versiones localmente.
Monitoreo y logs
Acceda a los logs de Uvicorn directamente desde Docker:
# Logs en tiempo real
docker compose logs -f api
# Últimas 100 líneas
docker compose logs --tail=100 apiPara logs estructurados en JSON (indexables por un agregador como Loki o Elasticsearch), sustituya el logger estándar de Python por loguru o structlog:
from loguru import logger
import sys
logger.remove()
logger.add(sys.stdout, format="{time} {level} {message}", serialize=True)
@app.middleware("http")
async def log_requests(request, call_next):
logger.info("request", method=request.method, url=str(request.url))
response = await call_next(request)
logger.info("response", status=response.status_code)
return responsePara exponer métricas Prometheus, añada un endpoint /metrics con prometheus-fastapi-instrumentator:
from prometheus_fastapi_instrumentator import Instrumentator
Instrumentator().instrument(app).expose(app, endpoint="/metrics")Restrinja luego /metrics a su red interna desde Nginx (allow 127.0.0.1; deny all;) para evitar exponer las métricas públicamente.
Resolución de errores frecuentes
Worker timed out (pid XXXXX) — Gunicorn mata un worker que no respondió en el plazo --timeout. Causas frecuentes: una ruta síncrona (def en lugar de async def) bloquea el event loop, o una operación bloqueante (lectura de archivo, llamada de red) se ejecuta sin asyncio.run_in_executor. Aumentar --timeout enmascara el problema; la corrección correcta es hacer la ruta asíncrona o delegar el trabajo bloqueante.
Address already in use (puerto 8000) — Un proceso Gunicorn anterior aún está activo. En Docker: docker compose down y luego docker compose up -d fuerza la recreación del contenedor y libera el puerto. Compruebe con docker ps -a que ningún contenedor zombie esté en marcha.
502 Bad Gateway tras un despliegue — Nginx redirigió la petición a la API durante su reinicio. Espere 10-15 segundos a que el healthcheck de Docker confirme que el nuevo contenedor está healthy antes de considerar el despliegue completado. Añada proxy_read_timeout 60; en Nginx para absorber los arranques lentos.
CORS en producción: No 'Access-Control-Allow-Origin' header — FastAPI sirve los orígenes declarados en CORSMiddleware. Verifique que CORS_ORIGINS en su .env contiene exactamente la URL del frontend (protocolo + dominio + puerto si no es estándar, sin barra al final). Un valor ["*"] funciona en desarrollo pero nunca debe ir a producción.
Conexión DB rechazada al arrancar — La API arranca antes de que PostgreSQL esté lista. El depends_on con condition: service_healthy en Docker Compose resuelve este caso. En Kubernetes o en despliegues sin Compose, implemente un bucle de reintentos con backoff exponencial en el código de inicio de la aplicación.
Desplegar FastAPI con un clic desde el Marketplace ServOrbit
El Marketplace ServOrbit ofrece una plantilla FastAPI Stack preconfigurada: Python 3.12, Uvicorn, Gunicorn, Nginx y PostgreSQL 16 se instalan automáticamente en su VPS desde el pedido. Sin sesión SSH para la instalación de base — llega directamente al paso de clonar su repositorio y de lanzar Uvicorn. La ficha del marketplace contiene las especificaciones exactas, los casos de uso y una guía de primera conexión.