Back to Articles

Kubeless: The Kubernetes-Native Serverless Framework That Proved Functions Could Be CRDs

[ View on GitHub ]

Kubeless: The Kubernetes-Native Serverless Framework That Proved Functions Could Be CRDs

Hook

Before Kubeless, deploying a serverless function on Kubernetes meant installing a complex stack of external components. Kubeless proved you could define a function with the same kubectl command you use to deploy a pod.

Context

In 2016, AWS Lambda had popularized serverless computing, but Kubernetes users faced a dilemma: adopt cloud-vendor FaaS platforms and accept lock-in, or cobble together their own solutions with API gateways, orchestration layers, and custom deployment pipelines. Early Kubernetes serverless attempts like Fission and OpenWhisk required substantial infrastructure beyond Kubernetes itself—separate routing layers, message queues, and control planes that felt foreign to the Kubernetes operational model.

Kubeless emerged from VMware's Bitnami team with a radical proposition: what if functions were just another Kubernetes resource type? By leveraging Custom Resource Definitions (CRDs), introduced in Kubernetes 1.7, Kubeless made functions first-class citizens in the Kubernetes API. You could create, update, and delete functions using kubectl, manage them with RBAC policies, and monitor them with the same Prometheus exporters you used for everything else. No external databases, no separate control planes—just Kubernetes doing what it does best: managing containerized workloads.

Technical Insight

Kubernetes Cluster

Creates

Watches

Injects code into

Creates

Provisions

Optional

Mounts as volume

Routes through

Forwards to

External access

User/kubectl

Function CRD

Kubeless Controller

ConfigMap

Function Code

Deployment

Runtime Container

Service

Ingress

HTTP Request

System architecture — auto-generated

Kubeless's architecture demonstrates elegant simplicity. At its core is a custom controller running as a Deployment in your cluster, watching for Function custom resources. When you define a function, you're creating a Kubernetes object that looks like this:

apiVersion: kubeless.io/v1beta1
kind: Function
metadata:
  name: hello-world
  namespace: default
spec:
  runtime: python3.7
  handler: handler.greet
  function: |
    def greet(event, context):
      return event['data']
  deployment:
    spec:
      replicas: 1

The controller intercepts this resource creation and orchestrates a series of native Kubernetes operations. First, it injects your function code into a ConfigMap. Then it creates a Deployment using a pre-built runtime container image—say, kubeless/python:3.7—mounting that ConfigMap as a volume. The runtime container runs a simple HTTP server that loads your function code and invokes it on incoming requests. Finally, the controller provisions a Service to expose the function and optionally an Ingress for external access.

What makes this architecture powerful is its composability with existing Kubernetes primitives. Want autoscaling? Apply a HorizontalPodAutoscaler to your function's Deployment. Need traffic splitting for canary deployments? Use standard Kubernetes Service meshes like Istio. Require secrets management? Mount Secrets as environment variables exactly as you would for any Pod. The function becomes indistinguishable from any other workload in your cluster.

The runtime containers themselves follow a plugin-like pattern. Each language runtime is a separate Docker image implementing a simple contract: expose an HTTP endpoint on port 8080, read the function code from a mounted volume at a known path, and invoke the handler function when requests arrive. This abstraction meant adding new language support required only building a new container image, not modifying the core controller. The Python runtime, for instance, uses the Bottle framework to handle HTTP routing, dynamically imports the user's module, and calls the specified handler function with normalized event and context objects.

Kubeless also pioneered the integration between serverless frameworks and Kubernetes. The project provided an official Serverless Framework plugin that translated serverless.yml configurations into Function CRDs:

service: my-service

provider:
  name: kubeless
  runtime: nodejs12

functions:
  greet:
    handler: handler.greet
    events:
      - http:
          path: /greet

This bridged the gap between cloud-agnostic serverless development workflows and Kubernetes-native deployments, letting developers write portable function definitions while operations teams retained full control over the underlying infrastructure.

The trigger mechanism showcases another architectural decision: rather than building a custom event bus, Kubeless created separate CRDs for different trigger types (HTTPTrigger, CronTrigger, KafkaTrigger). An HTTPTrigger would automatically create an Ingress rule pointing to your function's Service. A CronTrigger would create a CronJob that periodically invokes the function via HTTP. This pattern delegated event handling to Kubernetes's native scheduling and routing systems rather than reinventing them, keeping the Kubeless codebase focused and maintainable.

Gotcha

The elephant in the room: Kubeless is archived and unmaintained. VMware stopped active development, and the repository now serves as a historical artifact rather than a production-ready platform. This means no security patches, no compatibility updates for new Kubernetes versions, and no community support beyond what's frozen in GitHub issues. If you're evaluating Kubeless for a production deployment in 2024, stop—this is a non-starter.

Beyond the maintenance status, Kubeless had architectural limitations even in its heyday. Cold start performance remained a persistent issue because every function invocation on a scaled-to-zero deployment required Kubernetes to schedule a Pod, pull the runtime image (unless cached), mount the ConfigMap, and start the runtime server. This could take 10-30 seconds, making Kubeless unsuitable for latency-sensitive applications. Projects like Fission addressed this with pre-warmed pool architectures, but Kubeless never fully solved the cold start problem. The runtime injection mechanism also meant you couldn't easily use compiled languages or custom dependencies without building specialized runtime images, reducing the "just write code" simplicity that makes cloud FaaS platforms appealing.

Verdict

Skip if: you're building anything for production use. The archived status makes Kubeless a liability, not an asset. Security vulnerabilities will go unpatched, compatibility with newer Kubernetes versions is uncertain, and you'll be alone when things break. Even for greenfield internal projects, the ecosystem has moved on to actively maintained alternatives with better performance characteristics and richer feature sets.

Use if: you're architecting a custom Kubernetes-native platform and want to study a clean reference implementation of CRD-based resource management. Kubeless's codebase remains an excellent educational resource for understanding how to build Kubernetes controllers, structure runtime plugin systems, and integrate with the Kubernetes API. If you're a platform engineer designing internal developer platforms or evaluating whether to build versus buy a FaaS solution, reading Kubeless's source code will teach you more about Kubernetes extension patterns than a dozen tutorials. Just don't deploy it—learn from it, then choose Knative, OpenFaaS, or a managed cloud solution for actual workloads.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/vmware-archive-kubeless.svg)](https://starlog.is/api/badge-click/developer-tools/vmware-archive-kubeless)