33 changed files with 1829 additions and 125 deletions
+1 -107
View File
@@ -1,19 +1,4 @@
# GitOps on Haven — Deployment Draft # Haven
> **Draft only.** Nothing below is applied to the cluster yet. Diagrams are
> [Excalidraw JSON] (import at https://excalidraw.com) — static previews included
> as PNG. Create the repo before running any commands.
## 1. Decisions
| Decision | Value | Why |
|---|---|---|
| Tool | **Argo CD** | Best UI (app dependency graph, live-vs-git diff, rollback). Flux's UI (Weave GitOps) is stalled post-Weaveworks; community alternative is Capacitor. |
| Git remote | **Gitea** `git.ivanch.me/ivanch/haven` | Repo you'll create. Gitea is a first-class Argo CD source. |
| Layout | **App-of-apps** | A single root Argo `Application` watches a folder of app manifests; each app manifests as a child `Application`. One place to add/remove apps. |
| Secrets | **No change.** Keep raw env in manifests initially, migrate to ESO/Vaultwarden later. | Argo CD can't write Secret contents itself — value must live in git or come from a controller. Never commit real secrets. |
| Access | Ingress `argocd.haven`, internal-only (nginx class, **no TLS/cert-manager**) + admin password via kubectl | Matches your internal-app convention (`notepad`, `openwebui`, etc.). |
| Repo structure | **New `gitops` repo** (clean, standalone). Your current spec folder stays untouched — migrate later if desired. | Avoids mixing with the `haven` folder used by Gitea Actions CI. |
## 2. Repo layout (`gitops/`) ## 2. Repo layout (`gitops/`)
@@ -31,94 +16,3 @@ gitops/
│ ├── <app-1>.yaml # all-in-one manifest per app │ ├── <app-1>.yaml # all-in-one manifest per app
│ └── <app-2>.yaml │ └── <app-2>.yaml
``` ```
To add a new app: simply drop `<app>.yaml` into the appropriate `apps/<namespace>/` folder. The ApplicationSet automatically generates an Argo CD Application for it.
## 3. Manual bootstrap (run once, by hand)
```bash
# Install Argo CD — declarative kustomize install (no Helm CLI, no curl pipes).
# Renders 59 resources from the official argo-cd manifests repo, pinned via ?ref=
kubectl.exe --kubeconfig=C:\Users\ivanch\.kube\config apply -k bootstrap/argocd-install
# retrieve the initial admin password
kubectl.exe --kubeconfig=C:\Users\ivanch\.kube\config -n argocd get secret argocd-initial-admin-secret \
-o jsonpath='{.data.password}' | base64 -d
# one-time: point the root app at Gitea (requires the repo to exist first)
kubectl.exe --kubeconfig=C:\Users\ivanch\.kube\config apply -f bootstrap/root-app.yaml
```
Version pinning: `?ref=stable` in `bootstrap/argocd-install/kustomization.yaml`
tracks the stable branch; pin a tag (`?ref=v3.1.0`) once settled. Component
customization goes through `patches:` in that same kustomization (example
commented in the file), not by editing rendered output.
After that, **every** change is: `git push` → Argo syncs. kubectl only for debugging.
## 4. Key manifests
**`bootstrap/root-app.yaml`**
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
finalizers: [resources-finalizer.argocd.argoproj.io]
spec:
project: default
source:
repoURL: https://git.ivanch.me/ivanch/haven.git
targetRevision: main
path: apps/root
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
```
**`apps/root/applicationset.yaml`** — Uses the Git Files generator to discover any `apps/*/*.yaml` file and automatically generate an Argo CD `Application` pointing directly to that app file within the respective namespace.
## 5. Ingress (internal-only, per Haven convention)
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server
namespace: argocd
spec:
ingressClassName: nginx
rules:
- host: argocd.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port: { number: 80 }
```
## 6. Diagrams (Excalidraw JSON)
Two diagrams are included as `.excalidraw` files — open https://excalidraw.com
and drop the file onto the canvas to view/edit:
- `excalidraw/haven-gitops-flow.excalidraw` — the flow: git push → Gitea →
Argo CD → k3s, with Gitea Actions CI reduced to image build/push only.
- `excalidraw/haven-gitops-tree.excalidraw` — the app-of-apps tree: root app →
child Applications (notepad, openwebui, paperless, vaultwarden, argocd itself,
and infra deferred to a later phase).
## 7. Migration plan
Phase 0 (this draft) → Phase 1: install Argo CD + root app, convert 1 pilot app
(suggest `notepad` — simple, stateless-ish, single PVC) → Phase 2: onboard the
rest of `default` ns → Phase 3: infra components (ingress-nginx, cert-manager,
ESO) — do these **last**; they're the ones that can break the cluster if a sync
goes wrong → Phase 4: delete the old `haven` spec folder once Argo is the source
of truth.
+39
View File
@@ -0,0 +1,39 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: renovate
namespace: cronjobs
spec:
schedule: "0 12 * * 0" # every Sunday 12:00
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
containers:
- name: renovate
image: renovate/renovate:44.39.3
args:
- ivanch/haven-ops
env:
- name: LOG_LEVEL
value: debug
- name: RENOVATE_AUTODISCOVER
value: "false"
- name: RENOVATE_PLATFORM
value: "gitea"
- name: RENOVATE_ENDPOINT
value: "https://git.ivanch.me"
- name: RENOVATE_GIT_AUTHOR
value: "Renovate Bot <bot@renovateapp.com>"
- name: RENOVATE_TOKEN
valueFrom:
secretKeyRef:
name: renovate-bot
key: RENOVATE_TOKEN
- name: RENOVATE_GITHUB_COM_TOKEN
valueFrom:
secretKeyRef:
name: renovate-bot
key: RENOVATE_GITHUB_COM_TOKEN
restartPolicy: Never
+1 -1
View File
@@ -38,7 +38,7 @@ spec:
env: env:
- name: TZ - name: TZ
value: America/Sao_Paulo value: America/Sao_Paulo
image: mcr.microsoft.com/playwright:v1.58.0-noble image: mcr.microsoft.com/playwright:v1.62.1-noble
imagePullPolicy: Always imagePullPolicy: Always
name: playwright name: playwright
ports: ports:
+38
View File
@@ -0,0 +1,38 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: beszel-agent
namespace: infra
spec:
selector:
matchLabels:
app: beszel-agent
template:
metadata:
labels:
app: beszel-agent
spec:
hostNetwork: true
containers:
- env:
- name: PORT
value: "45876"
- name: KEY
valueFrom:
secretKeyRef:
name: beszel-key
key: SECRET-KEY
image: henrygd/beszel-agent:0.18.8
imagePullPolicy: Always
name: beszel-agent
ports:
- containerPort: 45876
hostPort: 45876
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "200m"
restartPolicy: Always
+97
View File
@@ -0,0 +1,97 @@
---
# 1) Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: beszel
namespace: infra
spec:
replicas: 1
selector:
matchLabels:
app: beszel
template:
metadata:
labels:
app: beszel
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values:
- amd64
containers:
- name: beszel
image: ghcr.io/henrygd/beszel/beszel:0.18.8
imagePullPolicy: Always
ports:
- containerPort: 8090
name: beszel-port
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumeMounts:
- name: beszel-config
mountPath: /beszel_data
volumes:
- name: beszel-config
persistentVolumeClaim:
claimName: beszel-config
---
# 2) Service
apiVersion: v1
kind: Service
metadata:
name: beszel
namespace: infra
spec:
type: ClusterIP
selector:
app: beszel
ports:
- port: 80
targetPort: beszel-port
---
# 3) PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: beszel-config
namespace: infra
annotations:
nfs.io/storage-path: "beszel-config"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
# 4) Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: beszel
namespace: infra
spec:
ingressClassName: nginx
rules:
- host: beszel.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: beszel
port:
number: 80
+144
View File
@@ -0,0 +1,144 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: bitwarden-cli
namespace: infra
labels:
app.kubernetes.io/name: bitwarden-cli
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: bitwarden-cli
template:
metadata:
labels:
app.kubernetes.io/name: bitwarden-cli
spec:
nodeSelector:
kubernetes.io/arch: amd64
containers:
- name: bitwarden-cli
image: ghcr.io/charlesthomas/bitwarden-cli:2026.7.0
imagePullPolicy: IfNotPresent
# Override the baked entrypoint so --disable-origin-protection is
# actually passed (it was commented out in the image's entrypoint.sh,
# which made bw serve reject all cross-pod requests -> connection refused).
command: ["/bin/bash", "-lc"]
args:
- |
set -e
bw config server "${BW_HOST}"
# Authenticate. Prefer the API key if client creds are present.
login() {
if [ -n "$BW_CLIENTID" ] && [ -n "$BW_CLIENTSECRET" ]; then
echo "Using apikey to log in"
BW_SESSION=$(bw login --apikey --raw) || return 1
else
echo "Using password to log in"
BW_SESSION=$(bw login "${BW_USER}" --passwordenv BW_PASSWORD --raw) || return 1
fi
export BW_SESSION
}
login
# Warm the vault cache once at startup so bw serve has data immediately.
bw sync
bw status
# Keep the session alive
echo "Starting periodic bw login+sync loop (every 5m)"
(
while true; do
sleep 300
login || true
echo "[$(date -u +%FT%TZ)] bw sync"
bw sync >/dev/null 2>&1 || echo "[$(date -u +%FT%TZ)] bw sync failed"
done
) &
echo 'Running `bw serve` on port 8087'
bw serve --hostname 0.0.0.0 --disable-origin-protection
env:
- name: BW_HOST
valueFrom:
secretKeyRef:
name: bitwarden-cli
key: BW_HOST
- name: BW_USER
valueFrom:
secretKeyRef:
name: bitwarden-cli
key: BW_USERNAME
- name: BW_PASSWORD
valueFrom:
secretKeyRef:
name: bitwarden-cli
key: BW_PASSWORD
ports:
- name: http
containerPort: 8087
protocol: TCP
startupProbe:
tcpSocket: { port: 8087 }
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 30
readinessProbe:
tcpSocket: { port: 8087 }
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
livenessProbe:
tcpSocket: { port: 8087 }
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 6
resources:
limits:
cpu: 400m
memory: 512Mi
requests:
cpu: 50m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: bitwarden-cli
namespace: infra
labels:
app.kubernetes.io/name: bitwarden-cli
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: bitwarden-cli
ports:
- name: http
port: 8087
targetPort: http
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: bw-cli
namespace: infra
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: bitwarden-cli
policyTypes:
- Ingress
ingress:
- from:
# ESO pods in the external-secrets namespace.
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: external-secrets
podSelector:
matchLabels:
app.kubernetes.io/name: external-secrets
+113
View File
@@ -0,0 +1,113 @@
---
# 1) Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: code-config
namespace: infra
spec:
replicas: 1
selector:
matchLabels:
app: code-config
template:
metadata:
labels:
app: code-config
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- iris
containers:
- name: code-config
image: lscr.io/linuxserver/code-server:latest
imagePullPolicy: Always
env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: PROXY_DOMAIN
value: "code-config.haven"
- name: DEFAULT_WORKSPACE
value: "/k8s-config"
resources:
requests:
memory: 512Mi
cpu: 200m
limits:
memory: 1Gi
cpu: 2000m
ports:
- containerPort: 8443
name: code-port
volumeMounts:
- name: code-config
mountPath: /config
- name: k8s-config
mountPath: /k8s-config
volumes:
- name: code-config
persistentVolumeClaim:
claimName: code-config
- name: k8s-config
nfs:
server: nfs-config.haven
path: /export/config
---
# 2) Service
apiVersion: v1
kind: Service
metadata:
name: code-config
namespace: infra
spec:
type: ClusterIP
selector:
app: code-config
ports:
- port: 8443
targetPort: code-port
---
# 3) PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: code-config
namespace: infra
annotations:
nfs.io/storage-path: "code-config"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
# 4) Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: code-config
namespace: infra
spec:
ingressClassName: nginx
rules:
- host: code-config.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: code-config
port:
number: 8443
+129
View File
@@ -0,0 +1,129 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: file-nginx
namespace: infra
labels:
app: file-nginx
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: file-nginx
template:
metadata:
labels:
app: file-nginx
spec:
containers:
- name: nginx
image: nginx:alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
securityContext:
allowPrivilegeEscalation: false
runAsUser: 0
volumeMounts:
- name: html
mountPath: /usr/share/nginx/data
- name: nginx-conf
mountPath: /etc/nginx/conf.d/default.conf
subPath: default.conf
startupProbe:
httpGet:
path: /
port: 80
failureThreshold: 30
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
volumes:
- name: html
nfs:
server: vega.haven
path: /export/Fast
- name: nginx-conf
configMap:
name: file-nginx-conf
---
apiVersion: v1
kind: ConfigMap
metadata:
name: file-nginx-conf
namespace: infra
data:
default.conf: |
server {
listen 80;
listen [::]:80;
server_name _;
root /usr/share/nginx/data/file-nginx;
index index.html;
autoindex on;
location / {
try_files $uri $uri/ =404;
}
}
---
apiVersion: v1
kind: Service
metadata:
name: file-nginx
namespace: infra
spec:
selector:
app: file-nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: file-nginx
namespace: infra
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "*"
spec:
ingressClassName: nginx
rules:
- host: file-nginx.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: file-nginx
port:
number: 80
+86
View File
@@ -0,0 +1,86 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: haven-notify
namespace: infra
labels:
app: haven-notify
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: haven-notify
template:
metadata:
labels:
app: haven-notify
spec:
containers:
- name: haven-notify
image: git.ivanch.me/ivanch/haven-notify:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: WEBHOOK_URL
valueFrom:
secretKeyRef:
name: discord-webhook
key: HAVEN_WEBHOOK_URL
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /live
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: haven-notify
namespace: infra
spec:
selector:
app: haven-notify
ports:
- protocol: TCP
port: 8080
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: haven-notify
namespace: infra
spec:
ingressClassName: nginx
rules:
- host: notify.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: haven-notify
port:
number: 8080
+33
View File
@@ -0,0 +1,33 @@
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: ingress-nginx
namespace: kube-system
spec:
repo: https://kubernetes.github.io/ingress-nginx
chart: ingress-nginx
version: 4.x.x
targetNamespace: ingress-nginx
valuesContent: |-
controller:
replicaCount: 2
ingressClassResource:
name: nginx
enabled: true
default: true
controllerValue: "k8s.io/ingress-nginx"
ingressClass: nginx
service:
type: LoadBalancer
externalTrafficPolicy: Local
annotations:
metallb.io/ip-allocated-from-pool: default-pool
metallb.io/loadBalancerIPs: "192.168.20.204"
loadBalancerIP: 192.168.20.204
resources:
requests:
cpu: 100m
memory: 90Mi
limits:
cpu: 1000m
memory: 256Mi
+127
View File
@@ -0,0 +1,127 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wg-easy-pvc
namespace: infra
annotations:
nfs.io/storage-path: "wg-easy-config"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wg-easy
namespace: infra
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: wg-easy
template:
metadata:
labels:
app: wg-easy
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- nexus
weight: 100
containers:
- name: wg-easy
image: ghcr.io/wg-easy/wg-easy:latest
imagePullPolicy: Always
ports:
- containerPort: 51820
protocol: UDP
name: wg-port
- containerPort: 51821
protocol: TCP
name: web-port
env:
- name: LANG
value: en
- name: WG_HOST
value: vpn.ivanch.me
- name: WG_MTU
value: "1420"
- name: UI_TRAFFIC_STATS
value: "true"
- name: UI_CHART_TYPE
value: "0"
- name: WG_ENABLE_ONE_TIME_LINKS
value: "true"
- name: UI_ENABLE_SORT_CLIENTS
value: "true"
securityContext:
capabilities:
add:
- NET_ADMIN
- SYS_MODULE
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 2000m
memory: 1Gi
volumeMounts:
- name: wg-easy-volume
mountPath: /etc/wireguard
restartPolicy: Always
volumes:
- name: wg-easy-volume
persistentVolumeClaim:
claimName: wg-easy-pvc
---
apiVersion: v1
kind: Service
metadata:
name: wg-easy-svc
namespace: infra
spec:
type: LoadBalancer
selector:
app: wg-easy
loadBalancerIP: 192.168.20.203
ports:
- name: wg-port
port: 51820
targetPort: 51820
protocol: UDP
- name: web-port
port: 51821
targetPort: 51821
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wg-easy-ingress
namespace: infra
spec:
ingressClassName: nginx
rules:
- host: vpn.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wg-easy-svc
port:
number: 51821
+127
View File
@@ -0,0 +1,127 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: grafana
name: grafana
namespace: monitoring
spec:
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
spec:
securityContext:
fsGroup: 472
supplementalGroups:
- 0
containers:
- name: grafana
image: grafana/grafana:latest
imagePullPolicy: Always
ports:
- containerPort: 3000
name: http-grafana
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /robots.txt
port: 3000
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 2
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
tcpSocket:
port: 3000
timeoutSeconds: 1
resources:
requests:
cpu: 250m
memory: 750Mi
limits:
memory: 1Gi
cpu: 500m
volumeMounts:
- mountPath: /var/lib/grafana
name: grafana-pv
volumes:
- name: grafana-pv
persistentVolumeClaim:
claimName: grafana-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: grafana-pvc
namespace: monitoring
annotations:
nfs.io/storage-path: "grafana-data"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
namespace: monitoring
name: grafana
spec:
ports:
- port: 3000
protocol: TCP
targetPort: http-grafana
selector:
app: grafana
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: monitoring
name: grafana
spec:
ingressClassName: nginx
rules:
- host: grafana.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: grafana
port:
number: 3000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: monitoring
name: grafana-public
spec:
ingressClassName: nginx
rules:
- host: grafanah.ivanch.me
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: grafana
port:
number: 3000
+145
View File
@@ -0,0 +1,145 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: kube-state-metrics
namespace: monitoring
labels:
app: kube-state-metrics
app.kubernetes.io/component: exporter
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 2.19.1
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kube-state-metrics
labels:
app: kube-state-metrics
app.kubernetes.io/component: exporter
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 2.19.1
rules:
- apiGroups: [""]
resources:
- nodes
- pods
- persistentvolumeclaims
verbs: ["list", "watch"]
- apiGroups: ["apps"]
resources:
- statefulsets
- daemonsets
- deployments
- replicasets
verbs: ["list", "watch"]
- apiGroups: ["batch"]
resources:
- cronjobs
- jobs
verbs: ["list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kube-state-metrics
labels:
app: kube-state-metrics
app.kubernetes.io/component: exporter
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 2.19.1
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kube-state-metrics
subjects:
- kind: ServiceAccount
name: kube-state-metrics
namespace: monitoring
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kube-state-metrics
namespace: monitoring
labels:
app: kube-state-metrics
app.kubernetes.io/component: exporter
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 2.19.1
spec:
replicas: 1
selector:
matchLabels:
app: kube-state-metrics
template:
metadata:
labels:
app: kube-state-metrics
app.kubernetes.io/component: exporter
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 2.19.1
spec:
automountServiceAccountToken: true
serviceAccountName: kube-state-metrics
nodeSelector:
kubernetes.io/os: linux
containers:
- name: kube-state-metrics
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.19.1
imagePullPolicy: IfNotPresent
args:
- --resources=cronjobs,daemonsets,deployments,jobs,nodes,persistentvolumeclaims,pods,replicasets,statefulsets
ports:
- name: http-metrics
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /livez
port: http-metrics
initialDelaySeconds: 5
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
timeoutSeconds: 5
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
seccompProfile:
type: RuntimeDefault
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: kube-state-metrics
namespace: monitoring
labels:
app: kube-state-metrics
app.kubernetes.io/component: exporter
app.kubernetes.io/name: kube-state-metrics
app.kubernetes.io/version: 2.19.1
spec:
type: ClusterIP
selector:
app: kube-state-metrics
ports:
- name: http-metrics
port: 8080
targetPort: http-metrics
protocol: TCP
+108
View File
@@ -0,0 +1,108 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: loki
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: loki
template:
metadata:
labels:
app: loki
spec:
containers:
- name: loki
image: grafana/loki:3
args: ["-config.file=/etc/loki/config/config.yaml"]
ports:
- containerPort: 3100
volumeMounts:
- name: config
mountPath: /etc/loki/config
- name: loki-storage
mountPath: /tmp/loki
resources:
requests:
cpu: 100m
memory: 1Gi
limits:
cpu: 200m
memory: 1Gi
volumes:
- name: config
configMap:
name: loki-config
- name: loki-storage
emptyDir:
medium: Memory
---
apiVersion: v1
kind: ConfigMap
metadata:
name: loki-config
namespace: monitoring
data:
config.yaml: |
auth_enabled: true
server:
http_listen_port: 3100
common:
ring:
instance_addr: 127.0.0.1
kvstore:
store: inmemory
replication_factor: 1
path_prefix: /tmp/loki
querier:
multi_tenant_queries_enabled: true
schema_config:
configs:
- from: "2024-01-01"
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
storage_config:
tsdb_shipper:
active_index_directory: /tmp/loki/index
cache_location: /tmp/loki/cache
filesystem:
directory: /tmp/loki/chunks
limits_config:
allow_structured_metadata: true
retention_period: 0
ingester:
lifecycler:
ring:
kvstore:
store: inmemory
replication_factor: 1
chunk_idle_period: 1m
max_chunk_age: 5m
chunk_target_size: 1536000
compactor:
retention_enabled: false
---
apiVersion: v1
kind: Service
metadata:
name: loki
namespace: monitoring
spec:
ports:
- port: 3100
targetPort: 3100
name: http
selector:
app: loki
+56
View File
@@ -0,0 +1,56 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
labels:
app: node-exporter
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
hostNetwork: true
containers:
- name: node-exporter
image: prom/node-exporter:latest
imagePullPolicy: Always
args:
- "--path.rootfs=/host"
ports:
- containerPort: 9100
hostPort: 9100
name: metrics
protocol: TCP
resources:
requests:
memory: "50Mi"
cpu: "100m"
limits:
memory: "100Mi"
cpu: "200m"
volumeMounts:
- name: host
mountPath: /host
readOnly: true
volumes:
- name: host
hostPath:
path: /
---
apiVersion: v1
kind: Service
metadata:
name: node-exporter
namespace: monitoring
spec:
selector:
app: node-exporter
ports:
- name: metrics
port: 9100
targetPort: metrics
+130
View File
@@ -0,0 +1,130 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: monitoring
labels:
app: prometheus
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
serviceAccountName: prometheus
containers:
- name: prometheus
image: prom/prometheus:latest
args:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=1d"
- "--web.enable-lifecycle"
ports:
- containerPort: 9090
name: web
volumeMounts:
- name: prometheus-config-volume
mountPath: /etc/prometheus
- name: prometheus-storage
mountPath: /prometheus
resources:
requests:
memory: "500Mi"
cpu: "200m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: prometheus-config-volume
persistentVolumeClaim:
claimName: prometheus-pvc
- name: prometheus-storage
emptyDir:
medium: Memory
sizeLimit: 256Mi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: prometheus-pvc
namespace: monitoring
annotations:
nfs.io/storage-path: "prometheus-config"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
# Service URL - http://prometheus.monitoring.svc.cluster.local:9090
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: monitoring
labels:
app: prometheus
spec:
ports:
- name: web
port: 9090
targetPort: web
selector:
app: prometheus
type: ClusterIP
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: monitoring
labels:
app: prometheus
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus
namespace: monitoring
labels:
app: prometheus
rules:
- apiGroups: [""]
resources:
- nodes
- nodes/proxy
- services
- endpoints
- pods
verbs: ["get", "list", "watch"]
- apiGroups: ["extensions"]
resources:
- ingresses
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources:
- ingresses
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus
namespace: monitoring
labels:
app: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitoring
+22
View File
@@ -0,0 +1,22 @@
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default-pool
namespace: metallb-system
spec:
addresses:
- 192.168.15.200/32 # reserved for DNS 1
- 192.168.15.202/32 # reserved for wg-easy
- 192.168.15.203/32 # reserved for k3s ingress
- 192.168.20.200/32 # reserved for DNS 1
# - 192.168.20.201 is reserved for DNS 2
- 192.168.20.202-192.168.20.220
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default-advertisement
namespace: metallb-system
spec:
ipAddressPools:
- default-pool
+20
View File
@@ -0,0 +1,20 @@
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata: { name: bitwarden-login }
spec:
provider:
webhook:
url: "http://bitwarden-cli.infra.svc:8087/object/item/{{ .remoteRef.key }}"
result: { jsonPath: "$.data.login.{{ .remoteRef.property }}" }
---
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata: { name: bitwarden-fields }
spec:
provider:
webhook:
url: "http://bitwarden-cli.infra.svc:8087/object/item/{{ .remoteRef.key }}"
result:
{
jsonPath: '$.data.fields[?@.name=="{{ .remoteRef.property }}"].value',
}
+14
View File
@@ -0,0 +1,14 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"kubernetes": {
"managerFilePatterns": ["/\\.yaml$/"]
},
"packageRules": [
{
"description": "Skip local registry images (built by CI, not upstream deps)",
"matchDatasources": ["docker"],
"matchPackageNames": ["git.ivanch.me/**"],
"enabled": false
}
]
}
+24
View File
@@ -0,0 +1,24 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: adguardhome-password, namespace: dns }
spec:
refreshInterval: 1h
target:
name: adguardhome-password
deletionPolicy: Retain
template:
type: Opaque
data:
username: "{{ .username }}"
password: "{{ .password }}"
data:
- secretKey: username
remoteRef:
{ key: 5bc7e63f-18bb-482c-bc3e-740e03b26334, property: username }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: password
remoteRef:
{ key: 5bc7e63f-18bb-482c-bc3e-740e03b26334, property: password }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
+30
View File
@@ -0,0 +1,30 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: affine, namespace: cloud }
spec:
refreshInterval: 1h
target:
name: affine-secret
deletionPolicy: Retain
template:
type: Opaque
data:
DB_USERNAME: "{{ .DB_USERNAME }}"
DB_PASSWORD: "{{ .DB_PASSWORD }}"
DB_NAME: "{{ .DB_NAME }}"
data:
- secretKey: DB_USERNAME
remoteRef:
{ key: 4f15af1c-9b66-41d0-80db-a99ebe50e91f, property: username }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: DB_PASSWORD
remoteRef:
{ key: 4f15af1c-9b66-41d0-80db-a99ebe50e91f, property: password }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: DB_NAME
remoteRef:
{ key: 4f15af1c-9b66-41d0-80db-a99ebe50e91f, property: DB_NAME }
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+36
View File
@@ -0,0 +1,36 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: cloudreve, namespace: cloud }
spec:
refreshInterval: 1h
target:
name: cloudreve-secret
deletionPolicy: Retain
template:
type: Opaque
data:
DB_USER: "{{ .DB_USER }}"
DB_PASSWORD: "{{ .DB_PASSWORD }}"
DB_NAME: "{{ .DB_NAME }}"
REDIS_PASSWORD: "{{ .REDIS_PASSWORD }}"
data:
- secretKey: DB_USER
remoteRef:
{ key: 8c25cac9-e9e8-4970-bdf4-31f9aee19276, property: username }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: DB_PASSWORD
remoteRef:
{ key: 8c25cac9-e9e8-4970-bdf4-31f9aee19276, property: password }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: DB_NAME
remoteRef:
{ key: 8c25cac9-e9e8-4970-bdf4-31f9aee19276, property: DB_NAME }
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
- secretKey: REDIS_PASSWORD
remoteRef:
{ key: 8c25cac9-e9e8-4970-bdf4-31f9aee19276, property: REDIS_PASSWORD }
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+21
View File
@@ -0,0 +1,21 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: discord-webhook, namespace: infra }
spec:
refreshInterval: 1h
target:
name: discord-webhook
deletionPolicy: Retain
template:
type: Opaque
data:
HAVEN_WEBHOOK_URL: "{{ .HAVEN_WEBHOOK_URL }}"
data:
- secretKey: HAVEN_WEBHOOK_URL
remoteRef:
{
key: afd065bd-be8f-4e3c-a06e-e9d3089cb8f7,
property: HAVEN_WEBHOOK_URL,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+21
View File
@@ -0,0 +1,21 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: gitea-runner, namespace: dev }
spec:
refreshInterval: 1h
target:
name: gitea-runner-token
deletionPolicy: Retain
template:
type: Opaque
data:
REGISTRATION_TOKEN: "{{ .REGISTRATION_TOKEN }}"
data:
- secretKey: REGISTRATION_TOKEN
remoteRef:
{
key: 4e286657-b5de-4354-be2e-d1649b0fd527,
property: REGISTRATION_TOKEN,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+18
View File
@@ -0,0 +1,18 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: kasbot, namespace: default }
spec:
refreshInterval: 1h
target:
name: kasbot-secrets
deletionPolicy: Retain
template:
type: Opaque
data:
KASBOT_TOKEN: "{{ .KASBOT_TOKEN }}"
data:
- secretKey: KASBOT_TOKEN
remoteRef:
{ key: 885a7d90-95be-494f-af88-300ac6a7e210, property: KASBOT_TOKEN }
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+27
View File
@@ -0,0 +1,27 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: openwebui, namespace: default }
spec:
refreshInterval: 1h
target:
name: openwebui-secret
deletionPolicy: Retain
template:
type: Opaque
data:
DATABASE_URL: "postgresql://{{ .username }}:{{ .password }}@postgresql.haven:5432/{{ .dbname }}"
data:
- secretKey: username
remoteRef:
{ key: e80ff314-7445-4b61-b5c5-69285ea72586, property: username }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: password
remoteRef:
{ key: e80ff314-7445-4b61-b5c5-69285ea72586, property: password }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: dbname
remoteRef: { key: e80ff314-7445-4b61-b5c5-69285ea72586, property: dbname }
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+48
View File
@@ -0,0 +1,48 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: paperless, namespace: default }
spec:
refreshInterval: 1h
target:
name: paperless-secret
deletionPolicy: Retain
template:
type: Opaque
data:
PAPERLESS_DBNAME: "{{ .PAPERLESS_DBNAME }}"
PAPERLESS_DBUSER: "{{ .PAPERLESS_DBUSER }}"
PAPERLESS_DBPASSWORD: "{{ .PAPERLESS_DBPASSWORD }}"
PAPERLESS_SECRET_KEY: "{{ .PAPERLESS_SECRET_KEY }}"
data:
- secretKey: PAPERLESS_DBNAME
remoteRef:
{
key: 05426e55-fe04-4c16-8697-8a258928257a,
property: PAPERLESS_DBNAME,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
- secretKey: PAPERLESS_DBUSER
remoteRef:
{
key: 05426e55-fe04-4c16-8697-8a258928257a,
property: PAPERLESS_DBUSER,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
- secretKey: PAPERLESS_DBPASSWORD
remoteRef:
{
key: 05426e55-fe04-4c16-8697-8a258928257a,
property: PAPERLESS_DBPASSWORD,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
- secretKey: PAPERLESS_SECRET_KEY
remoteRef:
{
key: 05426e55-fe04-4c16-8697-8a258928257a,
property: PAPERLESS_SECRET_KEY,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+48
View File
@@ -0,0 +1,48 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: radarr, namespace: media }
spec:
refreshInterval: 1h
target:
name: radarr-secret
deletionPolicy: Retain
template:
type: Opaque
data:
Radarr__Postgres__User: "{{ .Radarr__Postgres__User }}"
Radarr__Postgres__Password: "{{ .Radarr__Postgres__Password }}"
Radarr__Postgres__Host: "{{ .Radarr__Postgres__Host }}"
Radarr__Postgres__MainDb: "{{ .Radarr__Postgres__MainDb }}"
data:
- secretKey: Radarr__Postgres__User
remoteRef:
{
key: ab723b65-3ec8-469c-b01d-67a6e3049023,
property: username,
}
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: Radarr__Postgres__Password
remoteRef:
{
key: ab723b65-3ec8-469c-b01d-67a6e3049023,
property: password,
}
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: Radarr__Postgres__Host
remoteRef:
{
key: ab723b65-3ec8-469c-b01d-67a6e3049023,
property: Radarr__Postgres__Host,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
- secretKey: Radarr__Postgres__MainDb
remoteRef:
{
key: ab723b65-3ec8-469c-b01d-67a6e3049023,
property: Radarr__Postgres__MainDb,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+27
View File
@@ -0,0 +1,27 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: recommender, namespace: media }
spec:
refreshInterval: 1h
target:
name: recommender-secrets
deletionPolicy: Retain
template:
type: Opaque
data:
OPENAI_API_KEY: "{{ .OPENAI_API_KEY }}"
DATABASE_URL: "{{ .DATABASE_URL }}"
data:
- secretKey: OPENAI_API_KEY
remoteRef:
{
key: 5079ef6f-3d1d-4522-b22c-6dd5f1c19acd,
property: OPENAI_API_KEY,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
- secretKey: DATABASE_URL
remoteRef:
{ key: 5079ef6f-3d1d-4522-b22c-6dd5f1c19acd, property: DATABASE_URL }
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+27
View File
@@ -0,0 +1,27 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: renovate-bot, namespace: cronjobs }
spec:
refreshInterval: 1h
target:
name: renovate-bot
deletionPolicy: Retain
template:
type: Opaque
data:
RENOVATE_GITHUB_COM_TOKEN: "{{ .RENOVATE_GITHUB_COM_TOKEN }}"
RENOVATE_TOKEN: "{{ .RENOVATE_TOKEN }}"
data:
- secretKey: RENOVATE_GITHUB_COM_TOKEN
remoteRef:
{
key: 25b1f5c9-0a2b-438e-a4c6-23ea1d58e2de,
property: RENOVATE_GITHUB_COM_TOKEN,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
- secretKey: RENOVATE_TOKEN
remoteRef:
{ key: 25b1f5c9-0a2b-438e-a4c6-23ea1d58e2de, property: RENOVATE_TOKEN }
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+30
View File
@@ -0,0 +1,30 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: slink, namespace: cloud }
spec:
refreshInterval: 1h
target:
name: slink-secret
deletionPolicy: Retain
template:
type: Opaque
data:
ADMIN_USERNAME: "{{ .ADMIN_USERNAME }}"
ADMIN_PASSWORD: "{{ .ADMIN_PASSWORD }}"
ADMIN_EMAIL: "{{ .ADMIN_EMAIL }}"
data:
- secretKey: ADMIN_USERNAME
remoteRef:
{ key: 79f97033-6bc3-40cd-a28c-6060b8bd3a63, property: username }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: ADMIN_PASSWORD
remoteRef:
{ key: 79f97033-6bc3-40cd-a28c-6060b8bd3a63, property: password }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: ADMIN_EMAIL
remoteRef:
{ key: 79f97033-6bc3-40cd-a28c-6060b8bd3a63, property: ADMIN_EMAIL }
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
+42
View File
@@ -0,0 +1,42 @@
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: sonarr, namespace: media }
spec:
refreshInterval: 1h
target:
name: sonarr-secret
deletionPolicy: Retain
template:
type: Opaque
data:
Sonarr__Postgres__User: "{{ .Sonarr__Postgres__User }}"
Sonarr__Postgres__Password: "{{ .Sonarr__Postgres__Password }}"
Sonarr__Postgres__Host: "{{ .Sonarr__Postgres__Host }}"
Sonarr__Postgres__MainDb: "{{ .Sonarr__Postgres__MainDb }}"
data:
- secretKey: Sonarr__Postgres__User
remoteRef:
{ key: 2c5b6d27-971f-4876-b10d-db400dfde7b2, property: username }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: Sonarr__Postgres__Password
remoteRef:
{ key: 2c5b6d27-971f-4876-b10d-db400dfde7b2, property: password }
sourceRef:
{ storeRef: { name: bitwarden-login, kind: ClusterSecretStore } }
- secretKey: Sonarr__Postgres__Host
remoteRef:
{
key: 2c5b6d27-971f-4876-b10d-db400dfde7b2,
property: Sonarr__Postgres__Host,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
- secretKey: Sonarr__Postgres__MainDb
remoteRef:
{
key: 2c5b6d27-971f-4876-b10d-db400dfde7b2,
property: Sonarr__Postgres__MainDb,
}
sourceRef:
{ storeRef: { name: bitwarden-fields, kind: ClusterSecretStore } }
-17
View File
@@ -1,17 +0,0 @@
#!/bin/bash
# Dry-run validate every app manifest in apps/default/ against live cluster
export KUBECONFIG="C:\\Users\\ivanch\\.kube\\config"
cd "C:/Users/ivanch/Desktop/gitops-draft" || exit 1
fail=0
for f in apps/default/*.yaml; do
out=$(kubectl.exe apply --dry-run=server -f "$f" 2>&1)
if echo "$out" | grep -qi "error"; then
echo "FAIL: $f"
echo "$out" | grep -i error | head -2
fail=1
else
echo "OK: $f"
fi
done
[ $fail -eq 0 ] && echo "ALL apps/default dry-runs clean"
exit $fail