Bare Metal Kubernetes Homelab Setup

A self-hosted bare-metal Kubernetes platform on Ubuntu 24.04 LTS. Combines Ansible for automated node provisioning with ArgoCD for GitOps-based cluster management.

Published
Updated
Reading
3 min
Bare Metal Kubernetes Homelab Setup

ArgoCD, Tailscale, Envoy, Longhorn, Vault, and more!

A self-hosted bare-metal Kubernetes platform on Ubuntu 24.04 LTS. Combines Ansible for automated node provisioning with ArgoCD for GitOps-based cluster management. Traffic flows through Tailscale for secure ingress and Envoy Gateway for routing, with Vault and External Secrets handling credentials. Longhorn provides distributed storage. A local rehearsal workflow using Multipass VMs allows validating changes before deploying to hardware.

GitHub Repository: https://github.com/nsudhanva/homelab

Table of contents

Features

  • Multi-node bare-metal Kubernetes with kubeadm and Cilium
  • GitOps-managed infrastructure and apps via ArgoCD ApplicationSets
  • Tailscale Gateway API ingress with split-horizon DNS
  • Vault + External Secrets for centralized secret management
  • Longhorn storage, Prometheus monitoring, and optional GPU plugins
  • Automated image updates with ArgoCD Image Updater
  • Metrics Server for resource usage in Headlamp
  • ExternalDNS automation for Gateway API routes
  • Envoy Gateway data plane for Gateway API

Architecture

Core systems map

Image not yet migrated: 01_core_systems_map.png.

Platform services map

Image not yet migrated: 02_platform_services_map.png.

Network topology

Image not yet migrated: 03_network_topology.png.

Quick start

Bare metal is the primary target. The local VM flow exists to rehearse changes before touching hardware.

Bare metal bring-up

Provision nodes, bootstrap Kubernetes, then hand off to ArgoCD.

nano ansible/inventory/hosts.yaml
ansible-playbook -i ansible/inventory/hosts.yaml ansible/playbooks/provision-cpu.yaml

sudo kubeadm init --pod-network-cidr=10.244.0.0/16
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl taint nodes --all node-role.kubernetes.io/control-plane-

CILIUM_VERSION=$(grep -E "cilium_version:" ansible/group_vars/all.yaml | head -n 1 | awk -F'"' '{print $2}')
cilium install --version "$CILIUM_VERSION" --values infrastructure/cilium/values.cilium

kubectl apply -k bootstrap/argocd
kubectl wait --for=condition=available --timeout=600s deployment/argocd-server -n argocd
kubectl apply -f bootstrap/root.yaml

Guided flow: https://docs.sudhanva.me/docs/how-to/from-scratch and https://docs.sudhanva.me/docs/tutorials

From scratch flow

Image not yet migrated: 04_from_scratch_flow.png.

Local rehearsal

Use Multipass to validate the full flow on your workstation.

./scripts/local-cluster.sh up

Low-resource rehearsal:

WORKER_COUNT=0 VM_CPUS=2 VM_MEMORY=3G VM_DISK=12G CILIUM_HUBBLE_ENABLED=false ./scripts/local-cluster.sh up

Tear down:

./scripts/local-cluster.sh down

Documentation

Docs site: https://docs.sudhanva.me

Build locally:

cd docs
npm ci
npm start

Recommended reading paths:

Repository layout

ansible/          Node provisioning
bootstrap/        ArgoCD bootstrap and ApplicationSets
infrastructure/   Cluster components managed by ArgoCD
apps/             User workloads managed by ArgoCD
clusters/         Cluster-specific overrides
scripts/          Automation helpers
docs/             Docusaurus documentation

Applications

AppPurposeHostname
DocsDocusaurus site for cluster documentationdocs.sudhanva.me
HeadlampKubernetes UI with OIDC support and metricsheadlamp.sudhanva.me
HomerHome dashboard with service shortcutshome.sudhanva.me
JellyfinMedia streaming with GPU acceleration when presentjellyfin.sudhanva.me
FilebrowserFile manager for the media volumefilebrowser.sudhanva.me
ArgoCDGitOps control plane UIargocd.sudhanva.me
LonghornStorage UIlonghorn.sudhanva.me
VaultSecrets UIvault.sudhanva.me
Hubble UICilium network visibilityhubble.sudhanva.me
GrafanaMetrics dashboardsgrafana.sudhanva.me
PrometheusMetrics queriesprometheus.sudhanva.me
AlertmanagerAlerting UIalertmanager.sudhanva.me

GitOps model

ArgoCD reconciles everything under infrastructure/ and apps/ using ApplicationSets. Manual kubectl apply is discouraged after bootstrap.

Adding apps:

  • Create apps/<app>/app.yaml to define the ArgoCD app name/path/namespace
  • Add Kubernetes manifests in the same folder
  • Add kustomization.yaml if you want Image Updater to write overrides

Image updates:

  • ArgoCD Image Updater writes .argocd-source-<app>.yaml files into app folders
  • These files are not Kubernetes resources and are ignored by kubeconform

Operations

Routine checks:

kubectl get nodes
kubectl get pods -A
kubectl get apps -n argocd

Before pushing:

pre-commit run --all-files

Conventions

  • ApplicationSets generate infra-* and app-* ArgoCD applications from folders
  • App folders use app.yaml for app metadata and manifests in the same directory
  • kustomization.yaml enables Image Updater overrides per app
  • Image updates write .argocd-source-<app>.yaml files into app folders

Monitoring topology

Image not yet migrated: 05_monitoring_topology.png.

Maintainers

All posts →