{"id":13821478,"url":"https://github.com/Diaoul/home-ops","last_synced_at":"2025-05-16T12:33:28.971Z","repository":{"id":36995848,"uuid":"329312379","full_name":"Diaoul/home-ops","owner":"Diaoul","description":"My GitOps-managed home Kubernetes cluster...  and more! :sailboat:","archived":false,"fork":false,"pushed_at":"2025-05-15T12:52:42.000Z","size":8696,"stargazers_count":71,"open_issues_count":9,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-15T13:49:55.847Z","etag":null,"topics":["flux","gitops","k8s-at-home","kubernetes"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Diaoul.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"Diaoul"}},"created_at":"2021-01-13T13:11:23.000Z","updated_at":"2025-05-13T08:05:45.000Z","dependencies_parsed_at":"2024-02-06T23:31:51.463Z","dependency_job_id":"619d7f0a-4111-4a0e-8163-62dfe88d5454","html_url":"https://github.com/Diaoul/home-ops","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Diaoul%2Fhome-ops","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Diaoul%2Fhome-ops/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Diaoul%2Fhome-ops/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Diaoul%2Fhome-ops/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Diaoul","download_url":"https://codeload.github.com/Diaoul/home-ops/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254530698,"owners_count":22086666,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flux","gitops","k8s-at-home","kubernetes"],"created_at":"2024-08-04T08:01:22.595Z","updated_at":"2025-05-16T12:33:26.966Z","avatar_url":"https://github.com/Diaoul.png","language":"Python","funding_links":["https://github.com/sponsors/Diaoul"],"categories":["Just","kubernetes"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n   \u003cimg src=\"https://i.imgur.com/EXNTJnA.png\" alt=\"kubernetes home logo\" width=\"150\" align=\"left\" /\u003e\n\n### Operations for my home...\n_...managed by Flux, Renovate and GitHub Actions!_ ⛵\n\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![Talos](https://img.shields.io/endpoint?url=https%3A%2F%2Fkromgo.diaoul.io%2Fquery%3Fformat%3Dendpoint%26metric%3Dtalos_version\u0026style=for-the-badge\u0026logo=talos\u0026logoColor=white\u0026color=blue\u0026label=%20)](https://www.talos.dev/)\u0026nbsp;\u0026nbsp;\n[![Kubernetes](https://img.shields.io/endpoint?url=https%3A%2F%2Fkromgo.diaoul.io%2Fquery%3Fformat%3Dendpoint%26metric%3Dkubernetes_version\u0026style=for-the-badge\u0026logo=kubernetes\u0026logoColor=white\u0026color=blue\u0026label=%20)](https://www.talos.dev/)\u0026nbsp;\u0026nbsp;\n\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![Age-Days](https://img.shields.io/endpoint?url=https%3A%2F%2Fkromgo.diaoul.io%2Fquery%3Fformat%3Dendpoint%26metric%3Dcluster_age_days\u0026style=flat-square\u0026label=Age)](https://github.com/kashalls/kromgo/)\u0026nbsp;\u0026nbsp;\n[![Uptime-Days](https://img.shields.io/endpoint?url=https%3A%2F%2Fkromgo.diaoul.io%2Fquery%3Fformat%3Dendpoint%26metric%3Dcluster_uptime_days\u0026style=flat-square\u0026label=Uptime)](https://github.com/kashalls/kromgo/)\u0026nbsp;\u0026nbsp;\n[![Node-Count](https://img.shields.io/endpoint?url=https%3A%2F%2Fkromgo.diaoul.io%2Fquery%3Fformat%3Dendpoint%26metric%3Dcluster_node_count\u0026style=flat-square\u0026label=Nodes)](https://github.com/kashalls/kromgo/)\u0026nbsp;\u0026nbsp;\n[![Pod-Count](https://img.shields.io/endpoint?url=https%3A%2F%2Fkromgo.diaoul.io%2Fquery%3Fformat%3Dendpoint%26metric%3Dcluster_pod_count\u0026style=flat-square\u0026label=Pods)](https://github.com/kashalls/kromgo/)\u0026nbsp;\u0026nbsp;\n[![CPU-Usage](https://img.shields.io/endpoint?url=https%3A%2F%2Fkromgo.diaoul.io%2Fquery%3Fformat%3Dendpoint%26metric%3Dcluster_cpu_usage\u0026style=flat-square\u0026label=CPU)](https://github.com/kashalls/kromgo/)\u0026nbsp;\u0026nbsp;\n[![Memory-Usage](https://img.shields.io/endpoint?url=https%3A%2F%2Fkromgo.diaoul.io%2Fquery%3Fformat%3Dendpoint%26metric%3Dcluster_memory_usage\u0026style=flat-square\u0026label=Memory)](https://github.com/kashalls/kromgo/)\u0026nbsp;\u0026nbsp;\n\n\u003c/div\u003e\n\n## 📕 Overview\nThis mono repository contains everything I use to setup and run the devices in my home. It is based off the awesome [cluster-template](https://github.com/onedr0p/cluster-template).\n\nIt is fully managed following GitOps practices and using tools like [Ansible](https://www.ansible.com/), [Kubernetes](https://kubernetes.io/), [Flux](https://github.com/fluxcd/flux2), [Renovate](https://github.com/renovatebot/renovate), and [GitHub Actions](https://github.com/features/actions).\n\n## ⚙️  Hardware\n\n| Device                  | Count | Storage                  | Purpose                                      |\n|-------------------------|-------|--------------------------|----------------------------------------------|\n| Protectli FW4B clone    | 1     | 120GB                    | OPNsense router                              |\n| Synology NAS            | 1     | 12TB RAID 5 + 2TB RAID 1 | Main storage                                 |\n| Intel NUC8i5BEH         | 3     | 120GB SSD + 500GB NVMe   | Kubernetes control planes + storage          |\n| Intel NUC8i3BEH         | 2     | 120GB SSD                | Kubernetes workers                           |\n| Raspberry Pi 3          | 2     | 16GB SD                  | Unifi Controller / 3D Printer with OctoPrint |\n\n### Intel NUC\n\n#### BIOS\nIntel NUC bios can now be found on [Asus support](https://www.asus.com/supportonly/nuc8i5beh/helpdesk_bios/).\n\nConfiguration on top of Defaults (F9):\n\n1. Devices \u003e Onboard Devices \u003e Onboard Device Configuration\n  1. Uncheck `WLAN`\n  2. Uncheck `Bluetooth`\n2. Cooling \u003e CPU Fan Header\n   1. Uncheck `Fan off capability`\n3. Power \u003e Secondary Power Settings\n   1. Set `After Power Failure` to `Last State`\n4. Boot \u003e Boot Configuration \u003e Boot Display Config\n   1. Check `Display F12 for Network Boot`\n\nIn addition, to install Talos Linex with secure boot, we need to allow enrolling other keys.\nEnrolling new keys is done by booting the ISO and selecting the appropriate option.\n\n1. Boot \u003e Secure Boot \u003e Secure Boot Config\n   1. Check `Clear Secure Boot Data`\n\nThere is a [boot menu](https://www.intel.com/content/www/us/en/support/articles/000090607/intel-nuc.html) that can be helpful in case of boot failures:\n\n\u003e Press and hold down the power button for three seconds, then release it before the 4 second shutdown override. The Power Button Menu displays. (Options on the menu can vary, depending on the Intel NUC model.) Press F7 to start the BIOS update.\n\n#### Hardware\nThe fans on the Intel NUC are known to wear off. In case of overheating this is likely the issue. Amazon and Youtube are your best friends.\n\n\nThe CMOS battery can die and need replacing. Symptoms are the NUC not powering on at all.\n\n### Router\nIn addition to the regular things like a firewall, my router runs other useful\nstuff.\n\n#### HAProxy\nI have Talos configured with a Virtual IP to provide HA over the control nodes' API server but I also use HAProxy as loadbalancer.\n\nFirst, create a Virtual IP to listen on:\n\n1. Interfaces \u003e Virtual IPs \u003e Settings \u003e Add\n   1. `Mode` = `IP Alias`\n   2. `Interface` = `SERVER` (my VLAN for k8s nodes)\n   3. `Network / Address` = `10.0.3.2/32`\n   4. `Description` = `k8s-apiserver`\n\nThen, create the HAProxy configuration:\n\n1. Services \u003e HAProxy | Real Servers (for each **master node**)\n    1. `Enabled` = `true`\n    2. `Name or Prefix` = `k8s-node-x-apiserver`\n    3. `FQDN or IP` = `k8s-node-x`\n    4. `Port` = `6443`\n    5. `Verify SSL Certificate` = `false`\n2. Services \u003e HAProxy | Rules \u0026 Checks \u003e Health Monitors\n    1. `Name` = `k8s-apiserver`\n    2. `SSL preferences` = `Force SSL for health checks`\n    3. `Port to check` = `6443`\n    4. `HTTP method` = `GET`\n    5. `Request URI` = `/healthz`\n    6. `HTTP version` = `HTTP/1.1`\n3. Services \u003e HAProxy | Virtual Services \u003e Backend Pools\n    1. `Enabled` = `true`\n    2. `Name` = `k8s-apiserver`\n    3. `Mode` = `TCP (Layer 4)`\n    4. `Servers` = `k8s-node-x-apiserver` (Add one for each real server you created)\n    5. `Enable Health Checking` = `true`\n    6. `Health Monitor` = `k8s-apiserver`\n4. Services \u003e HAProxy | Virtual Services \u003e Public Services\n    1. `Enabled` = `true`\n    2. `Name` = `k8s-apiserver`\n    3. `Listen Addresses` = `10.0.3.2:6443` (the Virtual IP created above, alternatively, the router IP)\n    4. `Type` = `TCP`\n    5. `Default Backend Pool` = `k8s-apiserver`\n5. Services \u003e HAProxy | Settings \u003e Service\n    1. `Enable HAProxy` = `true`\n\nNote that Health Monitors require `anonymous-auth` to be enabled on Talos, otherwise we need to rely on TCP health checks instead.\n\n#### BGP\nCilium is configured with BGP to advertise load balancer IPs directly over BGP. Coupled with ECMP, this allows to spread workload in my cluster.\n\n1. Routing \u003e BPG | General\n    1. `enable` = `true`\n    2. `BGP AS Number` = `64512`\n    3. `Network` = `10.0.3.0/24` (Subnet of Kubernetes nodes)\n    4. Save\n2. Routing \u003e BGP | Neighbors\n    - Add a neighbor for each Kubernetes node\n      1. `Enabled` = `true`\n      2. `Peer-IP` = `10.0.3.x` (Kubernetes node IP)\n      3. `Remote AS` = `64512`\n      4. `Update-Source Interface` = `SERVER` (VLAN of Kubernetes nodes)\n      5. Save\n      6. Continue adding neighbors until all your nodes are present\n3. Routing \u003e General\n    1. `Enable` = `true`\n    2. Save\n4. System \u003e Settings \u003e Tunables\n    1. Add `net.route.multipath` and set the value to `1`\n    2. Save\n5. Reboot\n6. Verify\n    1. Routing \u003e Diagnostics \u003e BGP | Summary\n\n### SMTP Relay\nTo be able to send emails from my local devices easily without authentication,\nI run the Postfix plugin with the following configuration:\n\n1. System \u003e Services \u003e Postfix \u003e General\n    1. `Enable` = `true`\n    2. `Trusted Networks` += `10.0.0.0/8`\n    3. `TLS Wrapper Mode` = `true`\n    4. `SMTP Client Security` = `encrypt`\n    5. `Smart Host` = `[smtp.purelymail.com]:465`\n    6. `Enable SMTP Authentication` = `true`\n    7. `Authentication Username` = `admin@\u003cemail-domain\u003e`\n    8. `Authentication Password` = `\u003capp-password\u003e`\n    9. `Permit SASL Authenticated` = `false`\n    10. Save\n2. System \u003e Services \u003e Postfix \u003e Domains\n    - Add new domain\n      1. `Domainname` = `\u003cemail-domain\u003e`\n      2. `Destination` = `[smtp.purelymail.com]:465`\n      3. Save\n    - Apply\n3. System \u003e Services \u003e Postfix \u003e Senders\n    - Add new sender\n      1. `Enabled` = `true`\n      2. `Sender Address` = `admin@\u003cemail-domain\u003e`\n      3. Save\n    - Apply\n4. Verify\n    ```sh\n    swaks --server 10.0.3.1 --port 25 --to \u003cemail-address\u003e --from \u003cemail-address\u003e\n    ```\n\n## 🔍 Troubleshooting\n### cloudnative-pg\nIt can happen that a node is corrupt and fails to start, destroying it will trigger the creation of a new one.\nRun this command replacing {x} with the instance to destroy.\n```\nkubectl cnpg -n database destroy postgres16 {x}\n```\n\n### Browsing PVCs\nIt's sometimes useful to make some edits in a PVC or change permissions.\n```\ntask kubernetes:browse-pvc ns=media claim=jellystat\n```\n\n## 🤝 Thanks\nI learned a lot from the people that have shared their clusters over at [kubesearch](https://kubesearch.dev/) and from the [Home Operations](https://discord.gg/DNCynrJ) Discord Community.\n\nWant to get started? I highly recommend that you take a look at the [cluster-template](https://github.com/onedr0p/cluster-template) repository!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDiaoul%2Fhome-ops","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDiaoul%2Fhome-ops","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDiaoul%2Fhome-ops/lists"}