This repo contains all the configuration as well as documentation for my homelab.
The purpose of my homelab is to learn and have fun.
Self hosting using Kubernetes requires me to think about security, scalability, maintenance and deployment strategies.
I aim to apply all changes via GitOps rather than running manual commands on the cluster.
To this end I'm using FluxCD.
End User Applications
| Logo | Name | Description |
|---|---|---|
| philipkrueck.com | This is my personal website. I occasionally write there. | |
| Homepage | Application dashboard serving as a GUI entrypoint into my homelab. | |
| littlelink-server | Self hosted alternative to linktree. |
Everything needed to run my cluster & deploy my applications
| Logo | Name | Description |
|---|---|---|
| Flux CD | My GitOps solution of choice. | |
| Traefik | My Ingress Controller of choice. | |
| cert-manager | Certificate Management. I'm using Let's Encrypt as a CA. | |
| SOPS | Encryption of Kubernetes Secrets. So that I can store them in this repo. | |
| Grafana | Neat visualization of application and cluster state. | |
| Prometheus | Monitoring and Alerting. Datafeed for Grafana. | |
| Renovate | Automated dependency updates. | |
| Cloudflare Tunnels | I don't really need tunnels since my VM has a public IP. But they are amazing if I setup some hardware in my private network. |
I want my homelab to run on various types of Kubernetes setups.
I'm currently running my cluster on a simple single VM k3s, but planning to switch to the multi-node setup soon. I've documented both setup options below.
Follow the docs for installation.
Then make sure the traefik-config.yaml doesn't
exist in the static manifests directory created by K3s:
rm /var/lib/rancher/k3s/server/manifests/traefik.yamlThe Traefik Deployment will be managed as code instead: infrastructure/controllers/base/traefik.
This setup requires 2 virtual machines - one master and one worker node provisioned using kubeadm.
Both the master and worker node have the same specs:
- RAM = 4GiB
- disk = 120GiB SSD
- CPU = 2 vCPU
- OS = Ubuntu 20.04
Both machines require open SSH access (TCP:22).
The master node also requires an open TCP:6443 firewall rule for the Kube API Server.
Services are exposed on ports 30000-40000 so these ports will be opened as needed.
- SSH into Master Node.
- Login as root:
sudo -i. - Execute the setup script:
bash <(curl -s https://raw.githubusercontent.com/philipkrueck/homelab/refs/heads/main/setup/install-master.sh)- Take note of the join command in the output. It will be needed in the next steps and should look similar to this:
kubeadm join <masterIP>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>- SSH into Worker Node.
- Login as root:
sudo -i. - Execute the setup script:
bash <(curl -s https://raw.githubusercontent.com/philipkrueck/homelab/refs/heads/main/setup/install-worker.sh)- Execute the join command that was displayed at the end of the output of the setup script on the master.
- Go back to the master node.
- Login again:
sudo -i. - Check the nodes using the
k=kubectlalias:
❯ k get nodes
NAME STATUS ROLES AGE VERSION
homelab-master Ready control-plane,master 6d21h v1.32.3
homelab-worker Ready worker 6d21h v1.32.3- Now the kube config can copied to the local machine to interact with the cluster without SSH access.
- Obtain a new Personal Access Token
- Go to GitHub Settings > Developer Settings > Personal Access Tokens
- Generate a new classic token with 'repo' permissions
- Store the token in an environment variable:
export GITHUB_TOKEN=<your-token>or in nushell
$env.GITHUB_TOKEN = 'ghp_XXXX'- Bootstrap flux on cluster
flux bootstrap github --owner=philipkrueck --repository=homelab --branch=main --path=./clusters/staging --personal --token-auth- Provide flux with my age secret key
cat ~/.age/age.key | k create secret generic sops-age --namespace=flux-system --from-file=age.agekey=/dev/stdinFlux has access to my age secret key. In order to safely commit secrets to this repo, I use the following command on the raw secret files:
sops --age=age1vf5v73hyx36z3y398l2n7pxyhznptpl00kkxnuup4vrtnsjpg5tqcperyn --encrypt --encrypted-regex '^(data|stringData)$' --in-place super-secret.yaml- Deploy Atuin CLI History DB
- Deploy some more cool open source applications
- Add a back up solution
- Ability to restore all data & state from blob storage