Respuesta rápida: en producción, FastAPI corre con fastapi run y varios workers de Uvicorn dentro de un contenedor propio, detrás de Nginx:
fastapi run app/main.py \
  --host 0.0.0.0 \
  --port 8000 \
  --workers 4

Separa los endpoints de liveness y readiness, y deja que Nginx termine el TLS: el contenedor no se publica directamente en Internet.

Nivel: intermedio · Requiere: Docker y Nginx

Los cinco pasos para desplegar FastAPI en Docker: Dockerfile, usuario no root, construir y ejecutar, escalar workers y probar la salud
FAST API en servidores de desarrollo

Ejecutar una API con fastapi dev, publicar Uvicorn directamente en Internet o depender de un único proceso puede provocar problemas de capacidad, TLS y recuperación ante fallos.

Para producción, FastAPI recomienda construir una imagen propia y ejecutar fastapi run, que utiliza Uvicorn. La terminación TLS puede delegarse a Nginx, un balanceador o el proveedor cloud. La estrategia de workers depende del entorno: varios procesos pueden resultar útiles en un servidor único, mientras que en Kubernetes suele convenir un proceso por contenedor y varias réplicas. Documentación de FastAPI sobre contenedores.

Cuándo conviene aplicarlo

Este esquema resulta útil para:

  • APIs y microservicios Python.
  • Backends para aplicaciones web o móviles.
  • Servicios internos con requisitos de disponibilidad.
  • Despliegues en Docker Compose, Kubernetes o servicios gestionados.

Requisitos previos

Necesitarás:

  • Un proyecto FastAPI funcional.
  • Dependencias bloqueadas en requirements.txt, uv.lock o un formato equivalente.
  • Docker y acceso al registry.
  • Variables de entorno definidas fuera de la imagen.
  • Nginx o un balanceador si necesitas TLS y proxy inverso.
  • Endpoints de salud y una estrategia de logs.
  • Límites de CPU y memoria medidos para la aplicación.

Arquitectura recomendada

El flujo habitual conecta:

  1. Cliente.
  2. Nginx o balanceador con terminación TLS.
  3. Red privada de contenedores.
  4. Contenedor FastAPI.
  5. Uno o varios workers.
  6. Logs, métricas y comprobaciones de salud.

Solo el proxy debería publicar un puerto hacia Internet. FastAPI puede escuchar en 0.0.0.0:8000 dentro de la red privada sin exponer ese puerto directamente en el host.

Paso 1: preparar el Dockerfile

El siguiente ejemplo utiliza Python 3.12, instala primero las dependencias para aprovechar la caché y ejecuta la aplicación con un usuario sin privilegios:

# syntax=docker/dockerfile:1
FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

WORKDIR /app

RUN addgroup --system --gid 10001 app \
    && adduser --system \
        --uid 10001 \
        --ingroup app \
        --home /nonexistent \
        --no-create-home \
        app

COPY requirements.txt /app/requirements.txt

RUN pip install \
    --no-cache-dir \
    --upgrade \
    -r /app/requirements.txt

COPY --chown=10001:10001 ./app /app/app

USER 10001:10001

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
    CMD ["python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health/live', timeout=2)"]

CMD ["fastapi", "run", "app/main.py", "--host", "0.0.0.0", "--port", "8000"]

La forma JSON de CMD permite que el proceso reciba correctamente las señales de terminación y ejecute los eventos de cierre de FastAPI. COPY --chown evita dejar el código bajo propiedad de root. Referencia de Dockerfile.

Agrega también un .dockerignore:

.git
.github
.env
.env.*
__pycache__/
*.py[cod]
.venv/
tests/
dist/

No excluyas migraciones, certificados públicos u otros archivos que la aplicación necesite en tiempo de ejecución.

Paso 2: implementar liveness y readiness

Liveness responde si el proceso ASGI está funcionando. No debe consultar servicios externos:

from fastapi import FastAPI, HTTPException

app = FastAPI()


@app.get("/health/live", include_in_schema=False)
async def liveness() -> dict[str, str]:
    return {"status": "ok"}

Readiness indica si la instancia puede recibir tráfico. Aquí sí puede realizar una comprobación ligera de dependencias:

@app.get("/health/ready", include_in_schema=False)
async def readiness() -> dict[str, str]:
    try:
        await check_database()
    except Exception as exc:
        raise HTTPException(
            status_code=503,
            detail="service not ready",
        ) from exc

    return {"status": "ready"}

check_database() representa una función específica de la aplicación. Debe utilizar un timeout corto y no ejecutar consultas costosas.

Evita devolver versiones, cadenas de conexión o detalles internos del error. Si estos endpoints no necesitan ser públicos, restríngelos a la red interna o al sistema de monitoreo.

Paso 3: construir y probar la imagen

Construye una imagen local:

docker build \
  --pull \
  --tag "fastapi-app:${GIT_SHA:-local}" \
  .

No es necesario escapar los dos puntos de la etiqueta.

Ejecuta el contenedor limitando recursos y publicando el puerto únicamente en la interfaz local:

docker run -d \
  --name fastapi-app \
  --restart unless-stopped \
  --stop-timeout 30 \
  --memory 1g \
  --cpus 2 \
  --env-file /ruta/segura/fastapi.env \
  --publish 127.0.0.1:8000:8000 \
  "fastapi-app:${GIT_SHA:-local}"

Los contenedores no tienen límites de CPU o memoria de forma predeterminada, por lo que deben definirse después de medir el consumo real. Límites de recursos en Docker.

Comprueba el estado:

curl --fail --silent --show-error \
  http://127.0.0.1:8000/health/live

docker inspect \
  --format '{{json .State.Health}}' \
  fastapi-app |
jq .

docker logs --tail 100 fastapi-app

Paso 4: elegir la estrategia de workers

En un servidor único o un despliegue sencillo con Docker Compose, puedes ejecutar varios workers:

fastapi run app/main.py \
  --host 0.0.0.0 \
  --port 8000 \
  --workers 4

Cada worker es un proceso separado y carga su propia copia de la aplicación. Cuatro workers no implican automáticamente cuatro veces más rendimiento, pero sí pueden acercarse a cuatro veces el consumo base de memoria.

Empieza con uno o dos workers y mide:

  • Latencia p50, p95 y p99.
  • Uso de CPU.
  • Memoria por proceso.
  • Errores y timeouts.
  • Tiempo de arranque.
  • Capacidad de la base de datos y del pool de conexiones.

En Kubernetes, Nomad o un sistema que ya replica contenedores, ejecuta normalmente un worker por contenedor y escala réplicas. FastAPI documenta explícitamente ambas estrategias. Workers y replicación.

Las migraciones de base de datos no deben ejecutarse una vez por worker. Ejecútalas como una tarea previa y controlada antes de habilitar las nuevas instancias.

Paso 5: configurar Nginx como proxy

Una configuración base puede utilizar un nombre DNS interno de Docker:

upstream fastapi_backend {
    server fastapi:8000;
    keepalive 32;
}

server {
    listen 443 ssl;
    server_name api.ejemplo.com;

    ssl_certificate     /etc/nginx/tls/fullchain.pem;
    ssl_certificate_key /etc/nginx/tls/private-key.pem;

    location / {
        proxy_pass http://fastapi_backend;
        proxy_http_version 1.1;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_connect_timeout 5s;
        proxy_read_timeout 60s;
    }
}

Nginx permite modificar explícitamente Host y las cabeceras enviadas al servidor upstream mediante proxy_set_header. Documentación oficial de Nginx.

Valida antes de recargar:

nginx -t
nginx -s reload

Si utilizas WebSockets, agrega la configuración de Upgrade y Connection correspondiente. Si publicas la aplicación bajo un prefijo como /api, configura también root_path.

Paso 6: confiar en el proxy correcto

Para que FastAPI interprete HTTPS, host e IP original, habilita las cabeceras proxy y especifica las direcciones confiables:

fastapi run app/main.py \
  --host 0.0.0.0 \
  --port 8000 \
  --proxy-headers \
  --forwarded-allow-ips="172.20.0.0/24"

Sustituye el rango por la red real donde se encuentra Nginx.

También puede utilizarse --forwarded-allow-ips="*", pero únicamente cuando el puerto de FastAPI no admite conexiones directas y la red está suficientemente aislada. De otro modo, un cliente podría enviar cabeceras X-Forwarded-* falsas. FastAPI detrás de un proxy.

Paso 7: validar el despliegue completo

Realiza las comprobaciones en este orden:

curl --fail http://127.0.0.1:8000/health/live
curl --fail http://127.0.0.1:8000/health/ready
curl --fail https://api.ejemplo.com/health/ready

Después verifica:

  • Que una redirección genere una URL https://.
  • Que FastAPI no sea accesible desde Internet por el puerto 8000.
  • Que Nginx rechace certificados o configuraciones inválidas.
  • Que el contenedor pase a unhealthy cuando falle liveness.
  • Que readiness responda 503 cuando una dependencia no esté disponible.
  • Que un docker stop produzca un cierre ordenado.
  • Que los logs no contengan tokens, contraseñas ni cuerpos sensibles.
  • Que el servicio se recupere después de reiniciar un worker o contenedor.

Mantén la versión anterior de la imagen disponible hasta completar las pruebas de humo y confirmar las métricas.


Servidor único con Nginx y un contenedor FastAPI de cuatro workers, frente a un orquestador con cuatro réplicas de un worker cada una
Organigrama de despliegue de fast api

Problemas frecuentes

El contenedor reinicia continuamente

Comprueba los imports, variables obligatorias, permisos de archivos y el comando de inicio:

docker inspect fastapi-app
docker logs --tail 200 fastapi-app

Distingue entre un fallo de la aplicación, un healthcheck fallido y una terminación por falta de memoria.

Nginx devuelve 502

Verifica que FastAPI escuche en 0.0.0.0:8000, que ambos contenedores compartan red y que fastapi:8000 resuelva desde Nginx.

Las redirecciones utilizan HTTP

Confirma X-Forwarded-Proto, --proxy-headers y --forwarded-allow-ips. El problema suele aparecer cuando Uvicorn no confía en la dirección del proxy.

El contenedor queda sin memoria

Cada worker replica el estado de la aplicación. Reduce workers, revisa modelos o cachés cargados al inicio y establece un límite compatible con el consumo máximo observado.

El healthcheck pasa, pero la API no funciona

Probablemente solo se está verificando liveness. Agrega readiness con las dependencias imprescindibles y comprueba que el balanceador utilice el endpoint correcto.

El cliente entra por HTTPS a Nginx, que reenvía por HTTP a FastAPI en la red privada: el puerto 8000 no queda expuesto a internet
Diagrama de implementacion de FAST API

Buenas prácticas para producción

  • Construye la imagen desde una versión fijada de Python.
  • Bloquea versiones y verifica dependencias en CI.
  • Ejecuta como usuario no root.
  • Mantén secretos fuera de la imagen.
  • Publica imágenes con tag de commit y conserva su digest.
  • Expón únicamente Nginx o el balanceador.
  • Separa liveness de readiness.
  • Envía logs estructurados a stdout y stderr.
  • Define timeouts y límites de recursos.
  • Mide latencia, errores, saturación y reinicios.
  • Prueba el cierre ordenado durante cada release.
  • Usa un worker por contenedor cuando el orquestador gestione réplicas.

Cloud Servers by Donweb

Ya tienes FastAPI en Docker detrás de Nginx. Ahora necesitas dónde correrlo de verdad.
Alojamiento ultra rápido, escalable y con alta disponibilidad.

  • Performance que te sorprenderá
  • Rápida escalabilidad y sin limitaciones
  • Arquitectura de alta disponibilidad
  • Soporte experto y ejecutivos de cuenta
  • Pagos en tu moneda y facturación local

Despliega tu API en un Cloud Server