Console Login

Serverless Architectures on Bare Metal: Escaping the Public Cloud Tax in 2021

Serverless Architectures on Bare Metal: Escaping the Public Cloud Tax

Let's get one thing straight before we look at a single line of YAML: "Serverless" is a marketing term. There are always servers. The only variable is who controls the root shell and how much premium you pay for the abstraction.

I've spent the last six months migrating a legacy monolith for a FinTech client in Oslo. We started with the standard AWS Lambda approach. It worked, until the bill arrived. Between the NAT Gateway charges, cold start latencies, and the API Gateway request costs, we were paying three times what we used to pay for a standard EC2 cluster. And that’s before we even talk about Schrems II and the headache of ensuring customer data doesn't accidentally route through a US-controlled switch.

For European DevOps teams in 2021, the "Public Cloud Serverless" dream is often a trap. The solution isn't to abandon the architectural pattern—event-driven functions are brilliant—but to re-platform them onto infrastructure you actually control. Here is how we build scalable serverless patterns on high-performance VPS nodes without selling our soul to a hyperscaler.

The Architecture: The "Private FaaS" Pattern

The most robust pattern I'm seeing this year involves running an open-source FaaS framework on top of Kubernetes (K8s) or Docker Swarm, hosted on local, low-latency infrastructure. This gives you the developer experience (DX) of "git push deploy" without the vendor lock-in.

We use OpenFaaS. It's mature, Kubernetes-native, and doesn't require a PhD in IAM policies to set up.

The Stack

  • Compute: CoolVDS High-Performance KVM instances (4 vCPU / 8GB RAM minimum).
  • Orchestrator: K3s (Lightweight Kubernetes) - perfect for VPS environments.
  • FaaS Layer: OpenFaaS.
  • Ingress: Nginx or Traefik.
  • Storage: NVMe (Non-negotiable for etcd performance).
Pro Tip: Never run a Kubernetes cluster on spinning rust (HDD) or shared-storage solutions with low IOPS. K3s relies heavily on etcd (or SQLite in edge mode), and high disk latency will cause your API server to timeout. We use CoolVDS NVMe instances specifically because they expose raw I/O performance that keeps the control plane stable.

Implementation: Deploying the Control Plane

First, we need a lightweight K8s cluster. Assuming you have provisioned three CoolVDS instances (one master, two workers) running Ubuntu 20.04 LTS.

Step 1: Install K3s on the master node

curl -sfL https://get.k3s.io | sh -
# Get the token to join workers
sudo cat /var/lib/rancher/k3s/server/node-token

Step 2: Install OpenFaaS via Arkade

Alex Ellis's arkade tool is the fastest way to get this running in 2021. Don't waste time manually applying fifty kubectl manifests unless you have to.

# Install arkade
curl -sLS https://get.arkade.dev | sudo sh

# Install OpenFaaS and Nginx Ingress
arkade install openfaas
arkade install nginx-ingress

Once deployed, you expose the gateway. On a VPS environment, you'll likely want to bind the NodePort or use a LoadBalancer service if you are using MetalLB. For a simple setup, we port-forward to verify:

kubectl rollout status -n openfaas deploy/gateway
kubectl port-forward -n openfaas svc/gateway 8080:8080 &

Pattern 1: The "Strangler Fig" Migration

The most common use case we see in the Nordics is breaking apart legacy PHP or Java applications. You don't rewrite the whole thing. You strangle it.

You place an Nginx reverse proxy in front of your legacy CoolVDS instance. You route specific paths (e.g., /api/v2/image-resize) to your new OpenFaaS cluster, while the rest goes to the legacy monolith.

Here is an Nginx configuration snippet for this pattern. Note the upstream timeouts—serverless functions can have cold starts (though much faster on your own hardware than Lambda).

http {
    upstream legacy_backend {
        server 10.0.0.5:80;
    }

    upstream openfaas_gateway {
        server 10.0.0.6:31112; # NodePort for OpenFaaS
        keepalive 32;
    }

    server {
        listen 80;
        server_name api.norway-fintech.no;

        location /functions/ {
            proxy_pass http://openfaas_gateway/;
            proxy_set_header X-Real-IP $remote_addr;
            
            # Critical for long-running batch jobs
            proxy_read_timeout 300s;
            proxy_connect_timeout 300s;
        }

        location / {
            proxy_pass http://legacy_backend;
        }
    }
}

Pattern 2: Asynchronous Event Processing

Synchronous HTTP calls are fragile. If you are processing data—say, generating PDF invoices for a Norwegian e-commerce site—you should not make the user wait.

OpenFaaS has NATS built-in. This allows you to offload work instantly. You post to the queue, and the function picks it up when resources permit. This smooths out CPU spikes on your VPS.

The Function Code (Python 3):

import os
import json

def handle(req):
    """
    handle a request to the function
    Args:
        req (str): request body
    """
    data = json.loads(req)
    
    # Simulate heavy processing
    invoice_id = data.get("invoice_id")
    user_id = data.get("user_id")
    
    print(f"Processing invoice {invoice_id} for user {user_id}")
    
    # In a real scenario, we might write to MinIO or a DB here
    return {
        "status": "processing",
        "worker_node": os.getenv("HOSTNAME")
    }

You deploy this with a stack.yml that defines the autoscaling policies. This is where owning the infrastructure shines. On AWS, a misconfigured recursion loop can cost you $5,000 overnight. On CoolVDS, the worst case is your CPU hits 100% and the queue backs up. Your wallet remains safe.

provider:
  name: openfaas
  gateway: http://127.0.0.1:8080

functions:
  pdf-generator:
    lang: python3
    handler: ./pdf-generator
    image: registry.coolvds-client.no/pdf-generator:latest
    labels:
      com.openfaas.scale.min: 2
      com.openfaas.scale.max: 15
    environment:
      write_debug: true

The Latency Argument: Oslo vs. Frankfurt

Physics is stubborn. If your users are in Norway, and your serverless functions are hosted in eu-central-1 (Frankfurt) or eu-west-1 (Ireland), you are eating a 20-40ms round-trip penalty just on the speed of light and switching hops.

When we deploy this stack on CoolVDS instances in a Nordic datacenter, we see ping times to NIX (Norwegian Internet Exchange) drop to 1-3ms. For high-frequency trading bots or real-time gaming backends, that difference is the entire ball game.

Comparison: Public Cloud vs. CoolVDS + OpenFaaS

Feature Public Cloud FaaS CoolVDS + OpenFaaS
Data Sovereignty Subject to US CLOUD Act 100% Local (GDPR Compliant)
Cold Start Unpredictable (100ms - 2s) Tunable (Keep-warm is free)
Cost Model Per-invocation (Uncapped) Flat Monthly Rate
Execution Time Limit 15 minutes (hard limit) Unlimited

Security and Data Privacy (Schrems II)

Since the Schrems II ruling last year, transferring personal data to US-owned providers has become a legal minefield. By running your serverless framework on CoolVDS, you ensure the data physically never leaves the jurisdiction. You are not relying on "Standard Contractual Clauses" that might be invalidated next week. You are relying on physics and geography.

We harden our CoolVDS nodes using standard Linux practices—UFW firewalls, fail2ban, and SSH key-only access. Because you control the OS, you can install specific intrusion detection systems (IDS) that black-box managed services simply don't allow.

Final Thoughts

Serverless is an architectural pattern, not a billing model. Don't conflate the two. You can have the agility of event-driven code without the unpredictability of hyperscaler billing.

If you are building for the Nordic market, performance and privacy are your two biggest currencies. Don't spend them on latency to Frankfurt.

Ready to build your own FaaS platform? Spin up a high-performance NVMe instance on CoolVDS today and get full root access in under 55 seconds.