--- title: "Desplegar FastAPI con Uvicorn y Nginx en Ubuntu 24.04" description: "Despliega una API FastAPI en Ubuntu 24.04 con Uvicorn como servicio systemd, Nginx como proxy inverso y HTTPS gratuito de Let's Encrypt con Certbot." category: "API y Microservicios" language: es source: https://cubepath.com/es/docs/api-y-microservicios/despliegue-de-aplicacion-fastapi-en-linux updated: 2026-09-25 --- # Desplegar FastAPI con Uvicorn y Nginx en Ubuntu 24.04 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: ```bash sudo apt update sudo apt install python3-venv python3-pip nginx certbot python3-certbot-nginx ``` Comprueba las versiones: ```bash python3 --version nginx -v ``` ```output 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: ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable sudo ufw status ``` ```output 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: ```bash 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`: ```bash 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: ```bash python3 -m venv venv ``` Define las dependencias en `requirements.txt`: ```bash nano /opt/myapi/requirements.txt ``` ```text 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: ```bash /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: ```bash nano /opt/myapi/main.py ``` ```python 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: ```bash /opt/myapi/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 ``` Desde otra sesión SSH, haz una petición: ```bash curl http://127.0.0.1:8000/health ``` ```output {"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`: ```bash sudo nano /etc/myapi.env ``` ```ini APP_ENV=production ``` ```bash sudo chown root:fastapi /etc/myapi.env sudo chmod 640 /etc/myapi.env ``` Crea la unidad del servicio: ```bash sudo nano /etc/systemd/system/myapi.service ``` ```ini [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: ```bash sudo systemctl daemon-reload sudo systemctl enable --now myapi ``` Comprueba su estado: ```bash systemctl status myapi --no-pager ``` ```output ● 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: ```bash curl http://127.0.0.1:8000/health ``` Si el servicio no arranca, sus mensajes están en el journal: ```bash sudo journalctl -u myapi -n 50 --no-pager ``` ## Paso 4: Configurar Nginx como proxy inverso Crea un bloque de servidor para tu dominio: ```bash sudo nano /etc/nginx/sites-available/myapi ``` ```nginx 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: ```bash 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 ``` ```output 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: ```bash curl "http://your_domain/items/42?q=test" ``` ```output {"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: ```bash sudo certbot --nginx -d your_domain ``` Introduce un correo para los avisos de caducidad y acepta los términos. Al terminar verás: ```output 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á: ```bash sudo certbot renew --dry-run ``` Prueba la API por HTTPS: ```bash curl https://your_domain/health ``` ```output {"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`: ```python app = FastAPI(title="My API", docs_url=None, redoc_url=None, openapi_url=None) ``` Reinicia el servicio para aplicar cualquier cambio de código: ```bash 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: ```bash 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: ```bash 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.