OpenFaaS Installation for Serverless Functions

OpenFaaS (Functions as a Service) is an open-source framework for deploying serverless functions on Linux using Docker or Kubernetes, handling auto-scaling, async invocation, and observability. This guide covers installing OpenFaaS on Docker Swarm and Kubernetes, creating function templates, deploying functions, configuring auto-scaling, and monitoring your serverless workloads.

Prerequisites

  • Ubuntu 20.04+ or CentOS/Rocky 8+ with at least 2GB RAM
  • Docker installed: curl -fsSL https://get.docker.com | bash
  • Either Docker Swarm (single/multi-node) or Kubernetes cluster
  • Access to a container registry (Docker Hub, or self-hosted)

Installing OpenFaaS on Docker Swarm

# Initialize Docker Swarm (if not already done)
docker swarm init --advertise-addr $(curl -s ifconfig.me)

# Verify swarm is active
docker info | grep Swarm
docker node ls

Deploy OpenFaaS using the official stack:

# Clone OpenFaaS deployment repo
git clone https://github.com/openfaas/faas /tmp/faas
cd /tmp/faas

# Create the docker-compose stack deployment
./deploy_stack.sh --no-auth
# For production with authentication:
# ./deploy_stack.sh

# Verify all services are running
docker service ls
# Should show: func_gateway, func_prometheus, func_alertmanager, func_nats, etc.

# Check gateway is responding
curl http://localhost:8080/healthz

Deploy with authentication (recommended for production):

# Generate credentials
export BASIC_AUTH_PASSWORD=$(head -c 16 /dev/urandom | shasum | cut -d " " -f1)
echo "Password: $BASIC_AUTH_PASSWORD"

# Create secrets
echo -n "admin" | docker secret create basic-auth-user -
echo -n "$BASIC_AUTH_PASSWORD" | docker secret create basic-auth-password -

# Deploy with auth
cd /tmp/faas
docker stack deploy func --compose-file docker-compose.yml

Access the OpenFaaS web UI:

# Gateway runs on port 8080
# Open http://YOUR_SERVER_IP:8080/ui/

# Login with faas-cli
echo -n "$BASIC_AUTH_PASSWORD" | faas-cli login \
  --username admin \
  --password-stdin \
  --gateway http://localhost:8080

Installing faas-cli

# Install faas-cli
curl -sSL https://cli.openfaas.com | sudo sh

# Verify
faas-cli version

# Login to gateway
export OPENFAAS_URL=http://localhost:8080
faas-cli login --username admin --password "$BASIC_AUTH_PASSWORD"

Creating and Deploying Functions

# List available function templates
faas-cli template store list

# Pull commonly used templates
faas-cli template store pull python3
faas-cli template store pull node18
faas-cli template store pull golang-middleware
faas-cli template store pull java11

# Create a new function from template
mkdir -p /opt/functions/my-functions
cd /opt/functions/my-functions

faas-cli new hello-world --lang python3

Edit the function handler:

cat > /opt/functions/my-functions/hello-world/handler.py << 'EOF'
import json

def handle(req):
    """
    OpenFaaS function handler.
    req: HTTP request body as string
    returns: response body
    """
    try:
        data = json.loads(req) if req else {}
        name = data.get("name", "World")
        return json.dumps({
            "message": f"Hello, {name}!",
            "status": "success"
        })
    except json.JSONDecodeError:
        return json.dumps({
            "message": f"Hello, {req or 'World'}!",
            "status": "success"
        })
EOF

Configure the function in hello-world.yml:

version: 1.0
provider:
  name: openfaas
  gateway: http://localhost:8080

functions:
  hello-world:
    lang: python3
    handler: ./hello-world
    image: yourdockeruser/hello-world:latest
    environment:
      write_debug: true
    limits:
      memory: 128m
      cpu: 0.5
    requests:
      memory: 64m
      cpu: 0.1
    labels:
      com.openfaas.scale.min: "1"
      com.openfaas.scale.max: "10"
      com.openfaas.scale.factor: "5"

Build, push, and deploy:

cd /opt/functions/my-functions

# Build the function container
faas-cli build -f hello-world.yml

# Push to registry (Docker Hub)
faas-cli push -f hello-world.yml

# Deploy to OpenFaaS
faas-cli deploy -f hello-world.yml

# Or do all three at once
faas-cli up -f hello-world.yml

# Test the function
echo '{"name": "OpenFaaS"}' | faas-cli invoke hello-world

# Or via curl
curl -X POST http://localhost:8080/function/hello-world \
  -H "Content-Type: application/json" \
  -d '{"name": "OpenFaaS"}'

Function Templates

Create a custom template for your organization's standards:

# Node.js function with dependencies
faas-cli new image-processor --lang node18
cd image-processor

# Add npm dependencies
cat > package.json << 'EOF'
{
  "name": "image-processor",
  "version": "1.0.0",
  "description": "OpenFaaS image processing function",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "sharp": "^0.33.0"
  }
}
EOF

cat > handler.js << 'EOF'
'use strict';

const sharp = require('sharp');

module.exports = async (event, context) => {
    try {
        if (!event.body) {
            return context
                .status(400)
                .fail('No image data provided');
        }

        // Process image
        const imageBuffer = Buffer.from(event.body, 'base64');
        const processed = await sharp(imageBuffer)
            .resize(800, 600, { fit: 'inside' })
            .webp({ quality: 80 })
            .toBuffer();

        return context
            .status(200)
            .headers({ 'Content-Type': 'image/webp' })
            .succeed(processed);

    } catch (err) {
        console.error(err);
        return context.status(500).fail(err.message);
    }
};
EOF

Installing OpenFaaS on Kubernetes

# Add OpenFaaS Helm chart repo
helm repo add openfaas https://openfaas.github.io/faas-netes/
helm repo update

# Create namespaces
kubectl create namespace openfaas
kubectl create namespace openfaas-fn

# Create basic auth credentials
export BASIC_AUTH_PASSWORD=$(head -c 16 /dev/urandom | shasum | cut -d " " -f1)
kubectl -n openfaas create secret generic basic-auth \
  --from-literal=basic-auth-user=admin \
  --from-literal=basic-auth-password="$BASIC_AUTH_PASSWORD"

# Install OpenFaaS
helm upgrade --install openfaas openfaas/openfaas \
  --namespace openfaas \
  --set functionNamespace=openfaas-fn \
  --set basic_auth=true \
  --set generateBasicAuth=false \
  --set serviceType=NodePort

# Check deployment
kubectl get pods -n openfaas

# Get gateway URL
kubectl get svc -n openfaas gateway-external -o wide
export OPENFAAS_URL=http://$(kubectl get svc -n openfaas gateway-external -o jsonpath='{.status.loadBalancer.ingress[0].ip}'):8080

# Login
echo -n "$BASIC_AUTH_PASSWORD" | faas-cli login \
  --username admin \
  --password-stdin

Deploy function on Kubernetes:

# Update function YAML for Kubernetes
cat > hello-world.yml << 'EOF'
version: 1.0
provider:
  name: openfaas
  gateway: http://YOUR_GATEWAY:8080

functions:
  hello-world:
    lang: python3
    handler: ./hello-world
    image: yourdockeruser/hello-world:latest
    namespace: openfaas-fn
    environment:
      write_debug: "true"
    annotations:
      topic: "hello-topic"
EOF

faas-cli deploy -f hello-world.yml

Auto-Scaling Configuration

OpenFaaS uses Prometheus alerts to trigger scaling:

# Configure scale-to-zero (functions scale to 0 when idle)
# Add labels to function deployment:
cat >> hello-world.yml << 'EOF'
    labels:
      com.openfaas.scale.min: "0"       # Scale to zero when idle
      com.openfaas.scale.max: "20"      # Max instances
      com.openfaas.scale.factor: "10"   # Scale by this % per trigger
      com.openfaas.scale.zero: "true"   # Enable scale to zero
EOF

faas-cli deploy -f hello-world.yml

Configure scale-to-zero inactivity timeout:

# In Docker Swarm, update the faas-idler service
docker service update \
  --env-add inactivity_duration=5m \
  func_faas-idler

# In Kubernetes, update the helm deployment
helm upgrade openfaas openfaas/openfaas \
  --namespace openfaas \
  --set faasIdler.inactivityDuration=5m \
  --reuse-values

Async Function Invocation

# Invoke a function asynchronously (non-blocking)
curl -X POST http://localhost:8080/async-function/hello-world \
  -H "Content-Type: application/json" \
  -H "X-Callback-Url: http://callback-service:8080/results" \
  -d '{"name": "async test"}'

# The function runs in the background
# OpenFaaS uses NATS for async queue

# Check NATS queue status
curl http://localhost:8080/system/info | python3 -m json.tool

Monitoring OpenFaaS

# Built-in Prometheus metrics at port 9090
curl http://localhost:9090/metrics | grep openfaas

# Function invocation count
curl 'http://localhost:9090/api/v1/query?query=gateway_function_invocation_total'

# Function duration histogram
curl 'http://localhost:9090/api/v1/query?query=gateway_functions_seconds_bucket'

# Active replicas per function
faas-cli describe hello-world

# List deployed functions with replica count
faas-cli list --verbose

# Scale a function manually
faas-cli scale hello-world --replicas=3

Access the built-in Grafana dashboard (if deployed):

# Deploy Grafana with OpenFaaS
docker service create \
  --name func_grafana \
  --network func_functions \
  --publish 3000:3000 \
  -e GF_AUTH_ANONYMOUS_ENABLED=true \
  grafana/grafana:latest

Troubleshooting

Function times out on first cold invocation:

# Increase timeout in function YAML
cat >> hello-world.yml << 'EOF'
    environment:
      exec_timeout: "30s"
      write_timeout: "30s"
      read_timeout: "30s"
EOF
faas-cli deploy -f hello-world.yml

Function container fails to start:

# Check function logs
faas-cli logs hello-world

# For Kubernetes
kubectl logs -n openfaas-fn deployment/hello-world -f

"Unable to resolve function" error:

# List deployed functions
faas-cli list

# Check gateway connectivity
faas-cli version --verbose
curl http://localhost:8080/healthz

Docker Swarm function replicas at 0:

docker service ls | grep hello-world
docker service ps func_hello-world
docker service logs func_hello-world

Conclusion

OpenFaaS provides a developer-friendly serverless platform that runs on your own infrastructure, avoiding cloud vendor lock-in. Deploy it on Docker Swarm for simplicity or Kubernetes for production scale. The faas-cli tool handles the build/push/deploy workflow efficiently, and built-in Prometheus metrics make it easy to monitor function invocations and response times. Enable scale-to-zero for infrequently used functions to conserve resources, and use async invocation with NATS for workloads that don't require an immediate response.