Skip to main content
Updated Feb 23, 2026

Personal Cloud Lab - Hetzner + K3s

DigitalOcean DOKS is production-grade, but at ~$24+/month minimum, it's expensive for learning and experimentation. Sometimes you want a personal Kubernetes cluster where you can break things, try experimental configurations, and learn without worrying about costs.

Hetzner Cloud offers the most cost-effective path: real cloud servers at ~$5/month for a functional K3s cluster. K3s is a lightweight Kubernetes distribution that runs on smaller instances while remaining fully Kubernetes-compatible. Your kubectl commands, Helm charts, and Dapr configurations work identically.


Why Hetzner + K3s for a Personal Lab?

Before diving into setup, understand why this combination works for budget-conscious learning.

The Cost Comparison

ProviderMinimum SetupMonthly CostWhat You Get
DigitalOcean DOKS2 nodes~$24Managed control plane, automatic updates
Hetzner + K3s1 master + 2 workers~$5-10Self-managed, full control, real servers
AWS EKS2 nodes + control plane~$75+Enterprise features, managed
Docker Desktop K8sLocal only$0Not accessible externally

Hetzner's CPX11 instances (2 vCPU, 2GB RAM) cost approximately $5.59/month (EUR 4.99). A minimal K3s cluster (1 master + 2 workers) runs around $17/month, or a single-node setup for learning costs under $6/month.

What is K3s?

K3s is Kubernetes, stripped down to essentials:

Standard Kubernetes (K8s)          K3s (Lightweight)
┌──────────────────────┐ ┌──────────────────────┐
│ kube-apiserver │ │ │
│ kube-controller │ │ Single binary │
│ kube-scheduler │ → │ (k3s server) │
│ etcd (cluster) │ │ + sqlite/etcd │
│ kubelet │ │ + containerd │
│ Multiple binaries │ │ │
└──────────────────────┘ └──────────────────────┘
~1GB+ RAM overhead ~512MB RAM overhead

K3s achieves this by:

  • Single binary: All components compiled into one executable
  • SQLite by default: No separate etcd cluster needed (optional etcd for HA)
  • Containerd only: No Docker dependency
  • Reduced features: Removes legacy/alpha features rarely used
  • ARM support: Runs on Raspberry Pi and ARM servers

The key insight: K3s is a certified Kubernetes distribution. Your kubectl commands, Helm charts, and YAML manifests work exactly the same. You're learning real Kubernetes, not a simulation.


Prerequisites

Before starting, verify you have:

RequirementHow to Check
SSH key pairls ~/.ssh/id_rsa.pub (must exist)
Go installed (for CLI)go version (or use binary download)
kubectl installedkubectl version --client
Terminal accessYou're reading this, so you have one

Generate SSH Key (If Missing)

If you don't have an SSH key:

ssh-keygen -t rsa -b 4096 -C "your-email@example.com"

Output:

Generating public/private rsa key pair.
Enter file in which to save the key (/Users/you/.ssh/id_rsa): [press Enter]
Enter passphrase (empty for no passphrase): [optional]
Your public key has been saved in /Users/you/.ssh/id_rsa.pub.

Accept defaults. This creates ~/.ssh/id_rsa (private) and ~/.ssh/id_rsa.pub (public).


Step 1: Create Hetzner Cloud Account

Hetzner is a German cloud provider known for competitive pricing and reliable infrastructure.

1.1 Sign Up

  1. Visit console.hetzner.cloud
  2. Click "Register" and create an account
  3. Verify your email address
  4. Complete identity verification (Hetzner requires this for new accounts)

Note: Hetzner's verification process may take 24-48 hours. They manually review accounts to prevent abuse. Plan ahead if you need the cluster immediately.

1.2 Create a Project

After verification:

  1. Log into Hetzner Cloud Console
  2. Click "New Project"
  3. Name it something descriptive (e.g., "k3s-lab")
  4. Click "Create Project"

1.3 Generate API Token

The hetzner-k3s CLI needs an API token to provision resources:

  1. Select your project in Hetzner Console
  2. Click "Security" in the left sidebar
  3. Click "API Tokens" tab
  4. Click "Generate API Token"
  5. Name: hetzner-k3s-cli
  6. Permissions: Read & Write
  7. Click "Generate API Token"
  8. Copy the token immediately (it won't be shown again)

Security warning: This token has full access to create and destroy resources. Store it securely.

1.4 Export Token as Environment Variable

export HCLOUD_TOKEN="your-api-token-here"

Add to your shell profile (~/.zshrc or ~/.bashrc) for persistence:

echo 'export HCLOUD_TOKEN="your-api-token-here"' >> ~/.zshrc
source ~/.zshrc

Step 2: Install hetzner-k3s CLI

The hetzner-k3s CLI automates K3s cluster provisioning on Hetzner Cloud. It handles server creation, K3s installation, and kubeconfig generation.

If you have Go installed:

go install github.com/vitobotta/hetzner-k3s@latest

Output:

go: downloading github.com/vitobotta/hetzner-k3s v2.x.x

Verify installation:

hetzner-k3s version

Output:

hetzner-k3s version 2.0.x

Option B: Download Binary

If you don't have Go, download the binary directly:

macOS (Apple Silicon):

curl -L https://github.com/vitobotta/hetzner-k3s/releases/latest/download/hetzner-k3s-darwin-arm64 -o hetzner-k3s
chmod +x hetzner-k3s
sudo mv hetzner-k3s /usr/local/bin/

macOS (Intel):

curl -L https://github.com/vitobotta/hetzner-k3s/releases/latest/download/hetzner-k3s-darwin-amd64 -o hetzner-k3s
chmod +x hetzner-k3s
sudo mv hetzner-k3s /usr/local/bin/

Linux (AMD64):

curl -L https://github.com/vitobotta/hetzner-k3s/releases/latest/download/hetzner-k3s-linux-amd64 -o hetzner-k3s
chmod +x hetzner-k3s
sudo mv hetzner-k3s /usr/local/bin/

Verify:

hetzner-k3s version

Step 3: Write cluster.yaml Configuration

The hetzner-k3s CLI uses a YAML configuration file to define your cluster. Create a file named cluster.yaml:

---
cluster_name: task-api-lab
kubeconfig_path: ./kubeconfig
k3s_version: v1.31.4+k3s1

networking:
ssh:
port: 22
use_agent: false
public_key_path: ~/.ssh/id_rsa.pub
private_key_path: ~/.ssh/id_rsa

masters_pool:
instance_type: cpx11
instance_count: 1
location: fsn1

worker_pools:
- name: default
instance_type: cpx11
instance_count: 2
location: fsn1

Understanding the Configuration

FieldValuePurpose
cluster_nametask-api-labName for your cluster and server prefix
kubeconfig_path./kubeconfigWhere to save cluster credentials
k3s_versionv1.31.4+k3s1K3s version to install
masters_pool.instance_typecpx11Hetzner instance type (2 vCPU, 2GB RAM)
masters_pool.instance_count1Number of control plane nodes
masters_pool.locationfsn1Hetzner datacenter (Falkenstein, Germany)
worker_pools[0].instance_count2Number of worker nodes

Hetzner Instance Types

TypevCPURAMPrice/Month
cpx1122 GB~$5.59
cpx2134 GB~$8.50
cpx3148 GB~$14.50
cpx41816 GB~$27.00

For a learning lab, cpx11 is sufficient. Your Task API and supporting services fit comfortably in 2GB per node.

Hetzner Locations

CodeLocationRegion
fsn1Falkenstein, GermanyEU
nbg1Nuremberg, GermanyEU
hel1Helsinki, FinlandEU
ashAshburn, VirginiaUS East
hilHillsboro, OregonUS West

Choose the location closest to you for lower latency.


Step 4: Provision the Cluster

With configuration ready, provision your cluster:

hetzner-k3s create --config cluster.yaml

Output:

Creating master server...
Master server created: task-api-lab-master-1 (10.0.0.2)
Installing K3s on master...
K3s installed successfully
Creating worker servers...
Worker server created: task-api-lab-worker-1 (10.0.0.3)
Worker server created: task-api-lab-worker-2 (10.0.0.4)
Installing K3s on workers...
All workers joined the cluster
Kubeconfig saved to ./kubeconfig

This process takes 3-5 minutes. The CLI:

  1. Creates Hetzner Cloud servers for master and workers
  2. Installs K3s on the master node
  3. Joins worker nodes to the cluster
  4. Downloads kubeconfig to your local machine

Step 5: Connect to Your Cluster

Set the KUBECONFIG environment variable to use your new cluster:

export KUBECONFIG=./kubeconfig

Verify connectivity:

kubectl get nodes

Output:

NAME                     STATUS   ROLES                  AGE   VERSION
task-api-lab-master-1 Ready control-plane,master 2m v1.31.4+k3s1
task-api-lab-worker-1 Ready <none> 1m v1.31.4+k3s1
task-api-lab-worker-2 Ready <none> 1m v1.31.4+k3s1

All nodes show Ready status. Your K3s cluster is operational.

Verify Cluster Health

Check that system pods are running:

kubectl get pods -n kube-system

Output:

NAME                                      READY   STATUS    RESTARTS   AGE
coredns-7b98449c4-x7vqd 1/1 Running 0 3m
local-path-provisioner-6c79684f77-8vhqj 1/1 Running 0 3m
metrics-server-5f9f776df5-k8xvz 1/1 Running 0 3m
traefik-6b84f7cbc-9gflw 1/1 Running 0 3m

K3s includes:

  • CoreDNS: Cluster DNS
  • Traefik: Ingress controller (built-in)
  • Local Path Provisioner: Default storage class
  • Metrics Server: Resource metrics for kubectl top

Step 6: Understanding Hetzner Cloud Controller Manager

When you create LoadBalancer services on Hetzner, the cluster needs the Hetzner Cloud Controller Manager (CCM) to provision Hetzner Load Balancers automatically.

The hetzner-k3s CLI can install CCM automatically. Add to your cluster.yaml:

# ... existing configuration ...

additional_packages:
- hetzner-ccm

If you already created your cluster, install CCM manually:

# Create secret with Hetzner API token
kubectl -n kube-system create secret generic hcloud \
--from-literal=token=$HCLOUD_TOKEN \
--from-literal=network=default

# Deploy CCM
kubectl apply -f https://github.com/hetznercloud/hcloud-cloud-controller-manager/releases/latest/download/ccm.yaml

Output:

secret/hcloud created
serviceaccount/cloud-controller-manager created
deployment.apps/hcloud-cloud-controller-manager created

With CCM installed, creating a type: LoadBalancer service automatically provisions a Hetzner Load Balancer (~$6/month additional).


Step 7: Deploy a Test Application

Verify your cluster works by deploying a simple application:

kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort

Output:

deployment.apps/nginx created
service/nginx exposed

Check the deployment:

kubectl get pods,svc

Output:

NAME                        READY   STATUS    RESTARTS   AGE
pod/nginx-76d6c9b8c-7xvqd 1/1 Running 0 30s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 5m
service/nginx NodePort 10.43.45.123 <none> 80:31234/TCP 15s

Access nginx through any node's public IP:

# Get node external IPs
kubectl get nodes -o wide

# Access nginx (replace with your node IP and NodePort)
curl http://<node-external-ip>:31234

Output:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

Clean up the test:

kubectl delete deployment nginx
kubectl delete service nginx

Cost Breakdown: What You're Paying

With the configuration in this lesson:

ResourceCountMonthly Cost
Master (cpx11)1~$5.59
Workers (cpx11)2~$11.18
Total-~$16.77

For a minimal single-node learning cluster:

masters_pool:
instance_type: cpx11
instance_count: 1
location: fsn1

worker_pools: [] # No workers, master runs workloads

This costs ~$5.59/month. K3s can run workloads on the control plane node.

Comparison with DOKS

SetupHetzner K3sDigitalOcean DOKS
Single-node learning~$6/monthN/A (2 nodes minimum)
1 master + 2 workers~$17/month~$24/month
Managed control planeNoYes
Auto-updatesNoYes
Support SLACommunityBusiness

When to choose Hetzner K3s:

  • Learning and experimentation
  • Budget-conscious projects
  • Full control needed
  • Non-production workloads

When to choose DOKS:

  • Production workloads
  • Team environments
  • Need managed upgrades
  • SLA requirements

Cleanup: Destroying Your Cluster

When you're done experimenting, destroy the cluster to stop charges:

hetzner-k3s delete --config cluster.yaml

Output:

Deleting worker servers...
Deleted: task-api-lab-worker-1
Deleted: task-api-lab-worker-2
Deleting master servers...
Deleted: task-api-lab-master-1
Cluster task-api-lab deleted

Important: This permanently destroys all servers and data. Export any important configurations first.


Try With AI

Now that you have a personal cloud lab, explore K3s and Hetzner further with your AI companion.

Prompt 1: Understand K3s Architecture

I just provisioned a K3s cluster on Hetzner Cloud. K3s runs as a single binary
instead of multiple components like standard Kubernetes. Help me understand:

1. What components are compiled into the k3s binary?
2. How does K3s handle storage without etcd in single-server mode?
3. What are the tradeoffs of using K3s vs full Kubernetes for production?

I want to understand when K3s is appropriate and when I should choose managed
Kubernetes like DOKS or EKS.

What you're learning: Understanding K3s architecture helps you make informed decisions about when lightweight Kubernetes is appropriate versus when you need full enterprise features.

Prompt 2: Compare Cloud Provider Costs

I'm running a personal K3s lab on Hetzner at ~$15/month. Compare the costs if
I wanted to run similar workloads on:

1. DigitalOcean (DOKS or droplets + K3s)
2. AWS (EKS or EC2 + K3s)
3. Google Cloud (GKE or Compute Engine + K3s)

Include hidden costs like egress bandwidth, load balancers, and persistent
storage. I want to understand the true cost of cloud Kubernetes.

What you're learning: Cloud pricing is complex. Understanding all cost components helps you budget accurately and choose providers based on your actual workload patterns.

Prompt 3: Plan Production Migration

I've been learning on my Hetzner K3s cluster. Now I want to migrate my Task API
to production. Help me plan the migration:

1. What would I need to change in my Helm charts for managed Kubernetes?
2. How do I handle the transition from Traefik (K3s default) to other ingress controllers?
3. What configurations are K3s-specific vs portable to any Kubernetes?

I want to ensure my learning transfers to production without major rewrites.

What you're learning: Skill portability is the goal. Understanding what's K3s-specific versus universal Kubernetes ensures your learning applies to any cluster.

Safety Note

Hetzner servers have public IPs by default. Your cluster is internet-accessible. Before deploying sensitive workloads, implement network policies, firewall rules, and authentication. Never expose sensitive services without TLS and proper access controls.


Reflect on Your Skill

You built a multi-cloud-deployer skill in Lesson 0. Test and improve it based on what you learned about Hetzner and K3s.

Test Your Skill

Using my multi-cloud-deployer skill, generate the hetzner-k3s configuration
for a minimal learning cluster (1 node, cpx11, fsn1 location).

Does your skill produce valid cluster.yaml? Does it explain K3s versus full Kubernetes tradeoffs?

Identify Gaps

Ask yourself:

  • Does my skill include Hetzner-specific patterns (API token, instance types, locations)?
  • Can it generate both single-node and multi-node configurations?
  • Does it explain K3s architecture and when to choose it?

Improve Your Skill

If you found gaps:

My multi-cloud-deployer skill is missing Hetzner K3s patterns. Update it to include:
- Hetzner Cloud API token configuration
- hetzner-k3s CLI installation and usage
- cluster.yaml templates for single-node and multi-node setups
- K3s vs full Kubernetes comparison for decision-making