Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

home-prod

Home automation Kubernetes cluster on Proxmox with GPU passthrough, NFS storage, and automatic DNS.

What's Included

  • 3-node Kubernetes cluster on Proxmox VMs (1 control plane, 2 workers) provisioned via cloud-init
  • GPU passthrough with NVIDIA container runtime support (optional)
  • NFS persistent storage on the Proxmox host, provisioned dynamically via CSI driver
  • Automatic DNS + HTTPS via external-dns, Cloudflare, and Let's Encrypt
  • Home Assistant with NFS-backed config and Ingress routing
  • Ollama LLM inference server with GPU acceleration and persistent model storage
  • OpenClaw AI agent with chat support backed by local Ollama models

Architecture

Proxmox Host
├── NFS server (/mnt/nfs/k3s)
├── VM: control-1 ── control plane + Traefik ingress
├── VM: data-1    ── worker + GPU (optional)
└── VM: data-2    ── worker

Kubernetes Cluster
├── NFS CSI driver       ── dynamic persistent volumes
├── external-dns         ── auto-syncs Ingress hostnames to Cloudflare DNS
├── NVIDIA device plugin ── exposes GPU to pods (optional)
├── Home Assistant       ── ha.<domain>, config persisted on NFS
├── Ollama               ── ollama.<domain>, models persisted on NFS
└── OpenClaw             ── openclaw.<domain>, AI agent gateway using Ollama

VM hostnames are registered automatically via DHCP DNS, so the cluster uses hostnames instead of hardcoded IPs. external-dns keeps Cloudflare DNS records in sync with the cluster's current IPs.

Project Structure

.
├── cluster.sh                      # Cluster lifecycle management (runs on Proxmox host)
├── config.env.example              # Cluster config template
├── cloud-init/
│   ├── k3s-control-plane.yaml      # Control plane provisioning
│   ├── k3s-data-plane.yaml         # Worker node provisioning
│   └── k3s-data-plane-gpu.yaml     # GPU worker node provisioning
├── helm/
│   ├── Chart.yaml                  # Helm chart with dependencies
│   ├── values.yaml.example         # Config template (copy to values.yaml)
│   └── templates/
│       ├── cloudflare-secret.yaml
│       ├── home-assistant.yaml
│       ├── nfs-storage-class.yaml
│       ├── nvidia-device-plugin.yaml
│       ├── ollama.yaml
│       ├── openclaw.yaml
│       └── traefik-config.yaml

Prerequisites

  • A Kubernetes cluster with kubectl access — if you don't have one, see Host Setup below
  • An NFS server accessible from your cluster nodes
  • Cloudflare account with a domain
  • A router that registers VM hostnames via DHCP (e.g., UniFi)
  • Helm 3

Deploy

1. Configure

cp helm/values.yaml.example helm/values.yaml

Edit helm/values.yaml:

Key Description Example
domain Base domain for your apps home.example.com
nfs.server NFS server hostname or IP pve
cloudflare.apiToken Cloudflare API token with Edit Zone DNS permission
external-dns.domainFilters Your Cloudflare zone [example.com]
homeAssistant.timezone Your timezone America/Los_Angeles
gpu.enabled Enable NVIDIA GPU support true / false
acme.email Email for Let's Encrypt certificate expiry notices you@example.com
ollama.models Models to auto-download on startup [qwen3.5:27b]
openclaw.gatewayToken OpenClaw gateway auth token Any random string
openclaw.discordToken Discord bot token
openclaw.discordAllowedUsers Discord user IDs allowed to DM the bot ["123456789"]

2. Install

cd helm
helm dependency update
helm install home-prod .

Services will be available at https://<app>.<your-domain> once DNS propagates (may take a few minutes).

3. Upgrade

After changing values or templates:

cd helm
helm upgrade home-prod .

Ollama

Ollama runs on the GPU node and auto-downloads models listed in ollama.models on startup. Models are persisted on NFS so they survive restarts. To pull additional models:

kubectl exec deploy/ollama -- ollama pull <model-name>

OpenClaw

OpenClaw is an AI agent gateway that connects to your local Ollama instance. Access the Control UI at https://openclaw.<your-domain> using the gateway token from your config.

To connect a Discord bot, create one at discord.com/developers with the bot and applications.commands scopes, enable the Message Content Intent, and add the token to your config. Add your Discord user ID to discordAllowedUsers to skip the pairing step.

HTTPS

TLS certificates are automatically provisioned via Let's Encrypt using Cloudflare DNS-01 validation. This works for internal services — no ports need to be exposed to the internet. Traefik (the built-in K3s ingress controller) handles certificate issuance, renewal, and TLS termination.

GPU Support

Disabled by default. To enable, set gpu.enabled: true in your values.yaml. The NVIDIA device plugin only runs on nodes labeled nvidia.com/gpu=true (set automatically by the cloud-init script on GPU nodes).

Verify:

kubectl describe node <gpu-node> | grep nvidia.com/gpu
# Should show nvidia.com/gpu: 1 under Allocatable

Host Setup

This section covers setting up a K3s cluster on Proxmox from scratch. K3s is a lightweight Kubernetes distribution that's well-suited for home use — it bundles Traefik ingress, CoreDNS, and a local storage provider out of the box with minimal resource overhead. Skip this if you already have a Kubernetes cluster.

NFS Storage

Create a dedicated volume for persistent storage that survives VM rebuilds.

1. Create and mount a volume

lvcreate -V 256G -T pve/data -n nfs    # adjust size as needed
mkfs.ext4 /dev/pve/nfs
mkdir -p /mnt/nfs
mount /dev/pve/nfs /mnt/nfs

Add to /etc/fstab so it persists across reboots:

/dev/pve/nfs /mnt/nfs ext4 defaults 0 2

2. Install and configure NFS server

apt install -y nfs-kernel-server
mkdir -p /mnt/nfs/k3s
chown nobody:nogroup /mnt/nfs/k3s

Add to /etc/exports (adjust the subnet to match your network):

/mnt/nfs/k3s <your-subnet>(rw,sync,no_subtree_check,no_root_squash)

Apply:

exportfs -ra
systemctl restart nfs-kernel-server

GPU Passthrough (Optional)

Skip this section if you don't have a GPU.

1. Enable IOMMU

Edit /etc/default/grub and add IOMMU flags to GRUB_CMDLINE_LINUX_DEFAULT:

  • AMD: quiet amd_iommu=on iommu=pt
  • Intel: quiet intel_iommu=on iommu=pt

Then run update-grub.

2. Load VFIO modules

Create /etc/modules-load.d/vfio.conf:

vfio
vfio_iommu_type1
vfio_pci

3. Blacklist GPU drivers on host

Create /etc/modprobe.d/blacklist.conf:

blacklist nouveau
blacklist nvidia

4. Bind GPU to VFIO

Find your GPU's vendor:device IDs with lspci -nn, then create /etc/modprobe.d/vfio.conf:

options vfio-pci ids=<gpu-id>,<audio-id>

5. Apply and reboot

update-initramfs -u -k all
reboot

Verify after reboot: lspci -nnk -s <pci-slot> | grep -i driver should show vfio-pci.

6. Create GPU resource mapping

In the Proxmox UI: Datacenter > Resource Mappings > Add PCI Device. Use the mapping name as GPU_MAPPING in config.env.

SSH Keys

Prepare SSH keys so you can access VMs from both the Proxmox host and your workstation.

# On the Proxmox host, combine keys into a single file
cat ~/.ssh/id_rsa.pub > /tmp/keys.pub

Append your workstation's public key to the same file:

echo "<your-workstation-public-key>" >> /tmp/keys.pub

Use this path as SSH_KEYS in config.env.

Enable Snippets

In the Proxmox UI: Datacenter > Storage > local > Edit > Content > add Snippets. This allows cloud-init files to be stored on the host.

Deploy the Cluster

cp config.env.example config.env

Edit config.env:

Key Description Example
K3S_TOKEN Shared secret for K3s nodes to join Any random string
GPU_MAPPING Proxmox GPU resource mapping name rtx3090
NFS_SERVER NFS server hostname pve
CONTROL_PLANE_HOST Control plane VM hostname control-1
SSH_KEYS Path to SSH public keys file /tmp/keys.pub
# Create VM template and start cluster
./cluster.sh template-create
./cluster.sh start

# Verify (wait a few minutes for cloud-init to finish)
./cluster.sh status

Note: The GPU worker node reboots automatically after cloud-init finishes to load the NVIDIA kernel module. This is expected — it rejoins the cluster after ~1 minute.

Set Up kubectl

From your workstation:

ssh ubuntu@<control-plane-host> "sudo cat /etc/rancher/k3s/k3s.yaml" \
  | sed "s/127.0.0.1/<control-plane-host>/" > ~/.kube/config

Cluster Management

./cluster.sh start              # Create and start all VMs
./cluster.sh clean              # Stop and destroy all VMs
./cluster.sh restart            # Rebuild entire cluster
./cluster.sh restart 201        # Rebuild a single node
./cluster.sh status             # Check node status
./cluster.sh template-create    # Create VM template
./cluster.sh template-clean     # Destroy VM template

About

Home automation Kubernetes cluster

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages