Configuration management and automated deployment (GitOps) repository for RKE2 (Rancher Kubernetes Engine 2) clusters running on openSUSE Leap Micro 6.2, integrated directly with the lab-ipxe-os automated provisioning server.
The system is designed to run both environments (Dev & Prod) concurrently on a single RKE2 cluster (Single-Cluster Multi-Environment) via Namespace isolation and ArgoCD ApplicationSets.
A hybrid architecture combining App-of-Apps (bootstrapping core platform services) and ApplicationSet (automated discovery and deployment of dev and prod applications):
[ Git Repository ]
https://github.com/001123/lab-rke2-gitops
│
▼
[ ArgoCD Root Bootstrap ]
(path: "bootstrap")
│
┌────────────────────┴────────────────────┐
▼ (Sync Waves 1-3) ▼ (Sync Wave 4)
[ Platform Apps ] [ ApplicationSet ]
(Cluster-wide shared platform) (tenant-applications)
│ │
┌────────┴────────┐ ┌────────┴────────┐
▼ ▼ ▼ ▼
[local-path-storage] [cert-manager] [nginx-demo-dev] [nginx-demo-prod]
/var/local-path Internal CA 1 replica 2 replicas
(Btrfs safe) ClusterIssuer (dev-demo...nip.io) (demo...nip.io)
lab-rke2-gitops/
├── README.md # Architecture and operations documentation
├── Makefile # Infrastructure orchestration shortcuts (make all, make rke2, ...)
├── .sops.yaml # SOPS & Age encryption rules
├── .gitignore # Ignores temporary files and secrets
├── infra/ # Infrastructure automation layer
│ └── ansible/ # Ansible playbooks, roles & inventory for Ubuntu & RKE2
│ ├── ansible.cfg # Ansible configuration (SOPS vars plugin enabled)
│ ├── inventory/ # Node IPs (hosts.yml) and variables (group_vars)
│ ├── playbooks/ # site.yml, rke2.yml, argocd.yml
│ └── roles/ # common, rke2_server, sops_age, argocd_manifests
├── scripts/
│ └── validate.sh # Kustomize syntax validation script (100% pass)
├── bootstrap/ # ArgoCD Root Application entrypoint (gitops_path: bootstrap)
│ ├── root-app.yaml # Root Application bootstrap manifest
│ ├── kustomization.yaml # Root bootstrap bundle
│ ├── platform-apps.yaml # ArgoCD Apps for core platform services (Waves 1-3)
│ └── applicationset.yaml # ApplicationSet generating <app>-dev and <app>-prod (Wave 4)
├── platform/ # Core system platform (Cluster-scoped, shared)
│ ├── local-path-provisioner/ # Dynamic StorageClass (Rancher Local Path)
│ │ ├── kustomization.yaml
│ │ └── local-path-provisioner.yaml # hostPath: /var/local-path-provisioner (Btrfs safe)
│ ├── cert-manager/ # Automated SSL/TLS certificate issuance and renewal
│ │ ├── kustomization.yaml
│ │ ├── namespace.yaml
│ │ └── cluster-issuers.yaml # Self-Signed Root CA & Local CA ClusterIssuer
│ ├── monitoring/ # Observability stack (kube-prometheus-stack + Grafana)
│ │ ├── kustomization.yaml # Kustomize bundle with KSOPS generator
│ │ ├── namespace.yaml # monitoring namespace
│ │ ├── ksops-secret-generator.yaml # KSOPS generator for Grafana admin secret
│ │ ├── grafana-secret.sops.yaml # SOPS/Age encrypted Grafana admin credentials
│ │ └── values.yaml # Helm values for Prometheus, Grafana, Exporters
│ └── zot/ # Zot OCI Container Registry + Web UI (Wave 3)
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ ├── pvc.yaml # 30Gi local-path storage
│ ├── configmap.yaml # OCI specs & Web UI enabled
│ ├── deployment.yaml
│ ├── service.yaml
│ └── ingress.yaml # registry.192.168.250.215.nip.io
└── apps/ # Workloads & user-facing services
├── nginx-demo/ # Sample NGINX app with Traefik Ingress + automated TLS
│ ├── base/
│ │ ├── kustomization.yaml
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── ingress.yaml
│ └── overlays/
│ ├── dev/ # Dev environment (1 replica, dev-demo.192.168.250.2.nip.io)
│ │ ├── kustomization.yaml
│ │ └── patch-ingress.yaml
│ └── prod/ # Prod environment (2 replicas, demo.192.168.250.2.nip.io)
│ ├── kustomization.yaml
│ └── patch-ingress.yaml
├── anything-llm/ # AnythingLLM RAG & AI workspace (Dev environment)
│ ├── base/
│ │ ├── kustomization.yaml
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ ├── ingress.yaml
│ │ ├── pvc.yaml
│ │ └── secret.yaml
│ └── overlays/
│ └── dev/ # Dev environment (dev-anythingllm.192.168.250.215.nip.io)
│ ├── kustomization.yaml
│ └── patch-ingress.yaml
├── plane/ # Plane CE project management platform (Dev environment)
│ ├── base/ # Declarative K8s manifests (Web, Backend, Space, Live, Admin, DB, Redis, MinIO)
│ └── overlays/
│ └── dev/ # Dev environment (dev-plane.192.168.250.215.nip.io)
│ ├── kustomization.yaml
│ ├── patch-ingress.yaml
│ └── patch-env.yaml
├── dsh/ # DeepSeek Harness Web Agent Platform (Dev environment)
│ ├── docker/ # Dockerfile & build.sh (OrbStack cross-compile to linux/amd64)
│ ├── base/ # Deployment, Service, Ingress, PVC 10Gi, Secret
│ └── overlays/
│ └── dev/ # Dev environment (dev-dsh.192.168.250.215.nip.io)
├── temporal/ # Temporal Workflow Engine v1.32.1 + Web UI (Dev environment)
│ ├── base/ # Server (auto-setup 1.32.1), UI, PostgreSQL 15, Ingress, Secret
│ └── overlays/
│ └── dev/ # Dev environment (dev-temporal.192.168.250.215.nip.io)
└── temporal-demo/ # Temporal TypeScript E-Commerce Order Saga Demo (Dev environment)
├── src/ # Workflows, Activities, Worker, Express Server
├── public/ # Modern Web UI & Stepper Dashboard
├── docker/ # Dockerfile & build.sh
├── base/ # Web & Worker Deployments, Service, Ingress
└── overlays/
└── dev/ # Dev environment (dev-temporal-demo.192.168.250.215.nip.io)
-
Btrfs Transactional (Read-only Rootfs) Safety:
- openSUSE Leap Micro 6.2 protects the operating system with a read-only rootfs (
/). local-path-provisioneris configured to persist volumes under/var/local-path-provisioner(a writable Btrfs subvolume under/var), ensuring Pod Persistent Volume Claims (PVC) never encounterRead-only file systemerrors.- Provides a cluster-wide default StorageClass
local-path(storageclass.kubernetes.io/is-default-class: "true").
- openSUSE Leap Micro 6.2 protects the operating system with a read-only rootfs (
-
Deterministic Sync Order (Sync Waves):
- Wave 1:
local-path-provisionerdeploys first to make dynamic storage available. - Wave 2:
cert-managercontroller and CRDs install from the official Jetstack Helm chart (v1.16.2). - Wave 3: Creates
selfsigned-cluster-issuerandlocal-ca-issueronce the cert-manager CRDs are fully loaded and operational. - Wave 4:
ApplicationSetactivates, automatically discovering and deploying workloads underapps/across bothdevandprodenvironments. Any Ingress annotated withcert-manager.io/cluster-issuer: local-ca-issueris immediately issued a valid TLS certificate.
- Wave 1:
-
Built-in Traefik Ingress:
- RKE2 bundles the Traefik Ingress Controller by default. Ingress resources use
ingressClassName: traefikand route using wildcard domain naming:*.192.168.250.2.nip.io.
- RKE2 bundles the Traefik Ingress Controller by default. Ingress resources use
In config/hosts.yaml of the lab-ipxe-os project, configure the RKE2 node with clean, concise GitOps settings:
hosts:
"bc:24:11:00:24:35":
hostname: "rke2-micro-node"
os: suse-micro
version: "6.2"
profile: rke2-single-node
custom:
rke2_ingress: "traefik"
rke2_cni: "canal"
# Enable ArgoCD and connect directly to the GitOps repo:
argocd: true
argocd_hostname: "argocd.192.168.250.2.nip.io"
gitops_repo: "https://github.com/001123/lab-rke2-gitops.git"
gitops_branch: "main"
gitops_path: "bootstrap" # <-- Pointing to bootstrap loads both dev & prod
network:
dhcp: false
ip: "192.168.250.2"
netmask: "255.255.255.0"
gateway: "192.168.250.1"
nameservers: ["192.168.250.1", "1.1.1.1"]
storage:
target_disk: "/dev/sda"Once the node completes its iPXE unattended installation:
- RKE2 starts up and automatically loads the ArgoCD manifest at
/var/lib/rancher/rke2/server/manifests/argocd.yaml. - ArgoCD clones the
bootstrappath fromhttps://github.com/001123/lab-rke2-gitops.git. - The cluster bootstraps core platform services (
local-path,cert-manager) and concurrently deploys bothnginx-demo-devandnginx-demo-prod!
To deploy RKE2 and bootstrap GitOps onto a freshly installed Ubuntu Server:
-
Configure Target Node IP: Edit hosts.yml with your Ubuntu Server IP and SSH user (
ubuntu):rke2-node-1: ansible_host: <YOUR_UBUNTU_IP> ansible_user: ubuntu
-
Test Connectivity:
make ping
-
Deploy OS Tuning + RKE2 + ArgoCD GitOps:
make all
Ansible will configure Ubuntu kernel modules, install RKE2 v1.37.1, fetch
./kubeconfig/rke2.yaml, and deploy ArgoCD addon manifests. -
Verify Workloads & Access ArgoCD:
make nodes make pods make argocd-password
| Application | Environment | Replicas | Access URL | Namespace |
|---|---|---|---|---|
| ArgoCD Dashboard | System | 1 | http://argocd.192.168.250.215.nip.io |
argocd |
| Demo NGINX | Dev | 1 | https://dev-demo.192.168.250.215.nip.io |
nginx-demo-dev |
| Demo NGINX | Prod | 2 | https://demo.192.168.250.215.nip.io |
nginx-demo-prod |
Note
The kubeconfig file contains sensitive cluster credentials and is excluded from Git tracking via .gitignore. You must provide this file locally to interact with the cluster using kubectl.
Fetch the kubeconfig directly from the RKE2 node, replace the localhost loopback address (127.0.0.1) with the node's LAN IP (192.168.250.2), and secure file permissions:
# Fetch and patch kubeconfig into repository root
ssh root@192.168.250.2 "cat /etc/rancher/rke2/rke2.yaml" | sed 's/127.0.0.1/192.168.250.2/g' > ./kubeconfig
chmod 600 ./kubeconfig
# Export KUBECONFIG for your current terminal session
export KUBECONFIG=$(pwd)/kubeconfigIf configuring manually or copying via SCP:
- Copy
/etc/rancher/rke2/rke2.yamlfrom the node (192.168.250.2). - Edit the cluster server address:
# Change: server: https://127.0.0.1:6443 # To: server: https://192.168.250.2:6443
- Save the file as
./kubeconfigin the root of this repository. - Set permissions and export the environment variable:
chmod 600 ./kubeconfig export KUBECONFIG=$(pwd)/kubeconfig
# Check cluster node status
kubectl get nodes -o wide
# Check all running workloads across dev, prod, and system namespaces
kubectl get pods -A./scripts/validate.shTo access cluster Ingress HTTPS endpoints (https://argocd..., https://demo..., https://dev-demo...) on macOS without TLS/SSL warnings:
# Export Root CA and install into macOS System Keychain (requires sudo)
make sync-ca-mac
# Check status of Root CA in cluster & macOS Keychain
make status-ca-mac
# (Optional) Remove Root CA from macOS Keychain
make untrust-ca-macThe cluster runs a full GitOps-managed kube-prometheus-stack with Traefik Ingress and automated TLS:
- Grafana Web UI: https://grafana.192.168.250.215.nip.io
- Default Username:
admin - Admin Password: Retrieve using SOPS decryption:
make grafana-password
- Check Status of Pods, PVCs, and Ingress:
make monitoring
- Create directories:
apps/myapp/base/,apps/myapp/overlays/dev/,apps/myapp/overlays/prod/. - Define the Kubernetes manifests and
kustomization.yamlfiles. - Commit and push to Git:
git add apps/myapp git commit -m "feat: add myapp workload" git push origin main - ApplicationSet will automatically detect the new application and deploy both
myapp-devandmyapp-prodto ArgoCD without any manual configuration changes.