diff options
18 files changed, 635 insertions, 0 deletions
diff --git a/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.md b/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.md index 8cabdd69..5375f5ac 100644 --- a/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.md +++ b/gemfeed/2016-04-09-jails-and-zfs-on-freebsd-with-puppet.md @@ -397,6 +397,7 @@ E-Mail your comments to `paul@nospam.buetow.org` :-) Other *BSD related posts are: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.md b/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.md index b23a5a58..08106050 100644 --- a/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.md +++ b/gemfeed/2022-07-30-lets-encrypt-with-openbsd-and-rex.md @@ -676,6 +676,7 @@ E-Mail your comments to `paul@nospam.buetow.org` :-) Other *BSD related posts are: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.md b/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.md index 777eae1b..7c74fb30 100644 --- a/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.md +++ b/gemfeed/2024-01-13-one-reason-why-i-love-openbsd.md @@ -53,6 +53,7 @@ E-Mail your comments to `paul@nospam.buetow.org` :-) Other *BSD related posts are: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.md b/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.md index 62523fd1..2bde7c7f 100644 --- a/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.md +++ b/gemfeed/2024-04-01-KISS-high-availability-with-OpenBSD.md @@ -300,6 +300,7 @@ E-Mail your comments to `paul@nospam.buetow.org` :-) Other *BSD and KISS related posts are: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.md b/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.md index ac0485ad..91e52953 100644 --- a/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.md +++ b/gemfeed/2024-11-17-f3s-kubernetes-with-freebsd-part-1.md @@ -16,6 +16,7 @@ These are all the posts so far: [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [](./f3s-kubernetes-with-freebsd-part-1/f3slogo.png) @@ -164,6 +165,7 @@ Read the next post of this series: Other *BSD-related posts: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.md b/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.md index ab81238d..5f7e646f 100644 --- a/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.md +++ b/gemfeed/2024-12-03-f3s-kubernetes-with-freebsd-part-2.md @@ -16,6 +16,7 @@ These are all the posts so far: [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [](./f3s-kubernetes-with-freebsd-part-1/f3slogo.png) @@ -444,6 +445,7 @@ Read the next post of this series: Other *BSD-related posts: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.md b/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.md index ff2e1a30..bb4b2aae 100644 --- a/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.md +++ b/gemfeed/2025-02-01-f3s-kubernetes-with-freebsd-part-3.md @@ -12,6 +12,7 @@ This is the third blog post about my f3s series for my self-hosting demands in m [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [](./f3s-kubernetes-with-freebsd-part-1/f3slogo.png) @@ -366,6 +367,7 @@ Read the next post of this series: Other BSD related posts are: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.md b/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.md index 84d25df8..8372c042 100644 --- a/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.md +++ b/gemfeed/2025-04-05-f3s-kubernetes-with-freebsd-part-4.md @@ -12,6 +12,7 @@ This is the fourth blog post about the f3s series for self-hosting demands in a [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [](./f3s-kubernetes-with-freebsd-part-1/f3slogo.png) @@ -610,6 +611,7 @@ Read the next post of this series: Other *BSD-related posts: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.md b/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.md index 24544a30..65329eb1 100644 --- a/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.md +++ b/gemfeed/2025-05-11-f3s-kubernetes-with-freebsd-part-5.md @@ -18,6 +18,7 @@ These are all the posts so far: [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [](./f3s-kubernetes-with-freebsd-part-1/f3slogo.png) @@ -1442,6 +1443,7 @@ Read the next post of this series: Other *BSD-related posts: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.md b/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.md index 9c428755..809c9780 100644 --- a/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.md +++ b/gemfeed/2025-07-14-f3s-kubernetes-with-freebsd-part-6.md @@ -12,6 +12,7 @@ This is the sixth blog post about the f3s series for self-hosting demands in a h [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage (You are currently reading this)](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [](./f3s-kubernetes-with-freebsd-part-1/f3slogo.png) @@ -1932,6 +1933,7 @@ Read the next post of this series: Other *BSD-related posts: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage (You are currently reading this)](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2025-10-02-f3s-kubernetes-with-freebsd-part-7.md b/gemfeed/2025-10-02-f3s-kubernetes-with-freebsd-part-7.md index be6a823e..ac108aff 100644 --- a/gemfeed/2025-10-02-f3s-kubernetes-with-freebsd-part-7.md +++ b/gemfeed/2025-10-02-f3s-kubernetes-with-freebsd-part-7.md @@ -12,6 +12,7 @@ This is the seventh blog post about the f3s series for my self-hosting demands i [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments (You are currently reading this)](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [](./f3s-kubernetes-with-freebsd-part-1/f3slogo.png) @@ -1328,6 +1329,7 @@ I hope you enjoyed this walkthrough. Read the next post of this series: Other *BSD-related posts: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments (You are currently reading this)](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2025-12-07-f3s-kubernetes-with-freebsd-part-8.md b/gemfeed/2025-12-07-f3s-kubernetes-with-freebsd-part-8.md index 1313384d..9b8ed6b8 100644 --- a/gemfeed/2025-12-07-f3s-kubernetes-with-freebsd-part-8.md +++ b/gemfeed/2025-12-07-f3s-kubernetes-with-freebsd-part-8.md @@ -12,6 +12,7 @@ This is the 8th blog post about the f3s series for my self-hosting demands in a [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability (You are currently reading this)](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [](./f3s-kubernetes-with-freebsd-part-1/f3slogo.png) @@ -1656,6 +1657,7 @@ All configuration files are available on Codeberg: Other *BSD-related posts: +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) [2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability (You are currently reading this)](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) [2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) [2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) diff --git a/gemfeed/2026-04-02-f3s-kubernetes-with-freebsd-part-9.md b/gemfeed/2026-04-02-f3s-kubernetes-with-freebsd-part-9.md new file mode 100644 index 00000000..4a4f5ea5 --- /dev/null +++ b/gemfeed/2026-04-02-f3s-kubernetes-with-freebsd-part-9.md @@ -0,0 +1,613 @@ +# f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD + +> Published at 2026-04-02T00:00:00+03:00 + +This is the 9th post in the f3s series about my self-hosting home lab. f3s? The "f" stands for FreeBSD, and the "3s" stands for k3s, the Kubernetes distribution I use on FreeBSD-based physical machines. + +[2024-11-17 f3s: Kubernetes with FreeBSD - Part 1: Setting the stage](./2024-11-17-f3s-kubernetes-with-freebsd-part-1.md) +[2024-12-03 f3s: Kubernetes with FreeBSD - Part 2: Hardware and base installation](./2024-12-03-f3s-kubernetes-with-freebsd-part-2.md) +[2025-02-01 f3s: Kubernetes with FreeBSD - Part 3: Protecting from power cuts](./2025-02-01-f3s-kubernetes-with-freebsd-part-3.md) +[2025-04-05 f3s: Kubernetes with FreeBSD - Part 4: Rocky Linux Bhyve VMs](./2025-04-05-f3s-kubernetes-with-freebsd-part-4.md) +[2025-05-11 f3s: Kubernetes with FreeBSD - Part 5: WireGuard mesh network](./2025-05-11-f3s-kubernetes-with-freebsd-part-5.md) +[2025-07-14 f3s: Kubernetes with FreeBSD - Part 6: Storage](./2025-07-14-f3s-kubernetes-with-freebsd-part-6.md) +[2025-10-02 f3s: Kubernetes with FreeBSD - Part 7: k3s and first pod deployments](./2025-10-02-f3s-kubernetes-with-freebsd-part-7.md) +[2025-12-07 f3s: Kubernetes with FreeBSD - Part 8: Observability](./2025-12-07-f3s-kubernetes-with-freebsd-part-8.md) +[2026-04-02 f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD (You are currently reading this)](./2026-04-02-f3s-kubernetes-with-freebsd-part-9.md) + +[](./f3s-kubernetes-with-freebsd-part-1/f3slogo.png) + +[](./f3s-kubernetes-with-freebsd-part-9/argocd-app-tree.png) + +## Table of Contents + +* [⇢ f3s: Kubernetes with FreeBSD - Part 9: GitOps with ArgoCD](#f3s-kubernetes-with-freebsd---part-9-gitops-with-argocd) +* [⇢ ⇢ Introduction](#introduction) +* [⇢ ⇢ GitOps in a Nutshell](#gitops-in-a-nutshell) +* [⇢ ⇢ ArgoCD](#argocd) +* [⇢ ⇢ Why Bother for a Home Lab?](#why-bother-for-a-home-lab) +* [⇢ ⇢ Deploying ArgoCD](#deploying-argocd) +* [⇢ ⇢ ⇢ Accessing ArgoCD](#accessing-argocd) +* [⇢ ⇢ In-Cluster Git Server](#in-cluster-git-server) +* [⇢ ⇢ Repository Organization](#repository-organization) +* [⇢ ⇢ Migrating an App: Miniflux as Example](#migrating-an-app-miniflux-as-example) +* [⇢ ⇢ ⇢ Migration Order](#migration-order) +* [⇢ ⇢ Complex Migration: Prometheus Multi-Source](#complex-migration-prometheus-multi-source) +* [⇢ ⇢ ⇢ Sync Waves](#sync-waves) +* [⇢ ⇢ The Result](#the-result) +* [⇢ ⇢ What Changed Day-to-Day](#what-changed-day-to-day) +* [⇢ ⇢ Challenges Along the Way](#challenges-along-the-way) +* [⇢ ⇢ ⇢ Helm Release Adoption](#helm-release-adoption) +* [⇢ ⇢ ⇢ PersistentVolumes](#persistentvolumes) +* [⇢ ⇢ ⇢ Secrets](#secrets) +* [⇢ ⇢ ⇢ Grafana Not Reloading](#grafana-not-reloading) +* [⇢ ⇢ ⇢ Prometheus Multi-Source Ordering](#prometheus-multi-source-ordering) +* [⇢ ⇢ Wrapping Up](#wrapping-up) + +## Introduction + +In previous posts, I deployed applications to the k3s cluster using Helm charts and Justfiles--running `just install` or `just upgrade` to push changes to the cluster. That worked, but it had some drawbacks: + +* No single source of truth--cluster state depends on which commands were run and when +* Every change requires manually running commands +* No easy way to tell if the cluster drifted from the desired config +* Rolling back means re-running old Helm commands +* No audit trail for who changed what + +This post covers the migration to GitOps with ArgoCD. After this, the Git repo is the single source of truth, and ArgoCD keeps the cluster in sync automatically. + +## GitOps in a Nutshell + +The idea behind GitOps is simple: describe your entire desired state in Git, and let an agent in the cluster pull that state and reconcile it continuously. Every change goes through a commit, so you get version history, collaboration, and rollback for free. + +For Kubernetes specifically: + +* All manifests, Helm charts, and config live in a Git repo +* ArgoCD watches that repo +* Push a change, ArgoCD applies it +* If someone manually tweaks something in the cluster, ArgoCD detects the drift and reverts it + +## ArgoCD + +ArgoCD is a GitOps continuous delivery tool for Kubernetes. It runs as a controller in the cluster, continuously comparing live state against what's defined in Git. + +[ArgoCD Documentation](https://argo-cd.readthedocs.io) + +The features I care about most for f3s: + +* Automatic sync--monitors Git and applies changes to the cluster +* Application CRDs--each app is a Kubernetes custom resource +* Health checks--knows whether an app is healthy or degraded +* Web UI--visual overview of all applications and their sync status +* Sync waves and hooks--control deployment order and run post-deploy jobs +* Multi-source--combine upstream Helm charts with custom manifests + +## Why Bother for a Home Lab? + +Honestly, the biggest reason is disaster recovery. If the cluster dies, I can: + +* Bootstrap a fresh k3s cluster +* Install ArgoCD +* Point it at the Git repo +* Everything deploys automatically + +That's it. No "let me check my shell history to remember how I set this up." + +It's also a great way to learn. Setting up GitOps for real--even on a small cluster--teaches you things you won't pick up from tutorials alone. Debugging sync issues, figuring out sync waves, dealing with secrets management--all stuff that's directly applicable at work too. + +Beyond that: push to Git, things deploy. No SSH'ing to a workstation to run Helm commands. And if I manually tweak something while debugging and forget about it, ArgoCD reverts it back to the desired state. That's happened more than once. + +## Deploying ArgoCD + +ArgoCD manages everything else via GitOps, but ArgoCD itself needs a bootstrap. Chicken-and-egg problem. + +The installation lives in the config repo: + +[codeberg.org/snonux/conf/f3s/argocd](https://codeberg.org/snonux/conf/src/branch/master/f3s/argocd) + +I deployed it using Helm via a Justfile: + +```sh +$ cd conf/f3s/argocd +$ just install +helm repo add argo https://argoproj.github.io/argo-helm +helm repo update +kubectl create namespace cicd +kubectl apply -f persistent-volumes.yaml +helm install argocd argo/argo-cd --namespace cicd -f values.yaml +kubectl apply -f ingress.yaml +``` + +A few things worth noting in the `values.yaml`: + +Persistent storage for the repo-server so cloned Git repos survive pod restarts: + +```yaml +repoServer: + volumes: + - name: repo-server-data + persistentVolumeClaim: + claimName: argocd-repo-server-pvc + volumeMounts: + - name: repo-server-data + mountPath: /home/argocd/repo-cache + env: + - name: XDG_CACHE_HOME + value: /home/argocd/repo-cache +``` + +Server runs in insecure mode since TLS is terminated by the OpenBSD edge relays (same pattern as all other f3s services): + +```yaml +server: + insecure: true +configs: + params: + server.insecure: true +``` + +Dex (SSO) and notifications are disabled--overkill for a single-user home lab: + +```yaml +dex: + enabled: false +notifications: + enabled: false +``` + +The admin password is auto-generated on first install and stored in `argocd-initial-admin-secret`. It's preserved across Helm upgrades, so no manual secret creation needed: + +```sh +$ just get-password +# Reads from argocd-initial-admin-secret +``` + +### Accessing ArgoCD + +After deployment, ArgoCD runs in the `cicd` namespace: + +```sh +$ kubectl get pods -n cicd +NAME READY STATUS RESTARTS AGE +argocd-application-controller-0 1/1 Running 0 45d +argocd-applicationset-controller-66d6b9b8f4-vhm9k 1/1 Running 0 45d +argocd-redis-77b8d6c6d4-mz9hg 1/1 Running 0 45d +argocd-repo-server-5f98f77b97-8xtcq 1/1 Running 0 45d +argocd-server-6b9c4b4f8d-kxw7p 1/1 Running 0 45d +``` + +[](./f3s-kubernetes-with-freebsd-part-9/argocd-login.png) + +The ingress exposes both a WAN and LAN endpoint: + +```yaml +# WAN access (via OpenBSD relayd) +- host: argocd.f3s.foo.zone +# LAN access (via FreeBSD CARP VIP, with TLS) +- host: argocd.f3s.lan.foo.zone +``` + +## In-Cluster Git Server + +I didn't want ArgoCD pulling from Codeberg over the internet every time it checks for changes. If Codeberg is down (or my internet is), the cluster can't reconcile. So I set up a Git server inside the cluster itself. + +[codeberg.org/snonux/conf/f3s/git-server (at 190473b)](https://codeberg.org/snonux/conf/src/commit/190473b/f3s/git-server) + +The git-server runs as a single pod in the `cicd` namespace with two containers sharing a PVC: + +* An SSH git server (Alpine + OpenSSH + git-shell) for pushing changes from my laptop +* A CGit web UI with git-http-backend (nginx + fcgiwrap) for browsing repos and HTTP clones + +ArgoCD uses the HTTP backend to clone repos. Most Application manifests point at: + +``` +http://git-server.cicd.svc.cluster.local/conf.git +``` + +For pushing, I use SSH via a NodePort (30022). The git user is locked down to git-shell--no actual shell access. SSH keys are managed through a Kubernetes Secret. + +There's a chicken-and-egg situation here. The git-server's own ArgoCD Application manifest points at Codeberg (not at itself), since ArgoCD needs to bootstrap the git-server before it can use it: + +```yaml +# argocd-apps/cicd/git-server.yaml +source: + repoURL: https://codeberg.org/snonux/conf.git + targetRevision: master + path: f3s/git-server/helm-chart +``` + +Once the pod is up, all other apps use the in-cluster URL. The dependency chain is: Codeberg -> git-server -> everything else. + +The repo storage lives on NFS. Initial setup was just cloning the Codeberg repo as a bare repo into the NFS volume, then pointing my laptop's git remote at the NodePort: + +```sh +$ git remote add f3s f3s-git:/repos/conf.git +$ git push f3s master +``` + +ArgoCD detects the change within a few minutes and syncs. No internet required. The whole thing is intentionally minimal--no database, no accounts, no webhooks. Just git over SSH for writes and HTTP for reads. + +## Repository Organization + +I reorganized the config repo to support GitOps. Application manifests are grouped by Kubernetes namespace: + +``` +/home/paul/git/conf/f3s/ +├── argocd-apps/ +│ ├── cicd/ # CI/CD tooling (2 apps) +│ │ ├── argo-rollouts.yaml +│ │ └── git-server.yaml +│ ├── infra/ # Infrastructure (4 apps) +│ │ ├── cert-manager.yaml +│ │ ├── pkgrepo.yaml +│ │ ├── registry.yaml +│ │ └── traefik-config.yaml +│ ├── monitoring/ # Observability stack (6 apps) +│ │ ├── alloy.yaml +│ │ ├── grafana-ingress.yaml +│ │ ├── loki.yaml +│ │ ├── prometheus.yaml +│ │ ├── pushgateway.yaml +│ │ └── tempo.yaml +│ ├── services/ # User-facing applications (18 apps) +│ │ ├── anki-sync-server.yaml +│ │ ├── apache.yaml +│ │ ├── audiobookshelf.yaml +│ │ ├── filebrowser.yaml +│ │ ├── immich.yaml +│ │ ├── ipv6test.yaml +│ │ ├── jellyfin.yaml +│ │ ├── keybr.yaml +│ │ ├── kobo-sync-server.yaml +│ │ ├── miniflux.yaml +│ │ ├── navidrome.yaml +│ │ ├── opodsync.yaml +│ │ ├── pihole.yaml +│ │ ├── radicale.yaml +│ │ ├── syncthing.yaml +│ │ ├── tracing-demo.yaml +│ │ ├── wallabag.yaml +│ │ └── webdav.yaml +│ └── test/ # Test/example applications +├── miniflux/ # Per-app directories (unchanged) +│ ├── helm-chart/ +│ │ ├── Chart.yaml +│ │ ├── values.yaml +│ │ └── templates/ +│ └── Justfile +├── prometheus/ +│ ├── manifests/ # Additional manifests for multi-source +│ └── Justfile +└── ... +``` + +The per-app directories (miniflux, prometheus, etc.) stayed mostly the same--ArgoCD points at the same Helm charts. The main additions are the `argocd-apps/` directory structure and `manifests/` subdirectories for complex apps. + +## Migrating an App: Miniflux as Example + +I migrated all apps incrementally, one at a time. The procedure was the same for each. Here's miniflux as a concrete example. + +Before ArgoCD, the Justfile looked like this: + +```makefile +install: + kubectl apply -f helm-chart/persistent-volumes.yaml + helm install miniflux ./helm-chart --namespace services + +upgrade: + helm upgrade miniflux ./helm-chart --namespace services + +uninstall: + helm uninstall miniflux --namespace services +``` + +Workflow: edit chart, run `just upgrade`, hope you didn't forget anything. + +To migrate, I created an Application manifest telling ArgoCD where the Helm chart lives and how to sync it: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: miniflux + namespace: cicd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: http://git-server.cicd.svc.cluster.local/conf.git + targetRevision: master + path: f3s/miniflux/helm-chart + destination: + server: https://kubernetes.default.svc + namespace: services + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=false + retry: + limit: 3 + backoff: + duration: 5s + factor: 2 + maxDuration: 1m +``` + +Then applied it: + +```sh +# 1. Apply the Application manifest +$ kubectl apply -f argocd-apps/services/miniflux.yaml +application.argoproj.io/miniflux created + +# 2. Verify ArgoCD adopted the existing resources +$ argocd app get miniflux +Name: miniflux +Sync Status: Synced to master (4e3c216) +Health Status: Healthy + +# 3. Test that the app still works +$ curl -I https://flux.f3s.foo.zone +HTTP/2 200 +``` + +About 10 minutes, zero downtime. ArgoCD recognised the already-running resources matched the Helm chart in Git and adopted them without re-deploying. + +After the migration, the Justfile turned into utility commands--no more install/upgrade/uninstall: + +```makefile +status: + @kubectl get pods -n services -l app=miniflux-server + @kubectl get pods -n services -l app=miniflux-postgres + @kubectl get application miniflux -n cicd \ + -o jsonpath='Sync: {.status.sync.status}, Health: {.status.health.status}' + +sync: + @kubectl annotate application miniflux -n cicd \ + argocd.argoproj.io/refresh=normal --overwrite + +logs: + kubectl logs -n services -l app=miniflux-server --tail=100 -f + +restart: + kubectl rollout restart -n services deployment/miniflux-server + +port-forward port="8080": + kubectl port-forward -n services svc/miniflux {{port}}:8080 + +psql: + kubectl exec -it -n services deployment/miniflux-postgres -- psql -U miniflux +``` + +New workflow: edit chart, commit, push. ArgoCD picks it up within a few minutes. Run `just sync` if you're impatient. + +### Migration Order + +I started with the simplest services (miniflux, wallabag, radicale, etc.)--apps with straightforward Helm charts and no complex dependencies. This let me validate the pattern before touching anything critical. + +After that: infrastructure apps (registry, cert-manager, pkgrepo, traefik-config), then the monitoring stack (tempo, loki, alloy, and finally prometheus--the most complex one), and last the CI/CD tools (git-server, argo-rollouts). + +## Complex Migration: Prometheus Multi-Source + +Prometheus was the tricky one. It combines an upstream Helm chart with a bunch of custom manifests--recording rules, dashboards, persistent volumes, and a post-sync hook to restart Grafana. + +ArgoCD's multi-source feature handles this cleanly: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: prometheus + namespace: cicd +spec: + sources: + # Source 1: Upstream Helm chart + - repoURL: https://prometheus-community.github.io/helm-charts + chart: kube-prometheus-stack + targetRevision: 55.5.0 + helm: + releaseName: prometheus + valuesObject: + kubeEtcd: + enabled: true + endpoints: + - 192.168.2.120 + - 192.168.2.121 + - 192.168.2.122 + # ... hundreds of lines of config + + # Source 2: Custom manifests from Git + - repoURL: http://git-server.cicd.svc.cluster.local/conf.git + targetRevision: master + path: f3s/prometheus/manifests + + syncPolicy: + automated: + prune: false # Manual pruning--too risky for the monitoring stack + selfHeal: true + syncOptions: + - ServerSideApply=true +``` + +The `prometheus/manifests/` directory has 13 files, each with a sync wave annotation to control deployment order: + +``` +f3s/prometheus/manifests/ +├── persistent-volumes.yaml # Wave 0 +├── grafana-restart-rbac.yaml # Wave 0 +├── additional-scrape-configs-secret.yaml # Wave 1 +├── grafana-datasources-configmap.yaml # Wave 1 +├── freebsd-recording-rules.yaml # Wave 3 +├── openbsd-recording-rules.yaml # Wave 3 +├── zfs-recording-rules.yaml # Wave 3 +├── argocd-application-alerts.yaml # Wave 3 +├── epimetheus-dashboard.yaml # Wave 4 +├── zfs-dashboards.yaml # Wave 4 +├── argocd-applications-dashboard.yaml # Wave 4 +├── node-resources-multi-select-dashboard.yaml # Wave 4 +├── prometheus-nodeport.yaml # Wave 4 +└── grafana-restart-hook.yaml # Wave 10 (PostSync) +``` + +### Sync Waves + +Without sync waves, ArgoCD deploys all resources at once in no particular order. That's fine for simple apps, but for something like Prometheus it causes failures--a PersistentVolumeClaim can't bind if the PersistentVolume doesn't exist yet, and a PrometheusRule |
