This guide details the setup and configuration of ArgoCD, which serves as the GitOps engine for our Kubernetes cluster.
graph TD
A[K3s Cluster] -->|Install| B[ArgoCD]
B -->|Create| C[AppProjects]
C -->|Deploy| D[ApplicationSets]
D -->|Generate| E[Applications]
E -->|Sync| F[Resources]
subgraph "Three-Tier Architecture"
G[Infrastructure Tier]
H[Monitoring Tier]
I[Applications Tier]
end
D --> G
D --> H
D --> I
sequenceDiagram
participant User
participant ArgoCD
participant Cluster
User->>Cluster: Install Initial Components
Note over User,Cluster: kubectl apply -f projects.yaml
User->>Cluster: Apply Infrastructure ApplicationSet
Note over User,Cluster: kubectl apply -f infrastructure-components-appset.yaml
Cluster->>ArgoCD: Create Infrastructure Applications
ArgoCD->>Cluster: Deploy Infrastructure Components (wave -2)
Note over ArgoCD,Cluster: Cilium, Cert-Manager, etc.
User->>Cluster: Apply Monitoring ApplicationSet
Note over User,Cluster: kubectl apply -f monitoring-components-appset.yaml
Cluster->>ArgoCD: Create Monitoring Applications
ArgoCD->>Cluster: Deploy Monitoring Components (wave 0)
Note over ArgoCD,Cluster: Prometheus, Grafana, etc.
User->>Cluster: Apply Applications ApplicationSet
Note over User,Cluster: kubectl apply -f myapplications-appset.yaml
Cluster->>ArgoCD: Create User Applications
ArgoCD->>Cluster: Deploy User Applications (wave 1)
Note over ArgoCD,Cluster: Media apps, AI tools, etc.
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/latest/download/experimental-install.yaml
k3s kubectl kustomize --enable-helm infrastructure/controllers/argocd | k3s kubectl apply -f -
kubectl wait --for=condition=available deployment -l app.kubernetes.io/name=argocd-server -n argocd --timeout=300s
kubectl wait --for=condition=established crd/applications.argoproj.io --timeout=60s
kubectl wait --for=condition=established crd/appprojects.argoproj.io --timeout=60s
ArgoCD projects define permissions and boundaries for applications. Our cluster uses four main projects:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: infrastructure
namespace: argocd
spec:
sourceRepos:
- "*"
destinations:
- namespace: "*"
server: "*"
clusterResourceWhitelist:
- group: "*"
kind: "*"
---
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: applications
namespace: argocd
spec:
sourceRepos:
- "*"
destinations:
- namespace: "*"
server: "*"
clusterResourceWhitelist:
- group: "*"
kind: "Namespace"
- group: "*"
kind: "PersistentVolume"
- group: "networking.k8s.io"
kind: "*"
- group: "gateway.networking.k8s.io"
kind: "*"
---
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: monitoring
namespace: argocd
spec:
sourceRepos:
- "*"
destinations:
- namespace: "*"
server: "*"
clusterResourceWhitelist:
- group: "*"
kind: "Namespace"
- group: "monitoring.coreos.com"
kind: "*"
---
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: ai
namespace: argocd
spec:
sourceRepos:
- "*"
destinations:
- namespace: "*"
server: "*"
clusterResourceWhitelist:
- group: "*"
kind: "Namespace"
- group: "*"
kind: "PersistentVolume"
- group: "networking.k8s.io"
kind: "*"
- group: "gateway.networking.k8s.io"
kind: "*"
We use three main ApplicationSets to manage our deployments:
Located at infrastructure/infrastructure-components-appset.yaml
, this ApplicationSet manages infrastructure components like Cilium, Cert-Manager, and other core services.
Located at monitoring/monitoring-components-appset.yaml
, this ApplicationSet manages monitoring components like Prometheus, Grafana, and other observability tools.
Located at my-apps/myapplications-appset.yaml
, this ApplicationSet manages user applications like media servers, AI applications, and other user-facing services.
Apply the resources in the following order:
- Apply the projects first:
kubectl apply -f infrastructure/controllers/argocd/projects.yaml -n argocd
- Apply the ApplicationSets in order:
kubectl apply -f infrastructure/infrastructure-components-appset.yaml -n argocd
kubectl apply -f monitoring/monitoring-components-appset.yaml -n argocd
kubectl apply -f my-apps/myapplications-appset.yaml -n argocd
The repository follows a clean three-tier structure:
/infrastructure/
- Infrastructure components (network, security, etc.)/monitoring/
- Monitoring components (Prometheus, Grafana, etc.)/my-apps/
- User applications (media servers, AI tools, etc.)
-
Three-Tier Architecture:
- Clear separation of concerns
- Controlled deployment order
- Simplified management
-
Sync Waves:
- Infrastructure: -2 (deployed first)
- Monitoring: 0 (deployed second)
- Applications: 1 (deployed last)
-
Simplified Directory Patterns:
- No complex include/exclude logic
- One ApplicationSet per tier
- Clear path patterns
graph TD
subgraph "Deployment Options"
A[Pure K8s Manifests] --> B[Kustomize]
C[Helm Charts] --> D[values.yaml]
end
subgraph "Developer Experience"
B --> E[Direct kubectl apply]
B --> F[ArgoCD Sync]
D --> G[helm install]
D --> F
end
style A fill:#9f9,stroke:#333
style C fill:#f9f,stroke:#333
style F fill:#9cf,stroke:#333
-
Portability
- Manifests can be applied directly with
kubectl
- No dependency on ArgoCD for development/testing
- Easy to understand and modify
- Manifests can be applied directly with
-
Transparency
- Clear view of what's being deployed
- No templating abstraction
- Direct mapping to Kubernetes objects
-
Flexibility
- Mix and match with Helm when needed
- Easy to customize with Kustomize
- No lock-in to specific tools
graph TD
subgraph "Pure Manifests"
A[YAML Files] --> B[Kustomize]
B --> C[Overlay Management]
C --> D[Direct Application]
end
subgraph "Helm Charts"
E[Templates] --> F[values.yaml]
F --> G[Chart Dependencies]
G --> H[Package Management]
end
style A fill:#9f9,stroke:#333
style E fill:#f9f,stroke:#333
-
Pure Manifests + Kustomize
- Simple applications
- Clear configuration needs
- Direct control requirements
- Development environments
-
Helm Charts
- Complex applications
- Many configuration options
- Version management needed
- Third-party applications
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: example-helm-app
spec:
source:
chart: example
repoURL: https://charts.example.com
targetRevision: 1.2.3
helm:
values: |
key: value
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: example-kustomize-app
spec:
source:
path: apps/example
repoURL: https://github.com/org/repo
targetRevision: HEAD
graph TD
subgraph "Application Types"
A[Pure Manifests] --> B[apps/*]
C[Helm Charts] --> D[charts/*]
end
subgraph "Configuration"
B --> E[kustomization.yaml]
D --> F[values.yaml]
end
subgraph "ArgoCD"
E --> G[Application CR]
F --> G
end
sequenceDiagram
participant Dev
participant Git
participant K8s
participant ArgoCD
Dev->>Git: Push manifests
Note over Dev,K8s: Can test directly
Dev->>K8s: kubectl apply
Note over Git,ArgoCD: Or use GitOps
Git->>ArgoCD: Webhook
ArgoCD->>K8s: Apply changes
sequenceDiagram
participant Dev
participant Git
participant ArgoCD
participant K8s
Dev->>Git: Push changes
Git->>ArgoCD: Webhook
ArgoCD->>ArgoCD: Validate
ArgoCD->>K8s: Sync
K8s->>ArgoCD: Status
- Group related resources
- Use consistent naming
- Leverage labels and annotations
metadata:
labels:
app.kubernetes.io/name: example
app.kubernetes.io/part-of: system
# kustomization.yaml
resources:
- deployment.yaml
- service.yaml
commonLabels:
app: example
# Application with both Kustomize and Helm
spec:
source:
plugin:
name: kustomize-with-helm
configManagementPlugins: |
- name: kustomize-with-helm
generate:
command: ["sh", "-c"]
args: ["kustomize build --enable-helm"]
metadata:
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
- Export current Helm values
- Generate manifests
- Adapt to Kustomize
- Test with kubectl
- Commit to Git
- Create Helm templates
- Extract values
- Test locally
- Update ArgoCD application
graph TD
A[New Manifest] --> B{Test Locally}
B -->|Success| C[Commit to Git]
B -->|Fail| D[Modify]
C --> E[ArgoCD Sync]
E -->|Success| F[Done]
E -->|Fail| D
graph TD
subgraph "1. Initial Setup"
A[Install ArgoCD] --> B[Create Projects]
B --> C[infrastructure project]
B --> D[applications project]
B --> M[monitoring project]
B --> N[ai project]
end
subgraph "2. Infrastructure Deployment"
C --> E[Apply infrastructure ApplicationSet]
E --> F[networking]
E --> G[storage]
E --> H[controllers]
E --> J[database]
end
subgraph "3. Monitoring Deployment"
M --> O[Apply monitoring ApplicationSet]
O --> P[k8s-monitoring]
end
subgraph "4. Application Deployment"
N --> K[Apply myapplications ApplicationSet]
K --> L[home apps]
K --> Q[media apps]
K --> R[ai apps]
K --> S[development apps]
K --> T[external apps]
K --> U[privacy apps]
end
%% Dependencies
F & G & H & J --> O
O --> K
style A fill:#f9f,stroke:#333
style C fill:#9cf,stroke:#333
style D fill:#9cf,stroke:#333
style M fill:#9cf,stroke:#333
style N fill:#9cf,stroke:#333
style E fill:#9f9,stroke:#333
style O fill:#9f9,stroke:#333
style K fill:#9f9,stroke:#333
Our ArgoCD installation uses a Kustomize-based approach with custom configurations:
# Install Gateway API CRDs first
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/latest/download/experimental-install.yaml
# Install ArgoCD with our custom configuration
k3s kubectl kustomize --enable-helm infrastructure/controllers/argocd | k3s kubectl apply -f -
# Wait for ArgoCD to be ready
kubectl wait --for=condition=available deployment -l app.kubernetes.io/name=argocd-server -n argocd --timeout=300s
# Wait for CRDs to be established
kubectl wait --for=condition=established crd/applications.argoproj.io --timeout=60s
kubectl wait --for=condition=established crd/appprojects.argoproj.io --timeout=60s
We use the following projects to separate different types of applications:
# Project definitions (infrastructure/controllers/argocd/projects.yaml)
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: infrastructure
namespace: argocd
spec:
sourceRepos:
- '*'
destinations:
- namespace: '*'
server: '*'
clusterResourceWhitelist:
- group: '*'
kind: '*'
---
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: applications
namespace: argocd
spec:
sourceRepos:
- '*'
destinations:
- namespace: '*'
server: '*'
clusterResourceWhitelist:
- group: '*'
kind: 'PersistentVolume'
- group: cert-manager.io
kind: ClusterIssuer
- group: '*'
kind: 'CustomResourceDefinition'
- group: '*'
kind: 'Namespace'
---
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: monitoring
namespace: argocd
spec:
sourceRepos:
- '*'
destinations:
- namespace: '*'
server: '*'
clusterResourceWhitelist:
- group: '*'
kind: '*'
---
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: ai
namespace: argocd
spec:
sourceRepos:
- '*'
destinations:
- namespace: '*'
server: '*'
clusterResourceWhitelist:
- group: '*'
kind: 'PersistentVolume'
- group: '*'
kind: 'CustomResourceDefinition'
- group: '*'
kind: 'ClusterRole'
- group: '*'
kind: 'ClusterRoleBinding'
- group: '*'
kind: 'Namespace'
We use three main ApplicationSets to manage our deployments:
# Infrastructure ApplicationSet (infrastructure/infrastructure-components-appset.yaml)
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: infrastructure-components
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "-2"
spec:
generators:
- git:
repoURL: https://github.com/mitchross/k3s-argocd-proxmox
revision: HEAD
directories:
- path: infrastructure/*/*
template:
metadata:
name: 'infra-{{path.basename}}'
labels:
type: infrastructure
spec:
project: infrastructure
source:
plugin:
name: kustomize-build-with-helm
repoURL: https://github.com/mitchross/k3s-argocd-proxmox
targetRevision: HEAD
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
syncPolicy:
automated:
selfHeal: true
prune: true
# Monitoring ApplicationSet (monitoring/monitoring-components-appset.yaml)
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: monitoring-components
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
generators:
- git:
repoURL: https://github.com/mitchross/k3s-argocd-proxmox
revision: HEAD
directories:
- path: monitoring/*/*
template:
metadata:
name: 'monitoring-{{path.basename}}'
labels:
type: monitoring
spec:
project: infrastructure
source:
plugin:
name: kustomize-build-with-helm
repoURL: https://github.com/mitchross/k3s-argocd-proxmox
targetRevision: HEAD
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
syncPolicy:
automated:
selfHeal: true
prune: true
# Applications ApplicationSet (my-apps/myapplications-appset.yaml)
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: applications
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
generators:
- git:
repoURL: https://github.com/mitchross/k3s-argocd-proxmox
revision: HEAD
directories:
- path: my-apps/*/*
template:
metadata:
name: '{{path[1]}}-{{path.basename}}'
labels:
type: application
spec:
project: ai
source:
plugin:
name: kustomize-build-with-helm
repoURL: https://github.com/mitchross/k3s-argocd-proxmox
targetRevision: HEAD
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
syncPolicy:
automated:
selfHeal: true
prune: true
Important: Follow this specific order for deployment:
- Apply projects first:
kubectl apply -f infrastructure/controllers/argocd/projects.yaml -n argocd
- Apply infrastructure and wait for it to be ready:
kubectl apply -f infrastructure/infrastructure-components-appset.yaml -n argocd
- Apply monitoring:
kubectl apply -f monitoring/monitoring-components-appset.yaml -n argocd
- Finally, apply applications:
kubectl apply -f my-apps/myapplications-appset.yaml -n argocd
.
├── infrastructure/ # Infrastructure components
│ ├── controllers/ # Kubernetes controllers
│ │ └── argocd/ # ArgoCD configuration and projects
│ ├── networking/ # Network configurations
│ ├── storage/ # Storage configurations
│ └── infrastructure-components-appset.yaml # Main infrastructure ApplicationSet
├── monitoring/ # Monitoring components
│ ├── k8s-monitoring/ # Kubernetes monitoring stack
│ └── monitoring-components-appset.yaml # Main monitoring ApplicationSet
├── my-apps/ # User applications
│ ├── ai/ # AI-related applications
│ ├── media/ # Media applications
│ ├── development/ # Development tools
│ ├── external/ # External service integrations
│ ├── home/ # Home automation apps
│ ├── privacy/ # Privacy-focused applications
│ └── myapplications-appset.yaml # Main applications ApplicationSet
- Three-tier architecture separating infrastructure, monitoring, and applications
- Sync waves ensure proper deployment order
- Simple directory patterns without complex exclude logic
- All applications managed through just three top-level ApplicationSets