--- title: "Cómo instalar Fn Project serverless en Ubuntu 24.04" description: "Instala Fn Project en Ubuntu 24.04 con Docker, despliega una función Python, publícala con un trigger HTTP tras Nginx con HTTPS y arráncala con systemd." category: "Serverless en VPS" language: es source: https://cubepath.com/es/docs/serverless-en-vps/instalacion-de-fn-project-plataforma-serverless updated: 2026-09-25 --- # Cómo instalar Fn Project serverless en Ubuntu 24.04 Fn Project es una plataforma serverless de código abierto en la que cada función es una imagen de contenedor. El servidor Fn recibe las invocaciones, arranca el contenedor de la función y lo mantiene caliente unos segundos para atender las siguientes peticiones sin coste de arranque. En este tutorial instalarás la CLI y el servidor Fn en Ubuntu 24.04, desplegarás una función Python, la publicarás con un trigger HTTP detrás de Nginx con HTTPS y configurarás el servidor para que arranque con el sistema. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS de 64 bits, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Al menos 2 GB de RAM (4 GB si vas a construir funciones en Java). - Docker Engine instalado desde el repositorio oficial, con tu usuario en el grupo `docker`. - Para la parte de HTTPS: un dominio, por ejemplo `fn.your_domain`, con un registro DNS A apuntando a la IP del servidor, y los puertos 80 y 443 abiertos. ## Paso 1: Comprobar Docker El servidor Fn se ejecuta como contenedor y construye las funciones con Docker, así que tu usuario debe poder usarlo sin `sudo`: ```bash docker run --rm hello-world ``` ```output Hello from Docker! This message shows that your installation appears to be working correctly. ``` Si recibes `permission denied`, ejecuta `sudo usermod -aG docker "$USER"`, cierra la sesión y vuelve a entrar. ## Paso 2: Instalar la CLI de Fn El proyecto distribuye la CLI con un script de instalación que detecta tu sistema, descarga el binario `fn` desde GitHub y lo copia en `/usr/local/bin`. Descárgalo primero para poder revisarlo: ```bash curl -fsSL https://raw.githubusercontent.com/fnproject/cli/master/install -o fn-install.sh less fn-install.sh ``` Cuando hayas comprobado lo que hace, ejecútalo con `sudo` (necesita escribir en `/usr/local/bin`) y borra el script: ```bash sudo sh fn-install.sh rm fn-install.sh ``` Verifica la instalación: ```bash fn version ``` ```output Client version is latest version: 0.6.x Server version: ? ``` El servidor aparece como `?` porque todavía no está en marcha. ## Paso 3: Arrancar el servidor Fn `fn start` descarga la imagen `fnproject/fnserver` y la ejecuta con el socket de Docker montado. Guarda la base de datos del servidor en `~/.fn/data`, por lo que las aplicaciones y funciones persisten entre reinicios. Arráncalo en segundo plano: ```bash fn start -d ``` Comprueba que el contenedor está en marcha y que la API responde en el puerto 8080: ```bash docker ps --filter name=fnserver curl -s http://127.0.0.1:8080/version ``` ```output CONTAINER ID IMAGE STATUS PORTS NAMES b71c0e5a9d12 fnproject/fnserver Up 10 seconds 0.0.0.0:8080->8080/tcp fnserver {"version":"0.3.7xx"} ``` > Importante: la API de Fn no tiene autenticación y el puerto 8080 queda publicado en todas las interfaces. Docker gestiona sus propias reglas de iptables, así que UFW no lo bloquea. Bloquea el puerto 8080 en el firewall de red de tu proveedor (en CubePath, desde el panel) y expón solo los triggers HTTP a través de Nginx, como verás en el paso 8. ## Paso 4: Crear una aplicación y una función Python En Fn, las funciones se agrupan en *aplicaciones*, que comparten configuración y triggers bajo un mismo prefijo. Crea la aplicación: ```bash fn create app demo fn list apps ``` ```output Successfully created app: demo NAME ID demo 01J... ``` Genera el esqueleto de una función Python con `fn init`: ```bash mkdir -p ~/fn && cd ~/fn fn init --runtime python saludo cd saludo ls ``` ```output func.py func.yaml requirements.txt ``` - `func.py` contiene el código de la función. - `func.yaml` describe la función: nombre, versión, runtime, imágenes de build y de ejecución, memoria y triggers. - `requirements.txt` lista las dependencias de pip; incluye el FDK de Python (`fdk`), que gestiona la comunicación con el servidor. Sustituye el contenido de `func.py` por esta función, que calcula el total de un pedido a partir de un JSON: ```bash nano func.py ``` ```python import io import json import logging from fdk import response PRECIOS = {"vps": 19.99, "baremetal": 199.99} def handler(ctx, data: io.BytesIO = None): try: pedido = json.loads(data.getvalue()) except (ValueError, AttributeError): pedido = {} producto = pedido.get("producto", "vps") cantidad = int(pedido.get("cantidad", 1)) logging.getLogger().info("Pedido recibido: %s x %d", producto, cantidad) resultado = { "producto": producto, "cantidad": cantidad, "total": round(PRECIOS.get(producto, 0) * cantidad, 2), } return response.Response( ctx, response_data=json.dumps(resultado), headers={"Content-Type": "application/json"}, ) ``` ## Paso 5: Ajustar límites y añadir un trigger HTTP Abre `func.yaml`. Conserva las líneas que ha generado `fn init` (`schema_version`, `name`, `version`, `runtime`, `build_image`, `run_image` y `entrypoint`) y añade o ajusta al final los límites y el trigger: ```bash nano func.yaml ``` ```yaml memory: 256 timeout: 30 idle_timeout: 60 triggers: - name: saludo type: http source: /pedido ``` - `memory`: memoria máxima del contenedor de la función, en MB. - `timeout`: tiempo máximo de una invocación, en segundos. - `idle_timeout`: segundos que el contenedor permanece caliente tras la última invocación. Mientras siga vivo, las nuevas peticiones se atienden sin *cold start*. - `triggers`: publica la función en la ruta `/t/demo/pedido` del servidor. ## Paso 6: Desplegar la función `fn deploy` construye la imagen de la función, incrementa la versión en `func.yaml` y la registra en el servidor. Con `--local` la imagen se queda en el Docker local en vez de subirse a un registro, que es lo adecuado con un solo servidor: ```bash fn deploy --app demo --local ``` ```output Deploying saludo to app: demo Bumped to version 0.0.2 Building image saludo:0.0.2 ... Updating function saludo using image saludo:0.0.2... Successfully created function: saludo with saludo:0.0.2 Successfully created trigger: saludo Trigger Endpoint: http://localhost:8080/t/demo/pedido ``` El primer despliegue tarda más porque descarga las imágenes base de Python. Comprueba que la función y el trigger están registrados: ```bash fn list functions demo fn list triggers demo ``` ## Paso 7: Invocar la función Invoca la función con la CLI, pasando el JSON por la entrada estándar: ```bash echo '{"producto": "vps", "cantidad": 3}' | fn invoke demo saludo --content-type application/json ``` ```output {"producto": "vps", "cantidad": 3, "total": 59.97} ``` Y ahora a través del trigger HTTP, que es como la llamarán otras aplicaciones: ```bash curl -s -X POST http://127.0.0.1:8080/t/demo/pedido \ -H 'Content-Type: application/json' \ -d '{"producto": "baremetal", "cantidad": 2}' ``` ```output {"producto": "baremetal", "cantidad": 2, "total": 399.98} ``` Si repites la petición en pocos segundos, notarás que responde más rápido: el contenedor sigue caliente. Puedes verlo con `docker ps`, donde aparece un contenedor de la función mientras dura el `idle_timeout`. Para pasar configuración a la función sin tocar el código, usa variables de configuración, que llegan como variables de entorno: ```bash fn config function demo saludo MONEDA EUR fn list config function demo saludo ``` ## Paso 8: Publicar los triggers con Nginx y HTTPS Nginx actuará como proxy inverso: servirá HTTPS en tu dominio y reenviará solo las rutas `/t/`, que son las de los triggers. La API de gestión (`/v2/`) no se publica. Instala Nginx y Certbot: ```bash sudo apt update sudo apt install -y nginx certbot python3-certbot-nginx sudo ufw allow 'Nginx Full' ``` Crea el bloque de servidor, sustituyendo `fn.your_domain` por tu dominio: ```bash sudo nano /etc/nginx/sites-available/fn ``` ```nginx server { listen 80; listen [::]:80; server_name fn.your_domain; location /t/ { proxy_pass http://127.0.0.1:8080; 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; } location / { return 404; } } ``` Activa el sitio, valida la configuración y recarga Nginx: ```bash sudo ln -s /etc/nginx/sites-available/fn /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` Obtén un certificado de Let's Encrypt. Certbot modificará el bloque para escuchar en el 443 y redirigir HTTP a HTTPS: ```bash sudo certbot --nginx -d fn.your_domain ``` Prueba la función desde tu equipo: ```bash curl -s -X POST https://fn.your_domain/t/demo/pedido \ -H 'Content-Type: application/json' \ -d '{"producto": "vps", "cantidad": 1}' ``` ```output {"producto": "vps", "cantidad": 1, "total": 19.99} ``` Una petición a `https://fn.your_domain/v2/apps` debe devolver `404`, lo que confirma que la API de gestión no está expuesta. ## Paso 9: Arrancar el servidor Fn con systemd El contenedor que lanza `fn start` se elimina al detenerse y no vuelve a arrancar tras un reinicio del servidor. Crea una unidad de systemd que ejecute `fn start` en primer plano con tu usuario. Primero, detén el servidor actual: ```bash fn stop ``` Crea la unidad, sustituyendo `your_user` por tu usuario (debe pertenecer al grupo `docker`): ```bash sudo nano /etc/systemd/system/fnserver.service ``` ```ini [Unit] Description=Fn Project server Requires=docker.service After=docker.service network-online.target Wants=network-online.target [Service] User=your_user ExecStart=/usr/local/bin/fn start ExecStop=/usr/local/bin/fn stop Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target ``` Recarga systemd, habilita el servicio y arráncalo: ```bash sudo systemctl daemon-reload sudo systemctl enable --now fnserver systemctl status fnserver --no-pager ``` ```output ● fnserver.service - Fn Project server Loaded: loaded (/etc/systemd/system/fnserver.service; enabled; preset: enabled) Active: active (running) since ... ``` Comprueba que la aplicación sigue ahí, ya que los datos están en `~/.fn/data`: ```bash fn list functions demo ``` Los logs del servidor se consultan con `journalctl -u fnserver -f`. ## Solución de problemas **`fn deploy` falla con `Cannot connect to the Docker daemon`.** Tu usuario no está en el grupo `docker` o no has vuelto a iniciar sesión. Comprueba con `groups`. **`fn start` falla porque el puerto 8080 está en uso.** Localiza el proceso con `sudo ss -tlnp | grep ':8080'` y detenlo, o cambia el puerto del otro servicio. **La invocación devuelve un error de timeout.** Aumenta `timeout` en `func.yaml`, vuelve a desplegar con `fn deploy --app demo --local` y revisa los logs de la función. Por defecto, Fn envía la salida de las funciones a los logs del servidor, visibles con `journalctl -u fnserver` o `docker logs fnserver`. **Nginx devuelve `502 Bad Gateway`.** El servidor Fn no está en marcha. Revisa `systemctl status fnserver` y `curl -s http://127.0.0.1:8080/version`. ## Conclusión Tienes Fn Project funcionando en Ubuntu 24.04 con una función Python desplegada, accesible mediante un trigger HTTP publicado por Nginx con HTTPS y un servidor que arranca con el sistema. Como siguientes pasos, puedes crear funciones en Go, Node.js o Java con `fn init --runtime`, usar `fn config app` para compartir configuración entre funciones de una aplicación, o subir tus imágenes a un registro privado para desplegar las mismas funciones en varios servidores.