Get Started with Kubernetes
Deploy a persistent three-node SlimFaas cluster and example workloads, then explore them through the SlimFaas dashboard on your computer.
Before you start
You need Git, kubectl, a running Kubernetes cluster, and permission to create resources in a demonstration namespace. A local cluster from kind, minikube or Docker Desktop works; the manifests require a default StorageClass capable of provisioning ReadWriteOnce volumes. SlimFaas requests a 2 GiB data volume and a 1 GiB backup volume per replica; the MySQL dependency also needs storage.
kubectl config current-context
kubectl get nodes
kubectl get storageclass
git clone https://github.com/SlimPlanet/SlimFaas.git
cd SlimFaas
All commands below run from the repository root. Check the selected context before applying the demo.
Deploy the demo
kubectl apply -f demo/service-account-slimfaas.yml
kubectl apply -f demo/deployment-slimfaas.yml
kubectl apply -f demo/deployment-mysql.yml
kubectl apply -f demo/deployment-functions.yml
kubectl apply -f demo/deployment-cron.yaml
kubectl -n slimfaas-demo rollout status statefulset/slimfaas --timeout=300s
kubectl -n slimfaas-demo get pods,pvc
Note — Kubernetes watch: SlimFaas keeps its view of the cluster up to date through Kubernetes watch streams on pods, deployments, statefulsets, jobs and cronjobs (the
watchverb granted bydemo/service-account-slimfaas.ymlis required). Synchronization is event-driven: full LIST calls only run when something actually changed, with a periodic safety-net resync. The design — and how it stays Native AOT compatible without the client'sWatcher<T>— is described in How SlimFaas Works. The behavior is configurable underSlimFaas:KubernetesWatch:
Key Default Description EnabledtrueSet to falseto restore the legacy fixed-cadence pollingFunctionsResyncSeconds30Safety-net resync for deployments/pods/statefulsets JobsResyncSeconds30Safety-net resync for jobs JobsConfigurationResyncSeconds60Safety-net resync for CronJob configurations DebounceMilliseconds300Event burst coalescing window WatchTimeoutSeconds60Watch stream rotation (server-side close) WatchReadDeadlineMarginSeconds30Client-side margin over WatchTimeoutSecondsafter which a stalled connection is abandonedIf a watch stream cannot be established (for example the ServiceAccount lacks the
watchverb, or the API server is temporarily unreachable), SlimFaas logs a warning once and automatically falls back to the legacy polling cadence for the affected resources until the stream is restored, so an outdated RBAC never slows synchronization down.Services are not watched (the RBAC does not grant it): a change that only touches a Service — such as re-pointing the selector of the Service in front of the SlimFaas StatefulSet or of a function — is only picked up by the periodic resync, so it can take up to
FunctionsResyncSeconds(30 s by default) to propagate instead of a few hundred milliseconds. Lower that value if Service objects change frequently in your cluster.
The first manifest creates the namespace, ServiceAccount and RBAC. The SlimFaas manifest creates its configuration, StatefulSet, discovery Service and application Service. Functions carry annotations for visibility, inactivity, concurrency, dependencies and scaling. fibonacci2 depends on fibonacci1 and MySQL; MySQL is included to demonstrate orchestration dependencies. The sample API itself does not query it.
The fibonacci job is configured by SlimFaas. The additional fibonacci5 CronJob is suspended in Kubernetes and discovered by SlimFaas through its annotations. Functions can scale to zero before you finish these steps, so their absence from the pod list alone is not a startup failure.
Keep podManagementPolicy: OrderedReady: one healthy Raft member starts before the next. Image downloads and volume provisioning can make the initial startup take several minutes. The demo exposes data APIs publicly; review this choice when adapting it to your own environment.
Open the dashboard
Start the port-forward in a terminal and leave it running:
kubectl -n slimfaas-demo port-forward service/slimfaas-http 30021:5000
Open http://127.0.0.1:30021/. In a second terminal:
export BASE_URL=http://127.0.0.1:30021
curl -i "$BASE_URL/ready"
curl -fsS "$BASE_URL/status-functions"
curl -fsS "$BASE_URL/function/fibonacci1/hello/kubernetes"
Expect 200 READY, a function list and a greeting. Keep the network map visible during the next steps. Port-forward selects a pod; restart it if that pod is replaced. It is a convenient local access method, not a production ingress.
Readiness and Raft discovery
Use /ready on port 5000 for the readiness probe and /health for the liveness
probe, as in the demo. /ready returns 503 while the node lacks a leader,
consensus, protocol compatibility or completed snapshot restoration. /health
can still return 200: the process must remain alive to participate in recovery.
The two Services have different purposes:
| Service | Configuration | Use |
|---|---|---|
slimfaas |
Headless, publishNotReadyAddresses: true |
Stable per-pod DNS for Raft, including unready members. |
slimfaas-http |
ClusterIP, publishNotReadyAddresses: false |
Application HTTP/WebSocket traffic; Ingress and Route backends. |
Keep the StatefulSet's serviceName and the Raft URL template tied to the
discovery Service. The demo pins SlimFaas__BaseSlimDataUrl explicitly so adding
an application Service cannot change member identities. Do not point application
traffic at the discovery Service: publishing unready addresses deliberately
bypasses readiness filtering there. Port-forward is also a direct diagnostic
connection and does not demonstrate normal Service routing.
When adapting an existing deployment, preserve its Raft DNS names and PVCs.
Prepare discovery independently of readiness before switching the readiness
probe. Changing an existing Service's clusterIP to None requires a planned
Service replacement; it is not an in-place patch. For a full restart of an
existing multi-member cluster, OrderedReady can wait for quorum before creating
the other members. Plan quorum restoration explicitly; the demo's initial
one-member bootstrap does not validate that recovery procedure.
Validate in a disposable cluster that Raft DNS resolves for unready pods while the application Service excludes them, then confirm recovery and application traffic after quorum returns. See Raft diagnostics.
Pod replacement and membership
Keep the requested StatefulSet replica count unchanged during a pod replacement. SlimFaas defers Raft member removal until its snapshot contains exactly the requested positive number of distinct, started endpoints with an IP, including the local endpoint. Missing pods and pods waiting for startup or networking do not count as a scale-down. Readiness is not required for membership additions. An intentional scale-down still removes stale members after the configured number of consecutive complete observations; see membership reconciliation.
Recovering a leaderless cluster
This is an operator-controlled procedure, not an automatic repair. A membership guard prevents premature exclusions during future replacements; it does not prove why an existing cluster stopped making progress. A timeout in a replication stack alone is not evidence of disk corruption or a command-protocol mismatch.
-
Pause further rollout actions and preserve the PVCs, Raft identities, image version and configuration. Capture private logs and per-node metrics before restarting anything; obtain recoverable volume snapshots through your storage platform. Keep these artifacts private: logs, metrics and configuration can contain application data and internal addresses. Do not attach them to a public issue. Never delete the WAL, edit membership files, or force a cold bootstrap to recover an existing cluster.
-
Check requested replicas, started pods, discovery and per-node
/healthand/ready. Reachability of/healthonly establishes HTTP liveness. Checkslimdata_raft_has_leader,slimdata_raft_has_consensus, membership counts, committed/applied indexes and their progression. Compare the persisted member identities using read-only diagnostics; equal counts alone do not establish that nodes share the same configuration. If no recoverable voting majority can be established, stop and investigate rather than guessing a new membership. -
When a voting majority with matching configuration is identified and one of those members has stopped making progress, restart only that stalled member, retaining its PVCs and DNS identity. Keep the other voters running. Do not restart all pods or scale to zero as a shortcut. The following command is an example for a disposable demo after selecting the member, not a prescribed ordinal for an existing deployment:
namespace=slimfaas-demo restart_pod=slimfaas-2 kubectl -n "$namespace" delete pod "$restart_pod" --wait=true -
Allow the replacement to start, then observe election and replication for up to two minutes. Verify a common leader and consensus on the voting majority, successful
/readyresponses, and committed/applied progress. Stop if these checks fail; do not enter a restart loop. Once quorum is stable, let an excluded member rejoin through normal reconciliation. If it also needs a restart, perform that separately and repeat the checks. Confirm all expected members and verify application data before resuming normal rollout actions. Passing/healthor an unchanged applied index on an idle cluster is not sufficient validation.
Use direct per-pod diagnostics so a Service cannot hide an unhealthy member. For example, in separate terminals:
kubectl -n slimfaas-demo port-forward pod/slimfaas-0 30021:5000
curl --fail http://127.0.0.1:30021/health
curl --fail http://127.0.0.1:30021/ready
curl --fail --silent http://127.0.0.1:30021/metrics |
grep -E '^slimdata_raft_(has_leader|has_consensus|member_count|last_log_index|committed_log_index|applied_log_index) '
Repeat for each pod, restarting port-forward after a pod replacement. Prepare the
discovery Service and application Service separation described above before
changing an existing readiness probe to /ready. Preserve existing Raft DNS
names; a Service replacement or a full restart under OrderedReady requires its
own maintenance procedure. Validate those changes in a disposable cluster first.
Discover the features
Follow the Guided Tour and select Kubernetes in Bruno. The default HTTP base URL is the port-forward above. The source IP seen by SlimFaas depends on the access method; the tour explains how that can affect the private-function example.
The core tour does not require Kafka. To explore lag-based wake-up later, follow the Kafka guide and its demonstration manifests.
Other access methods
For clusters exposing NodePort to the host:
kubectl apply -f demo/slimfaas-nodeport.yml
SlimFaas is exposed on node port 30021. With kind or a remote cluster, the node address and host port mappings may differ from localhost. This Service uses externalTrafficPolicy: Local to preserve source IP; send requests to a node with a serving SlimFaas pod.
For Ingress, adapt demo/slimfaas-ingress.yml to your ingress controller and hostname, then apply it. Configure TLS and streaming support, including unbuffered SSE. WebSocket clients use the separate 5003 service port by default; see Clients.
Adapt the installation
Use the demo as an executable starting point. For your own namespace, update every manifest's namespace, ServiceAccount bindings, service addresses and job environment URLs together. Keep the Raft port internal and preserve durable storage when restarting nodes.
SlimFaas verifies the API server certificate with the CA projected into its pod. If your API server presents a certificate that cannot be validated, SlimFaas__KubernetesSkipTlsVerify=true restores the unverified connection and logs a warning at startup; prefer fixing the certificate. If an ingress controller or reverse proxy must pass the original client address for Private functions, declare its own address in SlimFaas__TrustedProxies__0 (a fixed IP or a proxy-only subnet, never the pod CIDR: every address in that list may claim any client address); otherwise X-Forwarded-For is ignored. See Functions.
Add SlimFaas/Function: "true" and the desired scaling annotations to your workload's pod-template metadata. Ensure its Service, HTTP port and health probes match the application. See Functions, Autoscaling, Jobs and How It Works for complete configuration details.
Troubleshooting
| Symptom | What to check |
|---|---|
| PVC stays Pending | Default StorageClass, provisioner, available capacity and PVC events. |
| Pod stays Pending or fails | kubectl -n slimfaas-demo describe pod <pod> and kubectl -n slimfaas-demo get events --sort-by=.lastTimestamp. |
| No cluster readiness | kubectl -n slimfaas-demo logs slimfaas-0; inspect storage, service discovery and RBAC. |
| Connection refused locally | Leave the port-forward running and check port 30021 is free. |
| Function does not wake | Inspect its annotations, dependencies and readiness probes; check SlimFaas logs. |
| Data API returns 404 | Check Data__DefaultVisibility, the caller's visibility and the actual stored ID. |
Stop and remove
Stop port-forward with Ctrl+C. To remove only the demonstrated workloads:
kubectl delete -f demo/deployment-cron.yaml
kubectl delete -f demo/deployment-functions.yml
kubectl delete -f demo/deployment-mysql.yml
kubectl delete -f demo/deployment-slimfaas.yml
The last manifest also declares the demo namespace: deleting it removes the namespace and its remaining resources, including PVCs. Use this cleanup only for the dedicated slimfaas-demo environment. To preserve data while pausing SlimFaas, scale its StatefulSet to zero instead and keep the namespace and volumes.