05 GitOps and IaC - seadogger-tech/seadogger-homelab GitHub Wiki
π Diagram Viewing Recommendation
The interactive Mermaid diagrams below are optimized for GitHub Dark Mode to provide maximum readability and visual impact.
To enable Dark Mode: GitHub Settings β Appearance β Theme β Dark default
Light mode users can still view the diagrams, though colors may appear less vibrant.
This page explains how automation is split between Ansible and ArgoCD, and provides a practical test plan.
For CI/CD automation and repo workflows, see 15-CI-CD-and-GitHub-Actions.

-
Ansible is the orchestrator for infrastructure and bootstrap:
- k3s control plane/workers, firmware, kernel settings
- MetalLB, RookβCeph (operators/CRDs), ArgoCD (native)
- Internal PKI with certβmanager (Root/Intermediate, ClusterIssuer, perβapp certs)
- App deployment triggers via perβapp task files
-
ArgoCD manages desired state for apps and some infra components via Git:
- Sources from
core/deployments/**(Helm values and manifests) - Reconciles Kubernetes resources for apps like Pi-hole, OpenWebUI, n8n, Jellyfin, Nextcloud
- Sources from
-
Domains & certificates:
- Services:
*.seadogger-homelab - Issuer:
internal-local-issuer - TLS secrets:
*-local-tls
- Services:
-
Operator switches (in
ansible/config.yml):-
cold_start_stage_1_wipe_cluster(destructive;cleanup.ymlonly) -
cold_start_stage_2_install_infrastructure(infra viamain.yml) -
cold_start_stage_3_install_applications(apps viamain.yml) -
manual_install_*flags per app
-
This section explains the complete deployment flow from initial code changes to running applications.
graph LR
Dev[π€ Developer] -->|1. Git Push| GitHub[π¦ GitHub<br/>seadogger-homelab]
GitHub -->|2. Checkout| Ansible[βοΈ Ansible<br/>Playbooks]
Ansible -->|3a. Deploy Infra| Helm[π Helm<br/>Charts]
Helm -->|3b. Install| K8s[βΈοΈ Kubernetes<br/>Cluster]
Ansible -->|4. Bootstrap| ArgoCD[π ArgoCD]
ArgoCD -->|5. Watch| GitHub
ArgoCD -->|6. Sync| K8s
GitHub -->|7. Wiki Update| WikiAction[π€ GitHub Action]
WikiAction -->|8. Publish| Wiki[π GitHub Wiki]
style Dev fill:#7b1fa2,stroke:#9c27b0,stroke-width:2px
style GitHub fill:#2d5016,stroke:#5a9216,stroke-width:2px
style Ansible fill:#1e3a5f,stroke:#4a90e2,stroke-width:2px
style Helm fill:#1e3a5f,stroke:#4a90e2,stroke-width:2px
style K8s fill:#1e3a5f,stroke:#4a90e2,stroke-width:2px
style ArgoCD fill:#8b4513,stroke:#d2691e,stroke-width:2px
style WikiAction fill:#2d5016,stroke:#5a9216,stroke-width:2px
style Wiki fill:#2d5016,stroke:#5a9216,stroke-width:2px
Developer makes changes locally:
# Edit deployment manifests, Helm values, or Ansible playbooks
cd /path/to/seadogger-homelab-pro/core
vim deployments/nextcloud/nextcloud-values.yaml
# Commit and push to core repo
git add . && git commit -m "Update Nextcloud configuration" && git push
# Update Pro repo submodule pointer
cd /path/to/seadogger-homelab-pro
git add core && git commit -m "Update core submodule: Nextcloud config" && git pushWhat happens: Changes are now in GitHub, ready for deployment or automated sync.
Initial cluster setup uses Ansible:
sequenceDiagram
participant Operator
participant Ansible
participant Helm
participant K8s
participant ArgoCD
Operator->>Ansible: ansible-playbook main.yml
Note over Ansible: Stage 2: Infrastructure
Ansible->>K8s: Deploy k3s cluster
Ansible->>Helm: helm install metallb
Helm->>K8s: Create MetalLB resources
Ansible->>Helm: helm install rook-ceph
Helm->>K8s: Create Rook operators
Ansible->>Helm: helm install argocd
Helm->>K8s: Create ArgoCD controllers
Ansible->>K8s: Generate PKI certificates
Ansible->>K8s: Create ClusterIssuer
Note over Ansible: Stage 3: Applications
Ansible->>ArgoCD: Create Application CRDs
ArgoCD->>K8s: Deploy apps from Git
Ansible's responsibilities:
- Bootstrap only - One-time cluster setup
- Helm wrapper - Installs MetalLB, Rook-Ceph, ArgoCD using Helm
- PKI setup - Generates Root/Intermediate CA certificates
- ArgoCD bootstrap - Creates initial Application resources
What Ansible does NOT do:
- β Manage application state (ArgoCD handles this)
- β Update apps after initial deployment (GitOps takes over)
- β Track drift (ArgoCD auto-syncs from Git)
After bootstrap, ArgoCD takes over:
sequenceDiagram
participant GitHub
participant ArgoCD
participant K8s
participant App
Note over ArgoCD: Continuous reconciliation loop
loop Every 3 minutes (default)
ArgoCD->>GitHub: Poll core/deployments/**
GitHub-->>ArgoCD: Return manifests/values
ArgoCD->>ArgoCD: Compare desired vs actual
alt Changes detected
ArgoCD->>K8s: Apply updated manifests
K8s->>App: Update pods/resources
ArgoCD->>ArgoCD: Mark "Synced"
else No changes
ArgoCD->>ArgoCD: Mark "Healthy"
end
end
ArgoCD's responsibilities:
- Continuous sync - Polls Git every 3 minutes
- Drift detection - Compares cluster state to Git
- Auto-healing - Reverts manual changes back to Git state
- Health checks - Monitors application health
Example ArgoCD Application:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nextcloud
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/seadogger-tech/seadogger-homelab
path: deployments/nextcloud
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: nextcloud
syncPolicy:
automated:
prune: true # Delete resources removed from Git
selfHeal: true # Revert manual changes
syncOptions:
- CreateNamespace=true| Component | Deployed By | Method | Managed By |
|---|---|---|---|
| K3s Cluster | Ansible | Direct install | Ansible (manual upgrades) |
| MetalLB | Ansible | Helm (native) | Ansible + Helm (manual upgrades) |
| Rook-Ceph | Ansible | Helm (native) | Ansible + Helm (manual upgrades) |
| ArgoCD | Ansible | Helm (native) | ArgoCD (self-managed) |
| Cert-Manager | ArgoCD | Application CRD | ArgoCD (GitOps) |
| Internal PKI | Ansible | OpenSSL + kubectl | Manual (CA rotation runbook) |
| Applications | ArgoCD | Application CRD | ArgoCD (GitOps) |
Example: Change Nextcloud storage size
# 1. Edit Helm values
cd core/deployments/nextcloud
vim nextcloud-values.yaml # Change persistence.size
# 2. Commit and push
cd core
git add . && git commit -m "Increase Nextcloud storage to 6TB" && git push
# 3. Update Pro submodule pointer
cd ..
git add core && git commit -m "Update core: Nextcloud storage" && git push
# 4. Wait for ArgoCD to sync (automatic, ~3 minutes)
# Or force sync: kubectl -n argocd patch app nextcloud -p '{"operation":{"initiatedBy":{"username":"admin"},"sync":{}}}'Result: ArgoCD detects change, updates PVC, Nextcloud pod restarts with new storage.
Example: Upgrade MetalLB
# 1. Update Helm chart version in Ansible
vim core/ansible/tasks/metallb_deploy.yml # Change chart version
# 2. Run Ansible playbook
ansible-playbook main.yml # With appropriate flags
# 3. Verify upgrade
helm list -n metallb-systemNote: Issue #50 proposes moving this to ArgoCD for GitOps management.
Separate from deployment, but related:
sequenceDiagram
participant Dev
participant GitHub
participant Action
participant Wiki
Dev->>GitHub: Push to core/docs/wiki/
GitHub->>Action: Trigger publish-wiki.yml
Action->>GitHub: Clone wiki repo
Action->>Action: rsync docs/wiki/ β wiki/
Action->>Wiki: Commit and push
GitHub->>Wiki: Render updated pages
Wiki update process:
- Developer edits
.mdfiles incore/docs/wiki/ - Push to
masterbranch triggers GitHub Action - Action syncs content to wiki repository
- GitHub renders updated wiki pages
Goal: Everything except K3s managed by ArgoCD
graph TD
subgraph Current["π΄ Current State"]
AnsibleInfra[Ansible deploys<br/>MetalLB, Rook, ArgoCD]
AnsiblePKI[Ansible generates<br/>PKI certificates]
ArgoApps[ArgoCD deploys<br/>Applications]
end
subgraph Future["π’ Future State"]
AnsibleMinimal[Ansible deploys<br/>K3s only]
ArgoAll[ArgoCD deploys<br/>Everything else]
Kustomize[All infrastructure<br/>as Kustomize]
end
Current -.->|Refactor| Future
style Current fill:#8b4513,stroke:#d2691e,stroke-width:2px
style Future fill:#2d5016,stroke:#5a9216,stroke-width:2px
Benefits of pure GitOps:
- β Single source of truth - Everything in Git
- β Visible in ArgoCD UI - See all infrastructure
- β Automated sync - No manual Ansible runs
- β Easy rollback - Git revert + auto-sync
- β Explicit dependencies - Via ArgoCD sync waves
- Ansible = Bootstrap - One-time cluster setup, infrastructure deployment
- Helm = Packaging - Kubernetes resource templates and values
- ArgoCD = GitOps - Continuous sync from Git to cluster
- GitHub = Source of Truth - All desired state stored in Git
The flow:
Developer β Git Push β GitHub β ArgoCD (polls) β Kubernetes β Running Apps
β
GitHub Action β Wiki
Remember:
- Infrastructure changes currently require Ansible re-run (being refactored)
- Application changes are automatic via ArgoCD sync
- Always commit to core first, then update Pro submodule pointer
- Wiki updates automatically on push to core repo
- Deploy
- In
config.yml, setcold_start_stage_3_install_applications: trueandmanual_install_bedrock: true(othersfalse) - Run
ansible-playbook main.yml
- In
- Verify
-
bedrock-access-gatewaypods running inbedrock-gateway
-
- Cleanup
- Set
run_pod_cleanup: trueand keep onlybedrock-access-gatewayinpod_cleanup_list - Run
ansible-playbook cleanup.yml
- Set
- Verify
-
bedrock-gatewaynamespace and pods removed
-
- Deploy
- Set
manual_install_bedrock: false,manual_install_openwebui: true - Run
ansible-playbook main.yml
- Set
- Verify
-
openwebuipods running and PVC bound
-
- Cleanup (preserve data)
- Set
run_pod_cleanup: true, configurepod_cleanup_listwithopenwebuianddelete_pvc: false - Run
ansible-playbook cleanup.yml
- Set
- Verify & redeploy
- Pods removed, PVC remains; rerun deploy and confirm reβattach
- Cleanup (destroy data)
- Set
delete_pvc: trueforopenwebui - Run
ansible-playbook cleanup.yml
- Set
- Verify
- Pods and PVC are deleted
- Deploy
- Enable
cold_start_stage_3_install_applications: trueand all relevantmanual_install_* - Run
ansible-playbook main.yml
- Enable
- Cleanup
- Set
run_pod_cleanup: trueand include all apps inpod_cleanup_list - Run
ansible-playbook cleanup.yml
- Set
- Verify
- All application namespaces and resources are gone
- Deploy
- Start from a full application and infrastructure deployment
- Full teardown
- Set
cold_start_stage_1_wipe_cluster: trueand optionallyperform_physical_disk_wipe: true - Run
ansible-playbook cleanup.yml
- Set
- Verify teardown
- Cluster wiped; nodes clean
- Full reinstall
- Reset all cleanup flags to
false - Set Stage 2 + Stage 3 true; run
ansible-playbook main.yml
- Reset all cleanup flags to
- Verify reinstall
- Entire cluster and apps are back online
- 04-Bootstrap-and-Cold-Start - Initial cluster deployment procedures
- 15-CI-CD-and-GitHub-Actions - Automated CI/CD workflows
- 21-Deployment-Dependencies - GitOps migration and dependency analysis
- 02-Architecture - GitOps architectural decisions
Related Issues:
- #48 - Deployment Dependencies Refactor - Pure GitOps migration
- #50 - Move infrastructure to ArgoCD - Converting all to Kustomize
This diagram proposes an ArgoCD sync wave sequence; the playbook imports in
ansible/main.yml remain the current deployment order.
graph TD
subgraph Wave0["π Wave 0: Operators & Base"]
MetalLB[MetalLB]
RookOp[Rook-Ceph<br/>Operator]
CertMgr[cert-manager]
end
subgraph Wave1["π Wave 1: Clusters & PKI"]
RookCluster[Rook-Ceph<br/>Cluster + Storage Classes]
PKI[Internal PKI<br/>CA + ClusterIssuer]
end
subgraph Wave2["π Wave 2: Applications"]
Apps[Nextcloud, Jellyfin<br/>OpenWebUI, N8N<br/>PiHole]
end
subgraph WavePro["π Wave 3: Pro Features"]
Pro[Portal<br/>Keycloak Future]
end
Wave0 --> Wave1
Wave1 --> Wave2
Wave2 --> WavePro
style Wave0 fill:#1e3a5f,stroke:#4a90e2,stroke-width:3px
style Wave1 fill:#1e3a5f,stroke:#4a90e2,stroke-width:3px
style Wave2 fill:#2d5016,stroke:#5a9216,stroke-width:3px
style WavePro fill:#7b1fa2,stroke:#9c27b0,stroke-width:3px,stroke-dasharray: 5 5
Wave Ordering Rationale:
- Wave 0: Foundation services (MetalLB for IPs, Rook operator, cert-manager)
- Wave 1: Clusters that depend on operators (Ceph cluster, PKI setup)
- Wave 2: Applications (depend on all infrastructure)
- Wave 3: Pro features (depend on base applications)