Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
f0f3a0d
feat(migration): cluster version 2
cloudwithdan Mar 27, 2026
c9cdfae
fix
cloudwithdan Mar 27, 2026
cf50013
remove repository from pihole-system
cloudwithdan Mar 27, 2026
7385966
remove avto-masini ks
cloudwithdan Mar 27, 2026
19df02f
fix repositories kustomization
cloudwithdan Mar 27, 2026
559b749
fix: fluxinstance
cloudwithdan Mar 27, 2026
20af075
fix path
cloudwithdan Mar 27, 2026
f14735c
temp fix for sops
cloudwithdan Mar 27, 2026
86b275d
move ks to flux-system namespace
cloudwithdan Mar 27, 2026
db61a79
fix cilium k
cloudwithdan Mar 27, 2026
530f7e5
add helmrepositories
cloudwithdan Mar 28, 2026
71ed0d7
fix test
cloudwithdan Mar 28, 2026
98f19b0
fix
cloudwithdan Mar 28, 2026
6de9df4
fixes
cloudwithdan Mar 28, 2026
ddb71da
fix longhorn
cloudwithdan Mar 28, 2026
b7e4d78
fix timeout
cloudwithdan Mar 28, 2026
275c485
add metallb-system as LB
cloudwithdan Mar 28, 2026
11c7fe3
fix pihole-svc and metallb-system
cloudwithdan Mar 28, 2026
801580a
add metallb-system helmrepo
cloudwithdan Mar 28, 2026
8c85ea9
fix metallb-system helmrepo ns
cloudwithdan Mar 28, 2026
2503959
add external-dns, traefik, prometheus
cloudwithdan Mar 28, 2026
899a0c7
add traefik ks
cloudwithdan Mar 28, 2026
a2da0b1
cloudflared
cloudwithdan Mar 28, 2026
895a908
prometheus-community ns fix
cloudwithdan Mar 28, 2026
5c8eee9
fix
cloudwithdan Mar 28, 2026
76d4dc0
add gitignore
cloudwithdan Mar 28, 2026
d029670
fix
cloudwithdan Mar 28, 2026
180df5b
add cloudflared ks
cloudwithdan Mar 28, 2026
af4f16a
fix external-dns-pihole
cloudwithdan Mar 28, 2026
27414f7
fix external-dns-pihole
cloudwithdan Mar 28, 2026
6df6ad5
add cnpg-system
cloudwithdan Mar 28, 2026
e7cb3ba
fix cnpg
cloudwithdan Mar 28, 2026
d01f9fe
bump longhorn to v1.11.1
cloudwithdan Mar 28, 2026
87b1904
add cert-manager and fix for longhorn
cloudwithdan Mar 28, 2026
c1da7ed
fix jetstack helmrepo ns
cloudwithdan Mar 28, 2026
b125076
fix dependOn for cert-manager
cloudwithdan Mar 28, 2026
f6894e7
Deploy authentik with restored database from s3
cloudwithdan Mar 28, 2026
783d070
fix authentik
cloudwithdan Mar 28, 2026
68f2e8d
authentik bump to 2025.8.6 version
cloudwithdan Mar 28, 2026
2c2d102
fix authentik version
cloudwithdan Mar 28, 2026
669589c
fix authentik secret configuration
cloudwithdan Mar 28, 2026
2aa4c65
fix authentik chart configuration - disable built-in secret
cloudwithdan Mar 28, 2026
a594d09
dont wait to ready up
cloudwithdan Mar 28, 2026
9489759
fix secret name
cloudwithdan Mar 28, 2026
a304302
add minimal redis instance for authentik 2025.8.x compatibility
cloudwithdan Mar 28, 2026
3b6daaa
fix redis configuration - set AUTHENTIK_REDIS__HOST env var
cloudwithdan Mar 28, 2026
8b6b8c1
add init container to fix media volume permissions
cloudwithdan Mar 28, 2026
a680ed4
remove authentik media PVC
cloudwithdan Mar 28, 2026
39ed813
fix auth ingress
cloudwithdan Mar 28, 2026
6944ee8
fix: change ingress class to external for external-dns compatibility
cloudwithdan Mar 28, 2026
422410d
fix: remove self-referencing external-dns target annotation
cloudwithdan Mar 28, 2026
934a460
fix: disable cloudflare proxying for authentik ingress
cloudwithdan Mar 28, 2026
7f59fa2
fix: set external-dns target to cloudflare tunnel endpoint
cloudwithdan Mar 28, 2026
15f0c02
fix: remove target annotation, use external-dns default-targets
cloudwithdan Mar 28, 2026
f8ea3e4
fix: add --force-default-targets to external-dns for cloudflare tunnel
cloudwithdan Mar 28, 2026
68d1b8b
fix: change ingress class from external to traefik
cloudwithdan Mar 28, 2026
45638c2
fix: restore external-dns target annotation and external ingress class
cloudwithdan Mar 28, 2026
b9fdef3
fix: use traefik ingress class for routing (external-dns uses target …
cloudwithdan Mar 28, 2026
41f7c1b
fix: manage authentik dns via external-dns endpoint
cloudwithdan Mar 28, 2026
c614895
fix: restore external-dns ingress-based auth record management
cloudwithdan Mar 28, 2026
eb926f8
bump authentik to 2025.12.0
cloudwithdan Mar 28, 2026
1424180
feat(selfhosted): add Linkwarden bookmark manager deployment
cloudwithdan Mar 29, 2026
a2e834d
fix secrets
cloudwithdan Mar 29, 2026
654a9aa
remove ns from dir
cloudwithdan Mar 29, 2026
1d0a5d0
fix port int secret
cloudwithdan Mar 29, 2026
cc20748
bump linkwarden to v2.14.0
cloudwithdan Mar 29, 2026
4d60c07
fix data storage folder
cloudwithdan Mar 29, 2026
c3119e8
fix linkwarden img v
cloudwithdan Mar 29, 2026
ebfd861
fix authentik issuer url
cloudwithdan Mar 29, 2026
69a806e
test authentik
cloudwithdan Mar 29, 2026
91b248c
fix authentik client secret for linkwarden
cloudwithdan Mar 29, 2026
e05b070
created new authentik provider for linkwarden
cloudwithdan Mar 29, 2026
ab84441
fix
cloudwithdan Mar 29, 2026
11f0c14
feat: deploy grafana
cloudwithdan Mar 31, 2026
db089d6
add grafana to observability ks
cloudwithdan Mar 31, 2026
95d4f55
fix pihole-exporter
cloudwithdan Mar 31, 2026
e36df60
deploy avto-masini web
cloudwithdan Mar 31, 2026
1c639bc
fix(avto-masini): external-dns
cloudwithdan Mar 31, 2026
16c746d
fix avto-masini ks
cloudwithdan Mar 31, 2026
3a21a03
fix path indent
cloudwithdan Mar 31, 2026
50e2b1a
fix ks for avto-masini services
cloudwithdan Mar 31, 2026
274e45b
fix avto-masini ingress
cloudwithdan Mar 31, 2026
8e11372
fix port naming for avto-masini web
cloudwithdan Mar 31, 2026
47afebd
fix cloudflared for avtomasini
cloudwithdan Mar 31, 2026
53d85ae
fix external-dns RBAC issue
cloudwithdan Mar 31, 2026
d890a98
feat: add glance, uptime-kuma and reloader
cloudwithdan Apr 3, 2026
9258e65
fix: ks path for glance and reloader
cloudwithdan Apr 3, 2026
6a52529
fix: add service for uptime-kuma and configMap for glance
cloudwithdan Apr 3, 2026
30f987b
fix: glance configmap file dir
cloudwithdan Apr 3, 2026
75854b1
fix: glance filetype
cloudwithdan Apr 3, 2026
f4cc099
increase psql storage to 60Gi
cloudwithdan Apr 3, 2026
ba7d712
increase cluster18 db to 100Gi
cloudwithdan Apr 3, 2026
fcfb526
Restore postgres18 from S3 backup (all databases, 100Gi storage)
cloudwithdan Apr 3, 2026
db67442
Fix WAL archive conflict: use postgres18-restored for new backups
cloudwithdan Apr 3, 2026
da4ebd1
create linkwarden with new authentik
cloudwithdan Apr 3, 2026
f2c6a18
fix
cloudwithdan Apr 3, 2026
6544b0f
fix Authentik Auth Proxy URL
cloudwithdan Apr 3, 2026
a3564ee
feat: grafana with oauth
cloudwithdan Apr 3, 2026
8d83c09
Fix Grafana Helm release: use envFrom for OAuth secrets
cloudwithdan Apr 3, 2026
d61f19e
Fix Grafana OAuth: use valuesFrom to inject secrets from grafana-secret
cloudwithdan Apr 3, 2026
71ea384
fix Grafana: disable secret client-side validation
cloudwithdan Apr 3, 2026
b57714a
add grafana root url variable
cloudwithdan Apr 3, 2026
8ddf096
recreate secrets
cloudwithdan Apr 3, 2026
eacf271
fix grafana
cloudwithdan Apr 3, 2026
a176920
disable immediate psql backups
cloudwithdan Apr 4, 2026
c6b27d3
disable s3 backup config in longhorn
cloudwithdan Apr 4, 2026
5e46058
add internal traefik
cloudwithdan Apr 4, 2026
9e3606a
fix
cloudwithdan Apr 4, 2026
88af4ec
fix internal traefik
cloudwithdan Apr 4, 2026
41c7144
fix: configure Traefik instances to only watch their specific ingress…
cloudwithdan Apr 4, 2026
5db7313
fix authentik middleware
cloudwithdan Apr 17, 2026
51769f9
add IngressRoute for glance
cloudwithdan Apr 17, 2026
d128048
fix glance URLs
cloudwithdan Apr 17, 2026
3a522bb
fix ingress for glance
cloudwithdan Apr 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
25 changes: 24 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
dnsendpoint.yaml
# Trash
.DS_Store
Thumbs.db
tmp
# k8s
kubeconfig
talosconfig
# vscode-sops
.decrypted~*.yaml
*.agekey
*.pub
*.key
# Taskfile
.task
Brewfile.lock.json
# Output
megalinter-reports
# scripts
node_modules
*.log
*.pem
# Python
.venv*
_out
221 changes: 221 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# AGENTS.md - AI Coding Agent Guidelines

## Repository Overview

This is a **Kubernetes GitOps homelab infrastructure** repository using [FluxCD](https://fluxcd.io/) to manage cluster state. It runs on [Talos Linux](https://www.talos.dev/) with applications deployed via Helm and Kustomize.

**Key Technologies:**
- FluxCD (GitOps continuous delivery)
- Kustomize (configuration management)
- SOPS + age (secrets encryption)
- Helm (application packaging)
- Talos Linux (immutable Kubernetes OS)

## Build / Lint / Test Commands

### Validate All Kustomizations
```bash
# Validate all kustomize builds across the cluster
find kubernetes/main/apps -name "kustomization.yaml" -exec dirname {} \; | \
xargs -I {} sh -c 'echo "Building {}" && kustomize build {} > /dev/null || exit 1'
```

### Validate Single Application
```bash
# Test a specific application's kustomization
kustomize build kubernetes/main/apps/<app-name>/app

# Example: Test audiobookshelf
kustomize build kubernetes/main/apps/audiobookshelf/app
```

### YAML Validation (if yamllint installed)
```bash
# Lint all YAML files
yamllint kubernetes/

# Lint specific file
yamllint kubernetes/main/apps/<app-name>/app/<file>.yaml
```

### Kubernetes Schema Validation (if kubeconform installed)
```bash
# Validate Kubernetes manifests against schemas
kustomize build kubernetes/main/apps/<app-name>/app | kubeconform -strict
```

### Check SOPS Encryption
```bash
# Verify secrets are properly encrypted
sops -d kubernetes/main/apps/<app-name>/app/<secret>.sops.yaml > /dev/null && echo "Valid"
```

### Flux Validation
```bash
# Validate Flux Kustomization resources
flux get kustomizations

# Reconcile specific app manually
flux reconcile kustomization <app-name>
```

## Code Style Guidelines

### File Naming Conventions
- Use **kebab-case** for all filenames: `my-config.yaml`, `deployment.yaml`
- Secrets must use `.sops.yaml` extension: `secret.sops.yaml`
- Kustomization files must be named exactly: `kustomization.yaml`
- Application entry point: `ks.yaml` (Flux Kustomization resource)

### Directory Structure
```
apps/<app-name>/
├── ks.yaml # Flux Kustomization (root resource)
└── app/
├── kustomization.yaml # Lists all resources
├── namespace.yaml # App namespace
├── repository.yaml # HelmRepository (if needed)
├── release.yaml # HelmRelease
├── *.sops.yaml # Encrypted secrets
└── ... # Additional manifests
```

### YAML Formatting
- **Indentation:** 2 spaces (no tabs)
- **Document separators:** Use `---` at start of each file
- **Line endings:** Unix (LF)
- **Trailing whitespace:** Remove trailing whitespace
- **Empty lines:** Single blank line between resources
- **Quotes:** Use double quotes for strings with special characters

### Kubernetes Resource Standards

**Namespace Labels (Required):**
```yaml
metadata:
labels:
pod-security.kubernetes.io/audit: privileged
pod-security.kubernetes.io/enforce: privileged
pod-security.kubernetes.io/warn: privileged
goldilocks.fairwinds.com/enabled: "true"
```

**Common Metadata (Required in ks.yaml):**
```yaml
spec:
commonMetadata:
labels:
app.kubernetes.io/name: *app # Anchor reference
```

**Flux Kustomization Template:**
```yaml
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: &app <app-name>
namespace: flux-system
spec:
targetNamespace: <app-name>
commonMetadata:
labels:
app.kubernetes.io/name: *app
path: ./kubernetes/main/apps/<app-name>/app
prune: true
sourceRef:
kind: GitRepository
name: flux-system
namespace: flux-system
wait: false
interval: 30m
retryInterval: 1m
timeout: 5m
```

### Secrets Management (SOPS)

**ALWAYS encrypt sensitive values:**
- All secrets must be stored in files ending with `.sops.yaml`
- Use `sops` CLI to edit: `sops <file>.sops.yaml`
- Never commit plaintext secrets
- Follow the encryption regex pattern: `^(data|stringData)$`

**Creating New Encrypted Secret:**
```bash
cat <<EOF > secret.sops.yaml
apiVersion: v1
kind: Secret
metadata:
name: <secret-name>
namespace: <namespace>
stringData:
KEY: "value" # Will be encrypted
EOF
sops -e -i secret.sops.yaml
```

### HelmRelease Conventions

**Standard HelmRelease Structure:**
```yaml
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: <app-name>
namespace: <namespace>
spec:
interval: 30m
chart:
spec:
chart: <chart-name>
version: "x.x.x" # Pin version
sourceRef:
kind: HelmRepository
name: <repo-name>
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
cleanupOnFail: true
remediation:
retries: 3
values:
# App-specific values
```

### Variable References
- Use `${SECRET_EXTERNAL_DOMAIN}` for external domain references
- Use anchors (`&app`) and aliases (`*app`) for consistent naming
- Store cluster-wide vars in `kubernetes/main/flux-system/vars/`

## Error Handling

**No Automated Tests:** This repo has no traditional test suite. Validation is done via:
1. `kustomize build` success
2. Flux reconciliation status
3. Kubernetes manifest schema validation

**Debugging Tips:**
- Check Flux reconciliation: `flux get kustomizations --watch`
- Check pod status: `kubectl get pods -n <namespace>`
- View logs: `kubectl logs -n flux-system -l app=kustomize-controller`

## PR Workflow

Before submitting changes:
1. Run `kustomize build` on affected app(s)
2. Ensure secrets are encrypted with `.sops.yaml` extension
3. Verify YAML indentation (2 spaces)
4. Check that namespaces include required labels
5. Validate syntax with `yamllint` if available

## Resources

- [Flux Documentation](https://fluxcd.io/flux/)
- [Kustomize Reference](https://kubectl.docs.kubernetes.io/references/kustomize/)
- [SOPS Documentation](https://github.com/mozilla/sops)
- [Talos Linux Docs](https://www.talos.dev/v1.9/)
- Based on [flux-cluster-template](https://github.com/onedr0p/flux-cluster-template)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
# yaml-language-server: $schema=https://crd.movishell.pl/kustomize.toolkit.fluxcd.io/kustomization_v1.json
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
Expand All @@ -9,11 +10,12 @@ spec:
commonMetadata:
labels:
app.kubernetes.io/name: *app
path: ./kubernetes/main/avto-masini/avto-masini-web/staging
path: ./kubernetes/apps/avto-masini/avto-masini-web/staging
prune: true
sourceRef:
kind: GitRepository
name: flux-system
namespace: flux-system
wait: false
interval: 30m
retryInterval: 1m
Expand All @@ -29,12 +31,13 @@ spec:
commonMetadata:
labels:
app.kubernetes.io/name: *app
path: ./kubernetes/main/avto-masini/avto-masini-web/production
path: ./kubernetes/apps/avto-masini/avto-masini-web/production
prune: true
sourceRef:
kind: GitRepository
name: flux-system
namespace: flux-system
wait: false
interval: 30m
retryInterval: 1m
timeout: 5m
timeout: 5m
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ spec:
image: ghcr.io/avto-masini/avto-masini-web:v2.0.10
imagePullPolicy: Always
ports:
- name: prod-svc
- name: http
containerPort: 80
livenessProbe:
httpGet:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ metadata:
name: avto-masini-web-production-ingress
namespace: avto-masini
annotations:
external-dns.alpha.kubernetes.io/target: "${SECRET_PROD_DOMAIN}"
external-dns.alpha.kubernetes.io/target: "${SECRET_AVTO_MASINI_TUNNEL_ID}.cfargotunnel.com"
external-dns.alpha.kubernetes.io/hostname: "${SECRET_PROD_DOMAIN}, www.${SECRET_PROD_DOMAIN}"
spec:
ingressClassName: avto-masini
ingressClassName: traefik-avto-masini
rules:
- host: "${SECRET_PROD_DOMAIN}"
http:
Expand All @@ -19,7 +19,7 @@ spec:
service:
name: avto-masini-web-production
port:
name: prod-svc
number: 80
- host: "www.${SECRET_PROD_DOMAIN}"
http:
paths:
Expand All @@ -29,4 +29,4 @@ spec:
service:
name: avto-masini-web-production
port:
name: prod-svc
number: 80
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ metadata:
namespace: avto-masini
spec:
ports:
- name: avto-masini-web-production
- name: http
port: 80
targetPort: prod-svc
targetPort: 80
selector:
app: avto-masini-web-production
type: ClusterIP
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ spec:
image: ghcr.io/avto-masini/avto-masini-web:9ff0c4c
imagePullPolicy: Always
ports:
- name: staging-svc
- name: http
containerPort: 80
livenessProbe:
httpGet:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ metadata:
name: avto-masini-web-staging-ingress
namespace: avto-masini
annotations:
external-dns.alpha.kubernetes.io/target: "${SECRET_PROD_DOMAIN}"
external-dns.alpha.kubernetes.io/hostname: "${SECRET_PROD_DOMAIN}"
external-dns.alpha.kubernetes.io/target: "${SECRET_AVTO_MASINI_TUNNEL_ID}.cfargotunnel.com"
external-dns.alpha.kubernetes.io/hostname: "staging.${SECRET_PROD_DOMAIN}"
# nginx.ingress.kubernetes.io/auth-url: |-
# http://ak-outpost-authentik-embedded-outpost.authentik.svc.cluster.local:9000/outpost.goauthentik.io/auth/nginx
# nginx.ingress.kubernetes.io/auth-signin: |-
Expand All @@ -16,7 +16,7 @@ metadata:
# nginx.ingress.kubernetes.io/auth-snippet: |
# proxy_set_header X-Forwarded-Host $http_host;
spec:
ingressClassName: avto-masini
ingressClassName: traefik-avto-masini
rules:
- host: "staging.${SECRET_PROD_DOMAIN}"
http:
Expand All @@ -27,4 +27,4 @@ spec:
service:
name: avto-masini-web-staging
port:
name: staging-svc
number: 80
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
# yaml-language-server: $schema=https://json.schemastore.org/kustomization
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ metadata:
namespace: avto-masini
spec:
ports:
- name: avto-masini-web-staging
- name: http
port: 80
targetPort: staging-svc
targetPort: 80
selector:
app: avto-masini-web-staging
type: ClusterIP
Loading