OpenFaaS es una plataforma de código abierto para ejecutar funciones serverless empaquetadas como contenedores, invocables por HTTP y de forma asíncrona. Su variante faasd ejecuta OpenFaaS sobre containerd en un único servidor, sin Kubernetes, lo que la hace ideal para un VPS. En esta guía instalarás faasd en Ubuntu 24.04, lo publicarás con HTTPS mediante Caddy y desplegarás una función en Python construida desde tu equipo de desarrollo.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 2 GB de RAM y 2 vCPU.
  • Un usuario no root con privilegios sudo.
  • Un dominio con un registro A apuntando a la IP del servidor. En esta guía se usa faas.your_domain.
  • En tu equipo de desarrollo: Docker (con Buildx) y una cuenta en un registro de contenedores, por ejemplo Docker Hub o GitHub Container Registry. Las funciones se construyen ahí y el servidor las descarga del registro.

Paso 1: Instalar faasd

El proyecto proporciona un script de instalación que descarga containerd, los plugins CNI, faasd y la CLI faas-cli, y crea los servicios de systemd. Instala Git y clona el repositorio:

sudo apt update
sudo apt install git
git clone https://github.com/openfaas/faasd --depth=1
cd faasd

Antes de ejecutarlo, revisa el script para saber qué va a instalar:

less hack/install.sh

Ejecuta la instalación:

sudo ./hack/install.sh

Al terminar, faasd queda ejecutándose con dos servicios de systemd: faasd-provider, que gestiona los contenedores de las funciones, y faasd, que arranca los componentes principales (gateway, NATS, queue-worker y Prometheus). Comprueba que ambos están activos:

sudo systemctl status faasd faasd-provider --no-pager
● faasd.service - faasd
     Loaded: loaded (/lib/systemd/system/faasd.service; enabled; preset: enabled)
     Active: active (running) since ...
● faasd-provider.service - faasd-provider
     Loaded: loaded (/lib/systemd/system/faasd-provider.service; enabled; preset: enabled)
     Active: active (running) since ...

Verifica también que faas-cli está instalado:

faas-cli version

La primera vez faasd tarda uno o dos minutos en descargar las imágenes de sus componentes. Puedes seguir el proceso con sudo journalctl -u faasd -f.

Paso 2: Iniciar sesión en el gateway

El gateway de OpenFaaS escucha en el puerto 8080. Las rutas de administración (/system/...) están protegidas con autenticación básica; el usuario es admin y la contraseña generada durante la instalación está en /var/lib/faasd/secrets/basic-auth-password.

Inicia sesión desde el propio servidor pasando la contraseña por la entrada estándar, para que no quede en el historial de la shell:

sudo cat /var/lib/faasd/secrets/basic-auth-password | faas-cli login --password-stdin
Calling the OpenFaaS server to validate the credentials...
credentials saved for admin http://127.0.0.1:8080

Despliega una función de ejemplo del catálogo oficial para comprobar que todo funciona:

faas-cli store deploy figlet
faas-cli list
Function                        Invocations     Replicas
figlet                          0               1

Invócala:

echo "CubePath" | faas-cli invoke figlet

La salida es el texto "CubePath" dibujado en caracteres ASCII. Si la función aparece con 0 réplicas, espera unos segundos a que se descargue la imagen y repite faas-cli list.

Paso 3: Publicar el gateway con HTTPS

El puerto 8080 sirve HTTP sin cifrar, y por él viajan la contraseña de administración y los datos de tus funciones. En lugar de abrirlo, pon delante Caddy, que obtiene y renueva automáticamente un certificado de Let's Encrypt.

Configura el cortafuegos para permitir SSH, HTTP y HTTPS, dejando el 8080 cerrado:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Instala Caddy desde los repositorios de Ubuntu:

sudo apt install caddy

Sustituye el contenido del archivo de configuración:

sudo nano /etc/caddy/Caddyfile
faas.your_domain {
    reverse_proxy 127.0.0.1:8080
}

Recarga Caddy y comprueba que ha obtenido el certificado:

sudo systemctl reload caddy
sudo journalctl -u caddy --since "5 minutes ago" | grep -i certificate

Desde cualquier equipo, el endpoint de salud del gateway debe responder por HTTPS:

curl -i https://faas.your_domain/healthz
HTTP/2 200
...
OK

La interfaz web de OpenFaaS está disponible en https://faas.your_domain/ui/ con el usuario admin y la misma contraseña.

Paso 4: Crear una función en Python

A partir de aquí trabajas en tu equipo de desarrollo. Instala faas-cli allí siguiendo las instrucciones de la documentación de OpenFaaS para tu sistema (en macOS, brew install faas-cli) e inicia sesión contra tu gateway. Copia la contraseña de forma segura desde el servidor y pásala por la entrada estándar:

export OPENFAAS_URL=https://faas.your_domain
faas-cli login --password-stdin < ~/openfaas-password.txt

Borra el archivo de la contraseña cuando hayas iniciado sesión. Inicia sesión también en tu registro de contenedores con docker login.

Descarga la plantilla python3-http, basada en Flask, que da acceso al método, las cabeceras y el cuerpo de la petición:

mkdir -p ~/funciones && cd ~/funciones
faas-cli template store pull python3-http

Crea la función. Con --prefix indicas tu usuario u organización en el registro, que se usará para nombrar la imagen:

faas-cli new saludo --lang python3-http --prefix your_registry_user

El comando crea el directorio saludo/ con handler.py y requirements.txt, y el archivo de despliegue stack.yaml (en versiones antiguas de faas-cli, saludo.yml). Edita el código de la función:

nano saludo/handler.py
import json
import os


def handle(event, context):
    try:
        datos = json.loads(event.body or b"{}")
    except json.JSONDecodeError:
        return {
            "statusCode": 400,
            "body": json.dumps({"detail": "El cuerpo debe ser JSON"}),
            "headers": {"Content-Type": "application/json"},
        }

    nombre = datos.get("nombre", "mundo")
    respuesta = {
        "mensaje": f"Hola, {nombre}",
        "metodo": event.method,
        "entorno": os.getenv("ENTORNO", "desconocido"),
    }
    return {
        "statusCode": 200,
        "body": json.dumps(respuesta, ensure_ascii=False),
        "headers": {"Content-Type": "application/json"},
    }

Revisa stack.yaml y añade una variable de entorno y un límite de memoria:

nano stack.yaml
version: 1.0
provider:
  name: openfaas
  gateway: https://faas.your_domain
functions:
  saludo:
    lang: python3-http
    handler: ./saludo
    image: your_registry_user/saludo:latest
    environment:
      ENTORNO: produccion
    limits:
      memory: 128Mi

Paso 5: Construir y desplegar la función

faas-cli up construye la imagen, la sube al registro y la despliega en el gateway en un solo paso:

faas-cli up -f stack.yaml

Si tu equipo usa una arquitectura distinta a la del servidor (por ejemplo, un Mac con Apple Silicon y un VPS x86_64), construye la imagen para la plataforma del servidor con Buildx y despliega después:

faas-cli publish -f stack.yaml --platforms linux/amd64
faas-cli deploy -f stack.yaml

Cuando termine, faas-cli muestra la URL de la función. Pruébala:

curl -s https://faas.your_domain/function/saludo \
  -H "Content-Type: application/json" \
  -d '{"nombre": "CubePath"}'
{"mensaje": "Hola, CubePath", "metodo": "POST", "entorno": "produccion"}

Consulta sus logs, que en faasd se leen del journal del servidor:

faas-cli logs saludo

El comando sigue mostrando líneas nuevas durante un tiempo; interrúmpelo con Ctrl+C.

Si la imagen está en un registro privado, el servidor necesita credenciales para descargarla. faasd las lee de /var/lib/faasd/.docker/config.json; la documentación de faasd explica cómo generarlo con docker login en otra máquina y copiarlo.

Paso 6: Usar secretos

Las credenciales (claves de API, contraseñas de bases de datos) no deben ir en environment, porque quedan visibles en stack.yaml y en la descripción de la función. Crea un secreto en el gateway:

faas-cli secret create api-key --from-literal "your_api_key"

Referéncialo en stack.yaml, dentro de la definición de saludo:

    secrets:
      - api-key

OpenFaaS monta cada secreto como archivo en /var/openfaas/secrets/<nombre>. Léelo desde el código:

with open("/var/openfaas/secrets/api-key") as f:
    api_key = f.read().strip()

Vuelve a desplegar con faas-cli up -f stack.yaml para aplicar el cambio.

Paso 7: Invocar funciones de forma asíncrona

Para trabajos que tardan (procesar un archivo, llamar a una API lenta), invoca la función por la ruta /async-function/. El gateway guarda la petición en la cola de NATS, responde de inmediato con 202 Accepted y el queue-worker la ejecuta en segundo plano:

curl -i https://faas.your_domain/async-function/saludo \
  -H "Content-Type: application/json" \
  -d '{"nombre": "cola"}'
HTTP/2 202
x-call-id: 6d2f0c8e-...
...

Si añades la cabecera X-Callback-Url: https://your_app/webhook, el queue-worker enviará el resultado a esa URL cuando termine. Puedes ver la ejecución en faas-cli logs saludo.

Limitaciones de faasd

faasd está pensado para un solo servidor y tiene diferencias importantes con OpenFaaS sobre Kubernetes:

  • Cada función se ejecuta con una réplica; no hay autoescalado ni escalado a cero.
  • No hay alta disponibilidad: si el servidor se reinicia, las funciones vuelven a arrancar con él, pero hay una interrupción.
  • La actualización de faasd se hace volviendo a ejecutar el script de instalación de una versión nueva.

Si necesitas autoescalado o varios nodos, OpenFaaS se instala en Kubernetes con su chart de Helm, y las funciones y stack.yaml que has creado sirven sin cambios.

Solución de problemas

faas-cli login falla con connection refused. faasd todavía está arrancando o ha fallado. Revisa sudo journalctl -u faasd -n 50 y sudo journalctl -u faasd-provider -n 50; los errores de descarga de imágenes suelen deberse a DNS o a falta de espacio en disco.

La función se despliega pero faas-cli list muestra 0 réplicas. El servidor no ha podido descargar la imagen. Comprueba en sudo journalctl -u faasd-provider si el error es de autenticación (registro privado sin credenciales) o de arquitectura (exec format error, imagen construida para ARM en un servidor x86_64 o al revés).

Caddy no obtiene el certificado. El registro A aún no apunta al servidor o los puertos 80 y 443 están cerrados. Comprueba dig +short faas.your_domain, sudo ufw status y los logs con sudo journalctl -u caddy -n 50.

faas-cli up falla en el paso de push. No has iniciado sesión en el registro o el prefijo de la imagen no coincide con tu usuario. Ejecuta docker login y revisa el campo image de stack.yaml.

Conclusión

Tienes OpenFaaS funcionando con faasd en un VPS, publicado con HTTPS, y una función en Python desplegada desde tu equipo con secretos e invocación asíncrona. Como siguientes pasos, automatiza faas-cli publish y faas-cli deploy en tu pipeline de CI, añade funciones en otros lenguajes con faas-cli template store list y, si tu carga crece, migra las mismas funciones a OpenFaaS sobre Kubernetes para obtener autoescalado.