FastAPI es un framework de Python para construir APIs con validación de datos basada en tipos y documentación OpenAPI automática. En producción no se ejecuta con el servidor de desarrollo, sino con Uvicorn gestionado por systemd, detrás de Nginx, que termina TLS y reenvía las peticiones. En este tutorial desplegarás una API FastAPI en Ubuntu 24.04 en un entorno virtual de Python, la ejecutarás como servicio con varios workers, la publicarás a través de Nginx y la protegerás con un certificado de Let's Encrypt.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS (incluye Python 3.12), por ejemplo un VPS de CubePath con al menos 1 GB de RAM.
  • Un usuario no root con privilegios sudo.
  • Un dominio con un registro DNS A que apunte a la IP pública del servidor. En esta guía se usa your_domain como marcador: sustitúyelo por tu dominio.
  • Los puertos 22, 80 y 443 accesibles desde internet.

Paso 1: Instalar los paquetes del sistema

Instala Python con soporte para entornos virtuales, Nginx y Certbot con su plugin para Nginx:

sudo apt update
sudo apt install python3-venv python3-pip nginx certbot python3-certbot-nginx

Comprueba las versiones:

python3 --version
nginx -v
Python 3.12.3
nginx version: nginx/1.24.0 (Ubuntu)

Abre el firewall para SSH y para HTTP/HTTPS de Nginx. Permite SSH antes de activar UFW para no perder la conexión:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
Nginx Full                 ALLOW       Anywhere

Paso 2: Crear la aplicación y su entorno virtual

La API se ejecutará con un usuario de sistema sin privilegios ni shell, de forma que un fallo en la aplicación no dé acceso al resto del servidor:

sudo adduser --system --group --no-create-home fastapi

Crea el directorio de la aplicación en /opt/myapi y hazte propietario para poder desplegar código sin sudo:

sudo mkdir -p /opt/myapi
sudo chown "$USER":"$USER" /opt/myapi
cd /opt/myapi

Crea un entorno virtual. Así las dependencias de la API quedan aisladas de los paquetes de Python del sistema:

python3 -m venv venv

Define las dependencias en requirements.txt:

nano /opt/myapi/requirements.txt
fastapi
uvicorn[standard]

uvicorn[standard] añade uvloop y httptools, que mejoran el rendimiento. En un proyecto real, fija las versiones (por ejemplo, con pip freeze > requirements.txt tras probarlas) para que cada despliegue instale exactamente lo mismo.

Instálalas en el entorno virtual:

/opt/myapi/venv/bin/pip install -r /opt/myapi/requirements.txt

Crea una API mínima con un endpoint de salud y otro de ejemplo:

nano /opt/myapi/main.py
from fastapi import FastAPI

app = FastAPI(title="My API")


@app.get("/health")
def health():
    return {"status": "ok"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

Pruébala en primer plano:

/opt/myapi/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000

Desde otra sesión SSH, haz una petición:

curl http://127.0.0.1:8000/health
{"status":"ok"}

Vuelve a la primera sesión y detén Uvicorn con Ctrl+C.

Paso 3: Ejecutar la API como servicio de systemd

systemd arrancará la API al iniciar el servidor, la reiniciará si falla y guardará sus logs en el journal.

Guarda la configuración y los secretos de la aplicación (claves de API, cadenas de conexión a la base de datos) en un archivo de entorno legible solo por root y por el grupo fastapi:

sudo nano /etc/myapi.env
APP_ENV=production
sudo chown root:fastapi /etc/myapi.env
sudo chmod 640 /etc/myapi.env

Crea la unidad del servicio:

sudo nano /etc/systemd/system/myapi.service
[Unit]
Description=My FastAPI application
After=network.target

[Service]
User=fastapi
Group=fastapi
WorkingDirectory=/opt/myapi
EnvironmentFile=/etc/myapi.env
ExecStart=/opt/myapi/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 2 --proxy-headers
Restart=always
RestartSec=5

NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true

[Install]
WantedBy=multi-user.target

Puntos clave de esta unidad:

  • --host 127.0.0.1: Uvicorn solo escucha en local; desde internet se accede únicamente a través de Nginx.
  • --workers 2: arranca dos procesos. Como punto de partida, usa tantos workers como vCPU tenga el servidor y ajusta según la memoria que consuma cada uno.
  • --proxy-headers: Uvicorn confía en las cabeceras X-Forwarded-For y X-Forwarded-Proto que envía Nginx (por defecto solo si vienen de 127.0.0.1), así la aplicación ve la IP real del cliente y el esquema https.
  • Las directivas Protect*, PrivateTmp y NoNewPrivileges restringen lo que el proceso puede tocar en el sistema.

Carga la unidad, habilítala y arráncala:

sudo systemctl daemon-reload
sudo systemctl enable --now myapi

Comprueba su estado:

systemctl status myapi --no-pager
● myapi.service - My FastAPI application
     Loaded: loaded (/etc/systemd/system/myapi.service; enabled; preset: enabled)
     Active: active (running) since Thu 2026-09-25 10:12:41 UTC; 4s ago
   Main PID: 5123 (uvicorn)

Y vuelve a probar el endpoint:

curl http://127.0.0.1:8000/health

Si el servicio no arranca, sus mensajes están en el journal:

sudo journalctl -u myapi -n 50 --no-pager

Paso 4: Configurar Nginx como proxy inverso

Crea un bloque de servidor para tu dominio:

sudo nano /etc/nginx/sites-available/myapi
server {
    listen 80;
    listen [::]:80;
    server_name your_domain;

    client_max_body_size 10m;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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_read_timeout 60s;
    }
}

client_max_body_size limita el tamaño de las peticiones (súbelo si la API recibe archivos grandes) y proxy_read_timeout es el tiempo máximo que Nginx espera la respuesta de la API.

Activa el sitio, desactiva el sitio por defecto, comprueba la sintaxis y recarga Nginx:

sudo ln -s /etc/nginx/sites-available/myapi /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Prueba la API a través de Nginx desde tu equipo:

curl "http://your_domain/items/42?q=test"
{"item_id":42,"q":"test"}

Paso 5: Activar HTTPS con Let's Encrypt

Certbot obtiene un certificado gratuito, modifica el bloque de Nginx para servir HTTPS y redirige HTTP a HTTPS:

sudo certbot --nginx -d your_domain

Introduce un correo para los avisos de caducidad y acepta los términos. Al terminar verás:

Successfully deployed certificate for your_domain to /etc/nginx/sites-enabled/myapi
Congratulations! You have successfully enabled HTTPS on https://your_domain

El paquete de Certbot instala un temporizador de systemd que renueva los certificados automáticamente. Comprueba que la renovación funcionará:

sudo certbot renew --dry-run

Prueba la API por HTTPS:

curl https://your_domain/health
{"status":"ok"}

La documentación interactiva que genera FastAPI está disponible en https://your_domain/docs.

Ocultar la documentación en producción

Si la API no es pública, puedes desactivar /docs, /redoc y el esquema OpenAPI al crear la aplicación en main.py:

app = FastAPI(title="My API", docs_url=None, redoc_url=None, openapi_url=None)

Reinicia el servicio para aplicar cualquier cambio de código:

sudo systemctl restart myapi

Paso 6: Desplegar nuevas versiones

Cada despliegue sigue los mismos pasos: actualizar el código, instalar dependencias y reiniciar el servicio. Si usas Git:

cd /opt/myapi
git pull
/opt/myapi/venv/bin/pip install -r requirements.txt
sudo systemctl restart myapi

Comprueba después que el servicio sigue activo y responde:

systemctl is-active myapi
curl -fsS https://your_domain/health

Durante los segundos del reinicio Nginx puede devolver 502. Si necesitas despliegues sin cortes, el siguiente paso es ejecutar dos instancias y alternarlas detrás de un upstream de Nginx.

Solución de problemas

Nginx devuelve 502 Bad Gateway. Uvicorn no está en marcha o no escucha en 127.0.0.1:8000. Comprueba systemctl status myapi y sudo ss -tlnp | grep 8000, y revisa sudo journalctl -u myapi.

El servicio falla con ModuleNotFoundError. La dependencia no está instalada en el entorno virtual, o ExecStart no apunta a /opt/myapi/venv/bin/uvicorn. Instala con el pip del entorno virtual, no con el del sistema.

El servicio falla con Error loading ASGI app. Could not import module "main". WorkingDirectory no es el directorio que contiene main.py, o el usuario fastapi no puede leer los archivos. Comprueba los permisos con sudo -u fastapi cat /opt/myapi/main.py.

La aplicación ve 127.0.0.1 como IP de todos los clientes. Falta --proxy-headers en ExecStart o las cabeceras X-Forwarded-* en Nginx. Revisa ambos archivos y reinicia los dos servicios.

Certbot falla con un error de validación. El registro DNS aún no apunta al servidor o el puerto 80 está cerrado. Comprueba dig +short your_domain y sudo ufw status.

Conclusión

Tu API FastAPI se ejecuta ahora como servicio de systemd con varios workers de Uvicorn, detrás de Nginx y con HTTPS de Let's Encrypt renovado automáticamente. Como siguientes pasos, puedes conectar la API a una base de datos como PostgreSQL, añadir limitación de peticiones en Nginx con limit_req o empaquetar la aplicación en una imagen Docker para desplegarla de forma reproducible.