Guía de despliegue

Desplegar FastAPI en un VPS: guía completa de producción

Desplegar en un VPS Cloud →

Tutorial

Desplegar FastAPI en un VPS: guía completa de producción

Desarrollo10 min de lectura7 pasos

FastAPI se ha impuesto como el framework Python de referencia para las API rápidas y tipadas, gracias a su soporte nativo de la asincronía y de la documentación OpenAPI. Desplegarlo en su propio VPS le da el control sobre el número de workers, los WebSocket y la latencia, sin techo impuesto por un servicio gestionado. Esta guía cubre la totalidad de la puesta en producción: contenedorización multistage, base de datos con migraciones Alembic, CI/CD automatizado y resolución de errores frecuentes.

Contenido· ¿Por qué autoalojar FastAPI en un VPS?1/9
  1. 01¿Por qué autoalojar FastAPI en un VPS?
  2. 02Beneficios concretos de un FastAPI autoalojado
  3. 03Requisitos de hardware, software y versiones
  4. 04Desplegar FastAPI paso a paso
  5. 05Base de datos y migraciones
  6. 06CI/CD y despliegues automatizados
  7. 07Monitoreo y logs
  8. 08Resolución de errores frecuentes
  9. 09Desplegar FastAPI con un clic desde el Marketplace ServOrbit

¿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

  1. Inicializar el VPS y la aplicación

    Conéctese por SSH, instale Docker, clone su repositorio y cree un .env desde la plantilla .env.example incluida 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=production

    Compruebe 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).

  2. 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 /health en su aplicación para los healthchecks:

    @app.get("/health")
    async def health():
        return {"status": "ok"}
  3. Ajustar los workers de Gunicorn de forma óptima

    La fórmula (2 × núcleos) + 1 es 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 rango núcleos + 1 a 2 × núcleos es 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.yml
    CMD ["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.

  4. Orquestar los servicios con Docker Compose

    En docker-compose.yml, declare el servicio api, un servicio db PostgreSQL y un servicio redis. Utilice depends_on con condition: service_healthy para 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:
  5. Colocar Nginx como reverse proxy

    Configure un bloque de servidor con proxy_pass http://api:8000 y, 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;
        }
    }
  6. 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 /docs y /openapi.json en producción si la API no es pública.

  7. Lanzar y probar

    Arranque con docker compose up -d, compruebe el estado mediante el endpoint /health que 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 session

Para 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 current

En 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 head

La 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 api

Para 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 response

Para 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.

Despliegue su FastAPI Stack en unos minutos

La plantilla FastAPI Stack de ServOrbit instala Python 3.12, Uvicorn, Gunicorn, Nginx y PostgreSQL 16 en su VPS. Lance su API de inmediato.

¿Necesita ayuda?

Consulte nuestro centro de ayuda y nuestra FAQ, o contacte con nuestro equipo: llamada, WhatsApp o correo electrónico. Soporte en francés, inglés y árabe.

Escribir por WhatsAppse abre en una pestaña nueva