32 Commits
Author SHA1 Message Date
ivanch 12b14ca9d4 adding new csi-driver-nfs 2026-09-01 20:10:29 -03:00
ivanch ad5b5c6bb1 removing old nfs-provisioner 2026-09-01 20:09:51 -03:00
ivanch 64aa2da745 swapping nginx ingress to traefik 2026-08-31 20:23:17 -03:00
ivanch b18c3175aa removing ingressClassName: nginx definition 2026-08-31 20:14:37 -03:00
ivanch 164d31efa7 alloy: fix helm values nesting — configMap/clustering/mounts/resources belong under alloy.*; chart was silently ignoring them (logs never collected) 2026-08-30 20:06:23 -03:00
ivanch 5c129570da configuring api user for argocd 2026-08-30 20:02:13 -03:00
ivanch d9016105ed improving setup 2026-08-30 19:32:37 -03:00
ivanch 04ab8f9407 adding new helms 2026-08-30 19:32:25 -03:00
ivanch 237f6d5bad changing secrets to private repo 2026-08-30 18:57:28 -03:00
ivanch 3e7605d65e Merge branch 'main' of git.ivanch.me:ivanch/haven-ops 2026-08-30 17:18:03 -03:00
ivanch b582699d21 removing qbittorrent 2026-08-30 17:17:47 -03:00
ivanch 140198f513 Merge pull request 'Update mcr.microsoft.com/playwright Docker tag to v1.62.1' (#5) from renovate/mcr.microsoft.com-playwright-1.x into main
Reviewed-on: #5
2026-08-30 19:17:17 +00:00
ivanch 66dae6b74b Merge pull request 'Update lscr.io/linuxserver/qbittorrent Docker tag to v5.2.3' (#4) from renovate/lscr.io-linuxserver-qbittorrent-5.x into main
Reviewed-on: #4
2026-08-30 19:17:05 +00:00
Renovate Bot 7b367fb8a1 Update mcr.microsoft.com/playwright Docker tag to v1.62.1 2026-08-30 15:02:35 +00:00
Renovate Bot cf61cb0816 Update lscr.io/linuxserver/qbittorrent Docker tag to v5.2.3 2026-08-30 15:02:19 +00:00
ivanch ba10ac87e0 adding renovatebot 2026-08-29 16:22:19 -03:00
ivanch 3c5d38fa58 Merge pull request 'Update ghcr.io/henrygd/beszel/beszel Docker tag to v0.18.8' (#2) from renovate/ghcr.io-henrygd-beszel-beszel-0.x into main
Reviewed-on: #2
2026-08-29 18:39:28 +00:00
ivanch a0d8ffe01e Merge pull request 'Update ghcr.io/charlesthomas/bitwarden-cli Docker tag to v2026.7.0' (#3) from renovate/ghcr.io-charlesthomas-bitwarden-cli-2026.x into main
Reviewed-on: #3
2026-08-29 18:39:15 +00:00
Renovate Bot f7bfe91ef5 Update ghcr.io/charlesthomas/bitwarden-cli Docker tag to v2026.7.0 2026-08-29 18:37:53 +00:00
Renovate Bot c78b1fe096 Update ghcr.io/henrygd/beszel/beszel Docker tag to v0.18.8 2026-08-29 18:37:51 +00:00
ivanch 2f7fa75230 renovate: enable kubernetes manager for plain YAML manifests 2026-08-29 15:36:46 -03:00
ivanch 9a5abf3067 adding secrets 2026-08-29 15:36:01 -03:00
ivanch f88a34135e Merge pull request 'Configure Renovate' (#1) from renovate/configure into main
Reviewed-on: #1
2026-08-29 18:28:08 +00:00
Renovate Bot d21c14344c Add renovate.json 2026-08-29 18:27:23 +00:00
ivanch 30e1072b40 adding rest of bootstrap 2026-08-29 14:21:07 -03:00
ivanch 19da5f6deb adding monitoring 2026-08-29 14:20:59 -03:00
ivanch 2c15b1f668 adding infra namespace 2026-08-29 14:19:05 -03:00
ivanch a13a567d4b removing internal service 2026-08-28 21:01:13 -03:00
ivanch fd6442e779 alloy: fix chart version pin to 1.12.1 (app v1.19.2) 2026-08-28 20:21:50 -03:00
ivanch 89ef1e10e3 appset: add templatePatch for chart-based apps; onboard alloy (helm chart source) 2026-08-28 20:15:11 -03:00
ivanch f1a8cf7ec4 reworking default namespace 2026-08-28 19:44:21 -03:00
ivanch 8debf442ce adding dev and dns namespaces 2026-08-28 19:42:48 -03:00
60 changed files with 3451 additions and 1850 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.
+32 -6
View File
@@ -1,12 +1,22 @@
# Initial setup # Setup Instructions
```sh
# Create namespace ## Pre-installation
1. Create namespaces with
```bash
kubectl apply -f bootstrap/namespaces.yaml kubectl apply -f bootstrap/namespaces.yaml
```
# Install ArgoCD 2. Apply Vaultwarden deployment app
```bash
kubectl apply -f apps/default/vaultwarden.yaml
```
3. Configure required secrets in `secrets/` directory, each mapping to an entity in Vaultwarden.
## ArgoCD Installation
```sh
kubectl apply -k bootstrap/argocd-install kubectl apply -k bootstrap/argocd-install
# Apply root app
kubectl apply -f bootstrap/root-app.yaml kubectl apply -f bootstrap/root-app.yaml
``` ```
@@ -17,3 +27,19 @@ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.pas
``` ```
2. Access ArgoCD and login with `admin` / password from above. 2. Access ArgoCD and login with `admin` / password from above.
## External Secrets Operator preparation
Create this Secret manually. `BW_HOST` must have no trailing slash.
```bash
kubectl -n infra create secret generic bitwarden-cli \
--from-literal=BW_HOST='[VAULTWARDEN_URL]' \
--from-literal=BW_USERNAME='[VAULTWARDEN_EMAIL]' \
--from-literal=BW_PASSWORD='[VAULTWARDEN_PASSWORD]'
```
## Apply order
1. `kubectl apply -f secrets/`
2. `kubectl apply -f apps/metalldb-system/`
3. `kubectl apply -f apps/infra/`
+123
View File
@@ -0,0 +1,123 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
chart: true
metadata:
name: alloy
namespace: argocd
finalizers: [resources-finalizer.argocd.argoproj.io]
spec:
project: default
destination:
server: https://kubernetes.default.svc
namespace: alloy
syncPolicy:
automated: { prune: true, selfHeal: true }
syncOptions: [CreateNamespace=true, ServerSideApply=true]
source:
repoURL: https://grafana.github.io/helm-charts
chart: alloy
targetRevision: 1.12.1
helm:
valuesObject:
controller:
type: daemonset
alloy:
clustering: { enabled: false }
mounts:
varlog: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
configMap:
create: true
content: |-
discovery.kubernetes "all_pods" {
role = "pod"
selectors {
role = "pod"
field = "spec.nodeName=" + coalesce(env("HOSTNAME"), constants.hostname)
}
}
discovery.relabel "all_pods" {
targets = discovery.kubernetes.all_pods.targets
rule {
source_labels = ["__meta_kubernetes_namespace"]
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
target_label = "pod"
}
rule {
source_labels = ["__meta_kubernetes_pod_container_name"]
target_label = "container"
}
rule {
source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name"]
target_label = "app"
}
}
loki.source.kubernetes "all_logs" {
targets = discovery.relabel.all_pods.output
forward_to = [loki.write.main.receiver]
}
discovery.kubernetes "shared_pods" {
role = "pod"
selectors {
role = "pod"
field = "metadata.namespace=chacal"
}
}
discovery.relabel "shared_pods" {
targets = discovery.kubernetes.shared_pods.targets
rule {
source_labels = ["__meta_kubernetes_namespace"]
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
target_label = "pod"
}
rule {
source_labels = ["__meta_kubernetes_pod_container_name"]
target_label = "container"
}
rule {
source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name"]
target_label = "app"
}
}
loki.source.kubernetes "shared_logs" {
targets = discovery.relabel.shared_pods.output
forward_to = [loki.write.shared.receiver]
}
loki.write "main" {
endpoint {
url = "http://loki.monitoring.svc.cluster.local:3100/loki/api/v1/push"
headers = {
"X-Scope-OrgID" = "main",
}
}
}
loki.write "shared" {
endpoint {
url = "http://loki.monitoring.svc.cluster.local:3100/loki/api/v1/push"
headers = {
"X-Scope-OrgID" = "chacal",
}
}
}
-1
View File
@@ -115,7 +115,6 @@ metadata:
labels: labels:
app: affine app: affine
spec: spec:
ingressClassName: nginx
rules: rules:
- host: affine.haven - host: affine.haven
http: http:
-2
View File
@@ -135,7 +135,6 @@ metadata:
nginx.ingress.kubernetes.io/proxy-read-timeout: "600" nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600" nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
spec: spec:
ingressClassName: nginx
rules: rules:
- host: cloud.haven - host: cloud.haven
http: http:
@@ -158,7 +157,6 @@ metadata:
nginx.ingress.kubernetes.io/proxy-read-timeout: "600" nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600" nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
spec: spec:
ingressClassName: nginx
rules: rules:
- host: cloud.ivanch.me - host: cloud.ivanch.me
http: http:
-1
View File
@@ -122,7 +122,6 @@ metadata:
nginx.ingress.kubernetes.io/proxy-read-timeout: "600" nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600" nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
spec: spec:
ingressClassName: nginx
rules: rules:
- host: slink.haven - host: slink.haven
http: http:
+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
+99 -91
View File
@@ -1,3 +1,39 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonic
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: sonic
template:
metadata:
labels:
app: sonic
spec:
containers:
- name: sonic
image: archivebox/sonic:latest
imagePullPolicy: Always
ports:
- containerPort: 1491
env:
- name: SEARCH_BACKEND_PASSWORD
valueFrom:
secretKeyRef:
name: password
key: password
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "200m"
---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -8,80 +44,69 @@ spec:
selector: selector:
matchLabels: matchLabels:
app: archivebox app: archivebox
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
app: archivebox app: archivebox
spec: spec:
containers: containers:
- env: - name: archivebox
image: archivebox/archivebox:latest
imagePullPolicy: Always
ports:
- containerPort: 8000
env:
- name: SONIC_HOST - name: SONIC_HOST
value: sonic.default.svc.cluster.local value: "sonic.default.svc.cluster.local"
- name: SONIC_PORT - name: SONIC_PORT
value: '1491' value: "1491"
- name: SEARCH_BACKEND_ENGINE - name: SEARCH_BACKEND_ENGINE
value: sonic value: "sonic"
- name: SONIC_PASSWORD - name: SONIC_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: password
name: password name: password
key: password
- name: ADMIN_USERNAME - name: ADMIN_USERNAME
value: ivanch value: "ivanch"
- name: ADMIN_PASSWORD - name: ADMIN_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: password
name: password name: password
key: password
- name: CSRF_TRUSTED_ORIGINS - name: CSRF_TRUSTED_ORIGINS
value: archive.haven value: "archive.haven"
- name: ALLOWED_HOSTS - name: ALLOWED_HOSTS
value: '*' value: "*"
- name: PUBLIC_ADD_VIEW - name: PUBLIC_ADD_VIEW
value: 'false' value: "false"
image: archivebox/archivebox:latest
imagePullPolicy: Always
name: archivebox
ports:
- containerPort: 8000
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts: volumeMounts:
- mountPath: /data - name: archivebox-data
name: archivebox-data mountPath: /data
dnsPolicy: ClusterFirst resources:
restartPolicy: Always requests:
schedulerName: default-scheduler memory: "256Mi"
securityContext: {} cpu: "100m"
terminationGracePeriodSeconds: 30 limits:
memory: "2Gi"
cpu: "3000m"
volumes: volumes:
- name: archivebox-data - name: archivebox-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: archivebox-data claimName: archivebox-data
status: ---
availableReplicas: 1 apiVersion: v1
conditions: kind: Service
- lastTransitionTime: '2026-05-19T01:51:47Z' metadata:
lastUpdateTime: '2026-07-22T10:14:38Z' name: sonic-svc
message: ReplicaSet "archivebox-bb7f58db4" has successfully progressed. namespace: default
reason: NewReplicaSetAvailable spec:
status: 'True' selector:
type: Progressing app: sonic
- lastTransitionTime: '2026-08-28T06:04:13Z' ports:
lastUpdateTime: '2026-08-28T06:04:13Z' - protocol: TCP
message: Deployment has minimum availability. port: 1491
reason: MinimumReplicasAvailable targetPort: 1491
status: 'True'
type: Available
observedGeneration: 94
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
@@ -89,20 +114,30 @@ metadata:
name: archivebox-svc name: archivebox-svc
namespace: default namespace: default
spec: spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 8000
protocol: TCP
targetPort: 8000
selector: selector:
app: archivebox app: archivebox
sessionAffinity: None ports:
type: ClusterIP - protocol: TCP
status: port: 8000
loadBalancer: {} targetPort: 8000
---
# 3) PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: archivebox-data
namespace: default
annotations:
nfs.io/storage-path: "archivebox-data"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
limits:
storage: 30Gi
--- ---
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
@@ -110,41 +145,14 @@ metadata:
name: archivebox-ingress name: archivebox-ingress
namespace: default namespace: default
spec: spec:
ingressClassName: nginx
rules: rules:
- host: archive.haven - host: "archive.haven"
http: http:
paths: paths:
- backend: - path: /
pathType: Prefix
backend:
service: service:
name: archivebox-svc name: archivebox-svc
port: port:
number: 8000 number: 8000
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- ip: 192.168.20.204
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: archivebox-data
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
limits:
storage: 30Gi
requests:
storage: 10Gi
storageClassName: nfs-client
volumeMode: Filesystem
status:
accessModes:
- ReadWriteOnce
capacity:
storage: 10Gi
phase: Bound
+72 -108
View File
@@ -1,12 +1,13 @@
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
labels:
app.kubernetes.io/name: changedetection
name: changedetection name: changedetection
namespace: default namespace: default
labels:
app.kubernetes.io/name: changedetection
spec: spec:
replicas: 1 replicas: 1
revisionHistoryLimit: 10
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: changedetection app.kubernetes.io/name: changedetection
@@ -20,64 +21,68 @@ spec:
affinity: affinity:
nodeAffinity: nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: preferredDuringSchedulingIgnoredDuringExecution:
- preference: - weight: 100
preference:
matchExpressions: matchExpressions:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- iris - iris
weight: 100
containers: containers:
- env: - name: changedetection
- name: PUID
value: '1000'
- name: PGID
value: '1000'
- name: TZ
value: Etc/UTC
- name: BASE_URL
value: http://change.haven/
- name: PLAYWRIGHT_DRIVER_URL
value: ws://localhost:3000
image: lscr.io/linuxserver/changedetection.io:latest image: lscr.io/linuxserver/changedetection.io:latest
imagePullPolicy: Always imagePullPolicy: Always
name: changedetection env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: TZ
value: "Etc/UTC"
- name: BASE_URL
value: "http://change.haven/"
- name: PLAYWRIGHT_DRIVER_URL
value: "ws://localhost:3000"
ports: ports:
- containerPort: 5000 - containerPort: 5000
name: http name: http
protocol: TCP protocol: TCP
resources: resources:
limits:
cpu: '2'
memory: 1Gi
requests: requests:
cpu: 100m cpu: 100m
memory: 256Mi memory: 256Mi
limits:
cpu: 2000m
memory: 1Gi
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
runAsUser: 0 runAsUser: 0
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts: volumeMounts:
- mountPath: /config - name: config
name: config mountPath: /config
- env: - name: browser-sockpuppet-chrome
- name: SCREEN_WIDTH
value: '1920'
- name: SCREEN_HEIGHT
value: '1024'
- name: SCREEN_DEPTH
value: '16'
- name: MAX_CONCURRENT_CHROME_PROCESSES
value: '10'
image: dgtlmoon/sockpuppetbrowser:latest image: dgtlmoon/sockpuppetbrowser:latest
imagePullPolicy: Always imagePullPolicy: Always
name: browser-sockpuppet-chrome env:
- name: SCREEN_WIDTH
value: "1920"
- name: SCREEN_HEIGHT
value: "1024"
- name: SCREEN_DEPTH
value: "16"
- name: MAX_CONCURRENT_CHROME_PROCESSES
value: "10"
ports: ports:
- containerPort: 3000 - containerPort: 3000
name: ws name: ws
protocol: TCP protocol: TCP
resources: {} resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 2000m
memory: 4Gi
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
capabilities: capabilities:
@@ -85,105 +90,64 @@ spec:
- SYS_ADMIN - SYS_ADMIN
drop: drop:
- ALL - ALL
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes: volumes:
- name: config - name: config
persistentVolumeClaim: persistentVolumeClaim:
claimName: changedetection-config claimName: changedetection-config
status: dnsPolicy: ClusterFirst
availableReplicas: 1 restartPolicy: Always
conditions: terminationGracePeriodSeconds: 30
- lastTransitionTime: '2026-08-04T16:20:08Z'
lastUpdateTime: '2026-08-04T16:32:45Z'
message: ReplicaSet "changedetection-77cd668cf4" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
- lastTransitionTime: '2026-08-28T06:05:12Z'
lastUpdateTime: '2026-08-28T06:05:12Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
observedGeneration: 22
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
labels:
app.kubernetes.io/name: changedetection
name: changedetection name: changedetection
namespace: default namespace: default
labels:
app.kubernetes.io/name: changedetection
spec: spec:
internalTrafficPolicy: Cluster type: ClusterIP
ipFamilies: selector:
- IPv4 app.kubernetes.io/name: changedetection
ipFamilyPolicy: SingleStack
ports: ports:
- name: http - name: http
port: 5000 port: 5000
protocol: TCP protocol: TCP
targetPort: http targetPort: http
selector:
app.kubernetes.io/name: changedetection
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
labels:
app.kubernetes.io/name: changedetection
name: changedetection
namespace: default
spec:
ingressClassName: nginx
rules:
- host: change.haven
http:
paths:
- backend:
service:
name: changedetection
port:
number: 5000
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- ip: 192.168.20.204
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: changedetection-config name: changedetection-config
namespace: default namespace: default
annotations:
nfs.io/storage-path: "changedetection-config"
spec: spec:
storageClassName: "nfs-client"
accessModes: accessModes:
- ReadWriteOnce - ReadWriteOnce
resources: resources:
limits:
storage: 2Gi
requests: requests:
storage: 1Gi storage: 1Gi
storageClassName: nfs-client limits:
volumeMode: Filesystem storage: 2Gi
status: ---
accessModes: apiVersion: networking.k8s.io/v1
- ReadWriteOnce kind: Ingress
capacity: metadata:
storage: 1Gi name: changedetection
phase: Bound namespace: default
labels:
app.kubernetes.io/name: changedetection
spec:
rules:
- host: change.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: changedetection
port:
number: 5000
-171
View File
@@ -1,171 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: havenllo
name: havenllo
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: havenllo
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: havenllo
spec:
containers:
- env:
- name: HAVENLLO_DATABASE_PATH
value: /data/havenllo.db
- name: HAVENLLO_LISTEN_ADDR
value: :8080
image: git.ivanch.me/ivanch/havenllo:latest
imagePullPolicy: Always
livenessProbe:
failureThreshold: 3
httpGet:
path: /api/health
port: http
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 2
name: havenllo
ports:
- containerPort: 8080
name: http
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /api/health
port: http
scheme: HTTP
initialDelaySeconds: 2
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 2
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 50m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsUser: 0
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /data
name: data
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- name: data
persistentVolumeClaim:
claimName: havenllo-data
status:
availableReplicas: 1
conditions:
- lastTransitionTime: '2026-07-14T13:21:13Z'
lastUpdateTime: '2026-07-21T21:06:45Z'
message: ReplicaSet "havenllo-5d4f8ccb4f" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
- lastTransitionTime: '2026-08-28T06:04:17Z'
lastUpdateTime: '2026-08-28T06:04:17Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
observedGeneration: 41
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: havenllo
name: havenllo
namespace: default
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app.kubernetes.io/name: havenllo
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: havenllo
namespace: default
spec:
ingressClassName: nginx-https
rules:
- host: havenllo.haven
http:
paths:
- backend:
service:
name: havenllo
port:
number: 8080
path: /
pathType: Prefix
tls:
- hosts:
- havenllo.haven
secretName: havenllo-tls
status:
loadBalancer:
ingress:
- ip: 192.168.20.204
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: havenllo-data
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs-client
volumeMode: Filesystem
status:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
phase: Bound
+152 -173
View File
@@ -1,226 +1,205 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: homepage
namespace: default
labels:
app.kubernetes.io/name: homepage
secrets:
- name: homepage
---
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: homepage
namespace: default
labels:
app.kubernetes.io/name: homepage
annotations:
kubernetes.io/service-account.name: homepage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: homepage
labels:
app.kubernetes.io/name: homepage
rules:
- apiGroups:
- ""
resources:
- namespaces
- pods
- nodes
verbs:
- get
- list
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
verbs:
- get
- list
- apiGroups:
- traefik.io
resources:
- ingressroutes
verbs:
- get
- list
- apiGroups:
- gateway.networking.k8s.io
resources:
- httproutes
- gateways
verbs:
- get
- list
- apiGroups:
- metrics.k8s.io
resources:
- nodes
- pods
verbs:
- get
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: homepage
labels:
app.kubernetes.io/name: homepage
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: homepage
subjects:
- kind: ServiceAccount
name: homepage
namespace: default
---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
labels:
app.kubernetes.io/name: homepage
name: homepage name: homepage
namespace: default namespace: default
labels:
app.kubernetes.io/name: homepage
spec: spec:
revisionHistoryLimit: 3
replicas: 1 replicas: 1
strategy:
type: RollingUpdate
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: homepage app.kubernetes.io/name: homepage
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
app.kubernetes.io/name: homepage app.kubernetes.io/name: homepage
annotations:
configmap.reloader/checksum: '{{ include (print $.Template.BasePath "/app/config/services.yaml") . | sha256sum }}'
spec: spec:
serviceAccountName: homepage
automountServiceAccountToken: true automountServiceAccountToken: true
enableServiceLinks: true
containers: containers:
- env: - name: homepage
- name: POD_IP image: "ghcr.io/gethomepage/homepage:latest"
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: HOMEPAGE_ALLOWED_HOSTS
value: '*'
image: ghcr.io/gethomepage/homepage:latest
imagePullPolicy: Always imagePullPolicy: Always
env:
- name: HOMEPAGE_ALLOWED_HOSTS
value: "*"
ports:
- name: http
containerPort: 3000
protocol: TCP
livenessProbe: livenessProbe:
failureThreshold: 3
httpGet: httpGet:
path: / path: /
port: 3000 port: 3000
scheme: HTTP
initialDelaySeconds: 30 initialDelaySeconds: 30
periodSeconds: 10 periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: homepage
ports:
- containerPort: 3000
name: http
protocol: TCP
readinessProbe: readinessProbe:
failureThreshold: 3
httpGet: httpGet:
path: / path: /
port: 3000 port: 3000
scheme: HTTP
initialDelaySeconds: 5 initialDelaySeconds: 5
periodSeconds: 5 periodSeconds: 5
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts: volumeMounts:
- mountPath: /app/config/logs - name: logs
name: logs mountPath: /app/config/logs
- mountPath: /app/config - name: homepage-config
name: homepage-config mountPath: /app/config
- mountPath: /app/public/images - name: homepage-config
name: homepage-config mountPath: /app/public/images
subPath: images subPath: images
dnsPolicy: ClusterFirst resources:
enableServiceLinks: true requests:
restartPolicy: Always memory: "128Mi"
schedulerName: default-scheduler cpu: "100m"
securityContext: {} limits:
serviceAccount: homepage memory: "512Mi"
serviceAccountName: homepage cpu: "500m"
terminationGracePeriodSeconds: 30
volumes: volumes:
- name: homepage-config - name: homepage-config
persistentVolumeClaim: persistentVolumeClaim:
claimName: homepage-config claimName: homepage-config
- emptyDir: {} - name: logs
name: logs emptyDir: {}
status:
availableReplicas: 1
conditions:
- lastTransitionTime: '2026-05-19T01:51:47Z'
lastUpdateTime: '2026-08-17T09:07:50Z'
message: ReplicaSet "homepage-7b47bf67fb" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
- lastTransitionTime: '2026-08-28T06:04:23Z'
lastUpdateTime: '2026-08-28T06:04:23Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
observedGeneration: 96
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/name: homepage
name: homepage
namespace: default
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 3000
protocol: TCP
targetPort: http
selector:
app.kubernetes.io/name: homepage
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
labels:
app.kubernetes.io/name: homepage
name: homepage
namespace: default
spec:
ingressClassName: nginx
rules:
- host: homepage.haven
http:
paths:
- backend:
service:
name: homepage
port:
number: 3000
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- ip: 192.168.20.204
--- ---
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: homepage-config name: homepage-config
namespace: default namespace: default
annotations:
nfs.io/storage-path: "homepage-config"
spec: spec:
storageClassName: "nfs-client"
accessModes: accessModes:
- ReadWriteMany - ReadWriteMany
resources: resources:
requests: requests:
storage: 1Gi storage: 1Gi
storageClassName: nfs-client
volumeMode: Filesystem
status:
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
phase: Bound
--- ---
apiVersion: v1 apiVersion: v1
data: kind: Service
bookmarks.yaml: "- Developer:\n - Github:\n - abbr: GH\n href:\
\ https://github.com/\n"
custom.css: ''
custom.js: ''
docker.yaml: ''
kubernetes.yaml: 'mode: cluster
'
services.yaml: "- Essentials:\n - AdGuard Home:\n href: http://adguard.haven/\n\
\ icon: adguard-home\n widget:\n type: adguard\n \
\ url: http://adguard.haven/\n username: ivanch\n password:\
\ ESVEPnull\n - AdGuard Home 2:\n href: http://adguard2.haven/\n \
\ icon: adguard-home\n widget:\n type: adguard\n \
\ url: http://adguard2.haven/\n username: ivanch\n password:\
\ ESVEPnull\n - Home Assistant:\n href: http://homeassistant.haven/\n\
\ - Traefik:\n href: http://traefik.haven/\n - Dockge:\n href:\
\ http://dockge.haven/\n - Proxmox:\n href: https://proxmox.haven:8006/\n\
\ widget:\n type: proxmox\n url: https://proxmox.haven:8006/\n\
\ token: homepage@pam!token\n secret: 7cadfc2d-560a-4bb4-8d5f-a29f394bc9a0\n\
\n- Media:\n - Jellyfin:\n href: http://tv.haven/\n icon: jellyfin.png\n\
\ widget:\n type: jellyfin\n url: http://tv.haven/\n\
\ key: c933070d9c8341bf8c64e7a792aaa6b9\n enableBlocks: true\n\
\ enableNowPlaying: true\n - Sonarr:\n href: http://sonarr.haven/\n\
\ - Radarr:\n href: http://radarr.haven/\n - Prowlarr:\n href:\
\ http://prowlarr.haven/\n\n- Storage:\n - Transmission:\n href: http://transmission.haven/\n\
\ - qBitTorrent:\n href: http://qbittorrent.haven/\n - OpenMediaVault:\n\
\ href: http://omv.haven/\n - ArchiveBox:\n href: http://archive.haven/\n\
\ - FileBrowser:\n href: http://files.haven/\n - Paperless:\n \
\ href: http://paperless.haven/\n\n- Other:\n - Uptime Kuma:\n href:\
\ http://uptimekuma.haven/\n - WireGuard:\n href: http://vpn.haven/\n\
\ - Beszel:\n href: http://beszel.haven/\n\n- Zephyr:\n - Gitea:\n\
\ href: http://gitea.ivanch.me/\n - Portainer:\n href: http://portainer.ivanch.me/\n\
\ - Nginx Proxy Manager:\n href: http://manager.ivanch.me/\n - Homepage:\n\
\ href: http://ivanch.me/\n"
settings.yaml: ''
widgets.yaml: "- kubernetes:\n cluster:\n show: true\n cpu: true\n\
\ memory: true\n # network: true\n showLabel: true\n label:\
\ \"Haven\"\n nodes:\n show: true\n cpu: true\n memory: true\n\
\ showLabel: true\n- resources:\n backend: resources\n expanded: true\n\
\ cpu: true\n memory: true\n network: default\n- search:\n provider:\
\ duckduckgo\n target: _blank\n"
kind: ConfigMap
metadata: metadata:
labels:
app.kubernetes.io/name: homepage
name: homepage name: homepage
namespace: default namespace: default
labels:
app.kubernetes.io/name: homepage
spec:
type: ClusterIP
ports:
- port: 3000
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: homepage
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: homepage
namespace: default
labels:
app.kubernetes.io/name: homepage
spec:
rules:
- host: "homepage.haven"
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: homepage
port:
number: 3000
+18 -58
View File
@@ -1,3 +1,4 @@
---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -8,58 +9,30 @@ spec:
selector: selector:
matchLabels: matchLabels:
app: it-tools app: it-tools
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
app: it-tools app: it-tools
spec: spec:
containers: containers:
- image: corentinth/it-tools:latest - name: it-tools
image: corentinth/it-tools:latest
imagePullPolicy: Always imagePullPolicy: Always
name: it-tools
ports: ports:
- containerPort: 80 - containerPort: 80
protocol: TCP
readinessProbe: readinessProbe:
failureThreshold: 3
httpGet: httpGet:
path: / path: /
port: 80 port: 80
scheme: HTTP
initialDelaySeconds: 5 initialDelaySeconds: 5
periodSeconds: 10 periodSeconds: 10
successThreshold: 1 resources:
timeoutSeconds: 1 requests:
resources: {} memory: "64Mi"
terminationMessagePath: /dev/termination-log cpu: "50m"
terminationMessagePolicy: File limits:
dnsPolicy: ClusterFirst memory: "128Mi"
restartPolicy: Always cpu: "200m"
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
availableReplicas: 1
conditions:
- lastTransitionTime: '2026-05-19T01:51:47Z'
lastUpdateTime: '2026-07-22T10:14:45Z'
message: ReplicaSet "it-tools-6fc797dd48" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
- lastTransitionTime: '2026-08-28T06:04:26Z'
lastUpdateTime: '2026-08-28T06:04:26Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
observedGeneration: 93
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
@@ -67,20 +40,12 @@ metadata:
name: it-tools-svc name: it-tools-svc
namespace: default namespace: default
spec: spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 80
selector: selector:
app: it-tools app: it-tools
sessionAffinity: None ports:
type: ClusterIP - protocol: TCP
status: port: 80
loadBalancer: {} targetPort: 80
--- ---
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
@@ -88,19 +53,14 @@ metadata:
name: it-tools-ingress name: it-tools-ingress
namespace: default namespace: default
spec: spec:
ingressClassName: nginx
rules: rules:
- host: tools.haven - host: "tools.haven"
http: http:
paths: paths:
- backend: - path: /
pathType: Prefix
backend:
service: service:
name: it-tools-svc name: it-tools-svc
port: port:
number: 80 number: 80
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- ip: 192.168.20.204
+20 -25
View File
@@ -1,34 +1,14 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: notepad-data
namespace: default
annotations:
nfs.io/storage-path: notepad-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs-client
--- ---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: notepad name: notepad
namespace: default namespace: default
labels:
app: notepad
spec: spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: notepad app: notepad
# RWO PVC + single replica: Recreate avoids the two-ReplicaSet PVC fight
# on rollout (see homelab-deploy skill: single-writer state).
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
@@ -41,7 +21,10 @@ spec:
command: command:
- sh - sh
- -c - -c
- mkdir -p /var/www/html/_tmp && cp -n /var/www/html/notes.htaccess /var/www/html/_tmp/.htaccess 2>/dev/null; exec docker-php-entrypoint apache2-foreground - >-
mkdir -p /var/www/html/_tmp &&
cp -n /var/www/html/notes.htaccess /var/www/html/_tmp/.htaccess 2>/dev/null;
exec docker-php-entrypoint apache2-foreground
ports: ports:
- containerPort: 80 - containerPort: 80
resources: resources:
@@ -58,6 +41,7 @@ spec:
- name: notepad-data - name: notepad-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: notepad-data claimName: notepad-data
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
@@ -72,13 +56,27 @@ spec:
- port: 80 - port: 80
targetPort: 80 targetPort: 80
--- ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: notepad-data
namespace: default
annotations:
nfs.io/storage-path: "notepad-data"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
metadata: metadata:
name: notepad name: notepad
namespace: default namespace: default
spec: spec:
ingressClassName: nginx
rules: rules:
- host: notepad.haven - host: notepad.haven
http: http:
@@ -90,6 +88,3 @@ spec:
name: notepad name: notepad
port: port:
number: 80 number: 80
# NOTE: the Argo Application manifest for notepad lives in apps/root/notepad.yaml
# (watched by the root app-of-apps). Do not add Application objects to this
# directory — this path is what the Application itself watches.
+50 -63
View File
@@ -8,8 +8,6 @@ spec:
selector: selector:
matchLabels: matchLabels:
app: openwebui app: openwebui
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
@@ -18,103 +16,92 @@ spec:
affinity: affinity:
nodeAffinity: nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: preferredDuringSchedulingIgnoredDuringExecution:
- preference: - weight: 100
preference:
matchExpressions: matchExpressions:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- iris - iris
weight: 100
containers: containers:
- env: - name: openwebui
image: ghcr.io/open-webui/open-webui:main-slim
imagePullPolicy: Always
ports:
- containerPort: 8080
env:
- name: PUID - name: PUID
value: '1000' value: "1000"
- name: PGID - name: PGID
value: '1000' value: "1000"
- name: TZ - name: TZ
value: America/Sao_Paulo value: "America/Sao_Paulo"
- name: DATABASE_URL - name: DATABASE_URL
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: DATABASE_URL
name: openwebui-secret name: openwebui-secret
- name: PLAYWRIGHT_WS_URL key: DATABASE_URL
value: ws://browserless.default.svc.cluster.local:3000
image: ghcr.io/open-webui/open-webui:main-slim
imagePullPolicy: Always
name: openwebui
ports:
- containerPort: 8080
protocol: TCP
resources: resources:
limits:
cpu: '1'
memory: 2Gi
requests: requests:
cpu: 250m cpu: "250m"
memory: 512Mi memory: "512Mi"
terminationMessagePath: /dev/termination-log limits:
terminationMessagePolicy: File cpu: "1000m"
memory: "2Gi"
volumeMounts: volumeMounts:
- mountPath: /app/backend/data - name: openwebui-data
name: openwebui-data mountPath: /app/backend/data
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes: volumes:
- name: openwebui-data - name: openwebui-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: openwebui-data claimName: openwebui-data
---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: openwebui name: openwebui
namespace: default namespace: default
spec: spec:
internalTrafficPolicy: Cluster type: ClusterIP
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector: selector:
app: openwebui app: openwebui
sessionAffinity: None ports:
type: ClusterIP - port: 8080
apiVersion: networking.k8s.io/v1 targetPort: 8080
kind: Ingress ---
metadata:
name: openwebui
namespace: default
spec:
ingressClassName: nginx
rules:
- host: openwebui.haven
http:
paths:
- backend:
service:
name: openwebui
port:
number: 8080
path: /
pathType: Prefix
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: openwebui-data name: openwebui-data
namespace: default namespace: default
annotations:
nfs.io/storage-path: "openwebui-data"
spec: spec:
storageClassName: "nfs-client"
accessModes: accessModes:
- ReadWriteOnce - ReadWriteOnce
resources: resources:
limits:
storage: 10Gi
requests: requests:
storage: 5Gi storage: 5Gi
storageClassName: nfs-client limits:
volumeMode: Filesystem storage: 10Gi
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: openwebui
namespace: default
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
rules:
- host: openwebui.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: openwebui
port:
number: 8080
+62 -70
View File
@@ -1,3 +1,5 @@
---
# 1) Deployment
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -8,8 +10,6 @@ spec:
selector: selector:
matchLabels: matchLabels:
app: paperless app: paperless
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
@@ -18,27 +18,30 @@ spec:
affinity: affinity:
nodeAffinity: nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution: preferredDuringSchedulingIgnoredDuringExecution:
- preference: - weight: 100
preference:
matchExpressions: matchExpressions:
- key: kubernetes.io/hostname - key: kubernetes.io/hostname
operator: In operator: In
values: values:
- iris - iris
weight: 100
containers: containers:
- env: - name: paperless
image: ghcr.io/paperless-ngx/paperless-ngx:latest
imagePullPolicy: Always
env:
- name: PUID - name: PUID
value: '1000' value: "1000"
- name: PGID - name: PGID
value: '1000' value: "1000"
- name: PAPERLESS_URL - name: PAPERLESS_URL
value: http://paperless.haven value: "http://paperless.haven"
- name: PAPERLESS_TIME_ZONE - name: PAPERLESS_TIME_ZONE
value: America/Sao_Paulo value: "America/Sao_Paulo"
- name: PAPERLESS_OCR_LANGUAGE - name: PAPERLESS_OCR_LANGUAGE
value: por value: "por"
- name: PAPERLESS_OCR_LANGUAGES - name: PAPERLESS_OCR_LANGUAGES
value: por value: "por"
- name: PAPERLESS_OCR_USER_ARGS - name: PAPERLESS_OCR_USER_ARGS
value: '{"invalidate_digital_signatures": true}' value: '{"invalidate_digital_signatures": true}'
- name: PAPERLESS_DBHOST - name: PAPERLESS_DBHOST
@@ -46,113 +49,102 @@ spec:
- name: PAPERLESS_DBNAME - name: PAPERLESS_DBNAME
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: PAPERLESS_DBNAME
name: paperless-secret name: paperless-secret
key: PAPERLESS_DBNAME
- name: PAPERLESS_DBUSER - name: PAPERLESS_DBUSER
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: PAPERLESS_DBUSER
name: paperless-secret name: paperless-secret
key: PAPERLESS_DBUSER
- name: PAPERLESS_DBPASSWORD - name: PAPERLESS_DBPASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: PAPERLESS_DBPASSWORD
name: paperless-secret name: paperless-secret
key: PAPERLESS_DBPASSWORD
- name: PAPERLESS_REDIS - name: PAPERLESS_REDIS
value: redis://redis.haven:6379 value: "redis://redis.haven:6379"
- name: PAPERLESS_PORT - name: PAPERLESS_PORT
value: '8000' value: "8000"
- name: PAPERLESS_SECRET_KEY - name: PAPERLESS_SECRET_KEY
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: PAPERLESS_SECRET_KEY
name: paperless-secret name: paperless-secret
image: ghcr.io/paperless-ngx/paperless-ngx:latest key: PAPERLESS_SECRET_KEY
imagePullPolicy: Always
name: paperless
ports: ports:
- containerPort: 8000 - containerPort: 8000
name: paperless-port name: paperless-port
protocol: TCP
resources: resources:
limits:
cpu: '4'
memory: 1Gi
requests: requests:
cpu: 100m cpu: "100m"
memory: 256Mi memory: "256Mi"
terminationMessagePath: /dev/termination-log limits:
terminationMessagePolicy: File cpu: "4000m"
memory: "1Gi"
volumeMounts: volumeMounts:
- mountPath: /usr/src/paperless/data - name: paperless-data
name: paperless-data
subPath: data subPath: data
- mountPath: /usr/src/paperless/media mountPath: /usr/src/paperless/data
name: paperless-data - name: paperless-data
subPath: media subPath: media
- mountPath: /usr/src/paperless/export mountPath: /usr/src/paperless/media
name: paperless-data - name: paperless-data
subPath: export subPath: export
- mountPath: /usr/src/paperless/consume mountPath: /usr/src/paperless/export
name: paperless-data - name: paperless-data
subPath: consume subPath: consume
dnsPolicy: ClusterFirst mountPath: /usr/src/paperless/consume
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes: volumes:
- name: paperless-data - name: paperless-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: paperless-data claimName: paperless-data
---
# 2) Service
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: paperless name: paperless
namespace: default namespace: default
spec: spec:
internalTrafficPolicy: Cluster type: ClusterIP
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 8000
protocol: TCP
targetPort: paperless-port
selector: selector:
app: paperless app: paperless
sessionAffinity: None ports:
type: ClusterIP - port: 8000
targetPort: paperless-port
---
# 3) PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: paperless-data
namespace: default
annotations:
nfs.io/storage-path: "paperless-data"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
limits:
storage: 15Gi
---
# 4) Ingress
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
metadata: metadata:
name: paperless name: paperless
namespace: default namespace: default
spec: spec:
ingressClassName: nginx
rules: rules:
- host: paperless.haven - host: paperless.haven
http: http:
paths: paths:
- backend: - path: /
pathType: Prefix
backend:
service: service:
name: paperless name: paperless
port: port:
number: 8000 number: 8000
path: /
pathType: Prefix
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: paperless-data
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
limits:
storage: 15Gi
requests:
storage: 5Gi
storageClassName: nfs-client
volumeMode: Filesystem
+9 -10
View File
@@ -32,13 +32,13 @@ spec:
- playwright@1.58.0 - playwright@1.58.0
- run-server - run-server
- --port - --port
- '3000' - "3000"
- --host - --host
- 0.0.0.0 - 0.0.0.0
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:
@@ -46,7 +46,7 @@ spec:
protocol: TCP protocol: TCP
resources: resources:
limits: limits:
cpu: '4' cpu: "4"
memory: 4Gi memory: 4Gi
requests: requests:
cpu: 500m cpu: 500m
@@ -68,17 +68,17 @@ spec:
status: status:
availableReplicas: 1 availableReplicas: 1
conditions: conditions:
- lastTransitionTime: '2026-05-29T15:54:24Z' - lastTransitionTime: "2026-05-29T15:54:24Z"
lastUpdateTime: '2026-07-22T10:14:35Z' lastUpdateTime: "2026-07-22T10:14:35Z"
message: ReplicaSet "playwright-86c74d7c78" has successfully progressed. message: ReplicaSet "playwright-86c74d7c78" has successfully progressed.
reason: NewReplicaSetAvailable reason: NewReplicaSetAvailable
status: 'True' status: "True"
type: Progressing type: Progressing
- lastTransitionTime: '2026-08-28T06:04:14Z' - lastTransitionTime: "2026-08-28T06:04:14Z"
lastUpdateTime: '2026-08-28T06:04:14Z' lastUpdateTime: "2026-08-28T06:04:14Z"
message: Deployment has minimum availability. message: Deployment has minimum availability.
reason: MinimumReplicasAvailable reason: MinimumReplicasAvailable
status: 'True' status: "True"
type: Available type: Available
observedGeneration: 95 observedGeneration: 95
readyReplicas: 1 readyReplicas: 1
@@ -113,7 +113,6 @@ metadata:
name: playwright name: playwright
namespace: default namespace: default
spec: spec:
ingressClassName: nginx
rules: rules:
- host: playwright.haven - host: playwright.haven
http: http:
-176
View File
@@ -1,176 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: qbittorrent
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: qbittorrent
strategy:
type: Recreate
template:
metadata:
labels:
app: qbittorrent
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- iris
weight: 100
containers:
- env:
- name: PUID
value: '1000'
- name: PGID
value: '1000'
- name: TZ
value: Etc/UTC
- name: WEBUI_PORT
value: '4300'
- name: TORRENTING_PORT
value: '6881'
image: lscr.io/linuxserver/qbittorrent:5.0.4
imagePullPolicy: Always
name: qbittorrent
ports:
- containerPort: 4300
name: webui-port
protocol: TCP
- containerPort: 6881
name: qbit-tcp
protocol: TCP
- containerPort: 6881
name: qbit-udp
protocol: UDP
resources:
limits:
cpu: '1'
memory: 512Mi
requests:
cpu: 200m
memory: 256Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /config
name: qbittorrent-config
- mountPath: /nas
name: nas-storage
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- name: qbittorrent-config
persistentVolumeClaim:
claimName: qbittorrent-config
- name: nas-storage
nfs:
path: /export/Storage
server: 192.168.15.99
status:
availableReplicas: 1
conditions:
- lastTransitionTime: '2026-05-19T01:51:48Z'
lastUpdateTime: '2026-07-22T10:14:39Z'
message: ReplicaSet "qbittorrent-56dbf5f6c" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
- lastTransitionTime: '2026-08-28T06:04:17Z'
lastUpdateTime: '2026-08-28T06:04:17Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
observedGeneration: 100
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
---
apiVersion: v1
kind: Service
metadata:
name: qbittorrent
namespace: default
spec:
externalTrafficPolicy: Cluster
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: webui
nodePort: 30948
port: 4300
protocol: TCP
targetPort: webui-port
- name: torrent-tcp
nodePort: 30183
port: 6881
protocol: TCP
targetPort: qbit-tcp
- name: torrent-udp
nodePort: 30183
port: 6881
protocol: UDP
targetPort: qbit-udp
selector:
app: qbittorrent
sessionAffinity: None
type: NodePort
status:
loadBalancer: {}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: qbittorrent
namespace: default
spec:
ingressClassName: nginx
rules:
- host: qbittorrent.haven
http:
paths:
- backend:
service:
name: qbittorrent
port:
number: 4300
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- ip: 192.168.20.204
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: qbittorrent-config
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs-client
volumeMode: Filesystem
status:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
phase: Bound
+41 -84
View File
@@ -1,3 +1,5 @@
---
# 1) Deployment
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -8,129 +10,84 @@ spec:
selector: selector:
matchLabels: matchLabels:
app: searxng app: searxng
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
app: searxng app: searxng
spec: spec:
enableServiceLinks: false
containers: containers:
- env: - name: searxng
- name: PUID
value: '1000'
- name: PGID
value: '1000'
image: searxng/searxng:latest image: searxng/searxng:latest
imagePullPolicy: Always imagePullPolicy: Always
name: searxng env:
- name: PUID
value: "1000"
- name: PGID
value: "1000"
ports: ports:
- containerPort: 8080 - containerPort: 8080
name: searxng-port name: searxng-port
protocol: TCP
resources: resources:
limits:
cpu: 500m
memory: 512Mi
requests: requests:
cpu: 100m cpu: "100m"
memory: 256Mi memory: "256Mi"
terminationMessagePath: /dev/termination-log limits:
terminationMessagePolicy: File cpu: "500m"
memory: "512Mi"
volumeMounts: volumeMounts:
- mountPath: /etc/searxng - name: searxng-config
name: searxng-config mountPath: /etc/searxng
dnsPolicy: ClusterFirst
enableServiceLinks: false
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes: volumes:
- name: searxng-config - name: searxng-config
persistentVolumeClaim: persistentVolumeClaim:
claimName: searxng-config claimName: searxng-config
status:
availableReplicas: 1
conditions:
- lastTransitionTime: '2026-05-19T01:51:48Z'
lastUpdateTime: '2026-07-22T10:15:23Z'
message: ReplicaSet "searxng-d6869bf47" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
- lastTransitionTime: '2026-08-28T06:04:24Z'
lastUpdateTime: '2026-08-28T06:04:24Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
observedGeneration: 93
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
--- ---
# 2) Service
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: searxng name: searxng
namespace: default namespace: default
spec: spec:
internalTrafficPolicy: Cluster type: ClusterIP
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 8080
protocol: TCP
targetPort: searxng-port
selector: selector:
app: searxng app: searxng
sessionAffinity: None ports:
type: ClusterIP - port: 8080
status: targetPort: searxng-port
loadBalancer: {}
--- ---
# 3) PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: searxng-config
namespace: default
annotations:
nfs.io/storage-path: "searxng-config"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
# 4) Ingress
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
metadata: metadata:
name: searxng name: searxng
namespace: default namespace: default
spec: spec:
ingressClassName: nginx
rules: rules:
- host: search.haven - host: search.haven
http: http:
paths: paths:
- backend: - path: /
pathType: Prefix
backend:
service: service:
name: searxng name: searxng
port: port:
number: 8080 number: 8080
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- ip: 192.168.20.204
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: searxng-config
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs-client
volumeMode: Filesystem
status:
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
phase: Bound
-79
View File
@@ -1,79 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonic
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: sonic
strategy:
type: Recreate
template:
metadata:
labels:
app: sonic
spec:
containers:
- env:
- name: SEARCH_BACKEND_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: password
image: archivebox/sonic:latest
imagePullPolicy: Always
name: sonic
ports:
- containerPort: 1491
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
availableReplicas: 1
conditions:
- lastTransitionTime: '2026-05-19T01:51:47Z'
lastUpdateTime: '2026-07-22T10:15:06Z'
message: ReplicaSet "sonic-b89458d6c" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
- lastTransitionTime: '2026-08-28T06:04:13Z'
lastUpdateTime: '2026-08-28T06:04:13Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
observedGeneration: 93
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
---
apiVersion: v1
kind: Service
metadata:
name: sonic-svc
namespace: default
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 1491
protocol: TCP
targetPort: 1491
selector:
app: sonic
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
+49 -94
View File
@@ -1,157 +1,112 @@
---
# 1) Deployment - Stirling-PDF
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
labels:
app: stirlingpdf
name: stirlingpdf name: stirlingpdf
namespace: default namespace: default
labels:
app: stirlingpdf
spec: spec:
replicas: 1 replicas: 1
selector: selector:
matchLabels: matchLabels:
app: stirlingpdf app: stirlingpdf
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
app: stirlingpdf app: stirlingpdf
spec: spec:
containers: containers:
- env: - name: stirlingpdf
- name: TZ
value: America/Sao_Paulo
- name: DOCKER_ENABLE_SECURITY
value: 'false'
- name: SECURITY_ENABLELOGIN
value: 'false'
image: stirlingtools/stirling-pdf:latest image: stirlingtools/stirling-pdf:latest
imagePullPolicy: Always imagePullPolicy: Always
name: stirlingpdf env:
- name: TZ
value: "America/Sao_Paulo"
- name: DOCKER_ENABLE_SECURITY
value: "false"
- name: SECURITY_ENABLELOGIN
value: "false"
ports: ports:
- containerPort: 8080 - containerPort: 8080
name: http name: http
protocol: TCP
readinessProbe: readinessProbe:
failureThreshold: 3
httpGet: httpGet:
path: / path: /
port: 8080 port: 8080
scheme: HTTP
initialDelaySeconds: 20 initialDelaySeconds: 20
periodSeconds: 10 periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources: resources:
limits:
cpu: '2'
memory: 2Gi
requests: requests:
cpu: 100m cpu: 100m
memory: 1Gi memory: 1Gi
limits:
cpu: 2000m
memory: 2Gi
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
runAsUser: 0 runAsUser: 0
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts: volumeMounts:
- mountPath: /configs - name: config
name: config mountPath: /configs
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes: volumes:
- name: config - name: config
persistentVolumeClaim: persistentVolumeClaim:
claimName: stirlingpdf-config claimName: stirlingpdf-config
status: terminationGracePeriodSeconds: 30
availableReplicas: 1
conditions:
- lastTransitionTime: '2026-08-12T01:49:00Z'
lastUpdateTime: '2026-08-12T01:49:43Z'
message: ReplicaSet "stirlingpdf-5d9d988965" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
- lastTransitionTime: '2026-08-28T06:05:17Z'
lastUpdateTime: '2026-08-28T06:05:17Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
observedGeneration: 11
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
--- ---
# 2) Service
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
labels:
app: stirlingpdf
name: stirlingpdf name: stirlingpdf
namespace: default namespace: default
labels:
app: stirlingpdf
spec: spec:
internalTrafficPolicy: Cluster type: ClusterIP
ipFamilies: selector:
- IPv4 app: stirlingpdf
ipFamilyPolicy: SingleStack
ports: ports:
- name: http - name: http
port: 8080 port: 8080
protocol: TCP protocol: TCP
targetPort: http targetPort: http
selector:
app: stirlingpdf
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
labels:
app: stirlingpdf
name: stirlingpdf
namespace: default
spec:
ingressClassName: nginx
rules:
- host: stirling.haven
http:
paths:
- backend:
service:
name: stirlingpdf
port:
number: 8080
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- ip: 192.168.20.204
--- ---
# 3) PersistentVolumeClaim
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: stirlingpdf-config name: stirlingpdf-config
namespace: default namespace: default
annotations:
nfs.io/storage-path: "stirlingpdf-config"
spec: spec:
storageClassName: "nfs-client"
accessModes: accessModes:
- ReadWriteOnce - ReadWriteOnce
resources: resources:
requests: requests:
storage: 1Gi storage: 1Gi
storageClassName: nfs-client ---
volumeMode: Filesystem # 4) Ingress
status: apiVersion: networking.k8s.io/v1
accessModes: kind: Ingress
- ReadWriteOnce metadata:
capacity: name: stirlingpdf
storage: 1Gi namespace: default
phase: Bound labels:
app: stirlingpdf
spec:
rules:
- host: stirling.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: stirlingpdf
port:
number: 8080
+50 -99
View File
@@ -1,3 +1,5 @@
---
# 1) Deployment
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -5,151 +7,100 @@ metadata:
namespace: default namespace: default
spec: spec:
replicas: 1 replicas: 1
strategy:
type: Recreate
selector: selector:
matchLabels: matchLabels:
app: uptimekuma app: uptimekuma
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
app: uptimekuma app: uptimekuma
spec: spec:
containers: containers:
- env: - name: uptimekuma
- name: PUID
value: '1000'
- name: PGID
value: '1000'
image: louislam/uptime-kuma:2 image: louislam/uptime-kuma:2
imagePullPolicy: Always imagePullPolicy: Always
livenessProbe: env:
failureThreshold: 3 - name: PUID
httpGet: value: "1000"
path: / - name: PGID
port: 3001 value: "1000"
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 60
successThreshold: 1
timeoutSeconds: 1
name: uptimekuma
ports: ports:
- containerPort: 3001 - containerPort: 3001
name: uptimekuma-port name: uptimekuma-port
protocol: TCP resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /
port: 3001
initialDelaySeconds: 30
periodSeconds: 60
readinessProbe: readinessProbe:
failureThreshold: 3
httpGet: httpGet:
path: / path: /
port: 3001 port: 3001
scheme: HTTP
initialDelaySeconds: 5 initialDelaySeconds: 5
periodSeconds: 5 periodSeconds: 5
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts: volumeMounts:
- mountPath: /app/data - name: uptimekuma-config
name: uptimekuma-config mountPath: /app/data
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes: volumes:
- name: uptimekuma-config - name: uptimekuma-config
persistentVolumeClaim: persistentVolumeClaim:
claimName: uptimekuma-config claimName: uptimekuma-config
status:
availableReplicas: 1
conditions:
- lastTransitionTime: '2026-05-19T01:51:48Z'
lastUpdateTime: '2026-07-22T10:17:49Z'
message: ReplicaSet "uptimekuma-5cdbf7d589" has successfully progressed.
reason: NewReplicaSetAvailable
status: 'True'
type: Progressing
- lastTransitionTime: '2026-08-28T06:04:55Z'
lastUpdateTime: '2026-08-28T06:04:55Z'
message: Deployment has minimum availability.
reason: MinimumReplicasAvailable
status: 'True'
type: Available
observedGeneration: 94
readyReplicas: 1
replicas: 1
terminatingReplicas: 0
updatedReplicas: 1
--- ---
# 2) Service
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: uptimekuma name: uptimekuma
namespace: default namespace: default
spec: spec:
internalTrafficPolicy: Cluster type: ClusterIP
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 3001
protocol: TCP
targetPort: uptimekuma-port
selector: selector:
app: uptimekuma app: uptimekuma
sessionAffinity: None ports:
type: ClusterIP - port: 3001
status: targetPort: uptimekuma-port
loadBalancer: {}
--- ---
# 3) PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: uptimekuma-config
namespace: default
annotations:
nfs.io/storage-path: "uptimekuma-config"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
# 4) Ingress
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
metadata: metadata:
name: uptimekuma name: uptimekuma
namespace: default namespace: default
spec: spec:
ingressClassName: nginx
rules: rules:
- host: uptimekuma.haven - host: uptimekuma.haven
http: http:
paths: paths:
- backend: - path: /
pathType: Prefix
backend:
service: service:
name: uptimekuma name: uptimekuma
port: port:
number: 3001 number: 3001
path: /
pathType: Prefix
status:
loadBalancer:
ingress:
- ip: 192.168.20.204
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: uptimekuma-config
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs-client
volumeMode: Filesystem
status:
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
phase: Bound
+78 -84
View File
@@ -1,3 +1,5 @@
---
# 1) Deployment
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -8,145 +10,137 @@ spec:
selector: selector:
matchLabels: matchLabels:
app: vaultwarden app: vaultwarden
strategy:
type: Recreate
template: template:
metadata: metadata:
labels: labels:
app: vaultwarden app: vaultwarden
spec: spec:
containers: containers:
- env: - name: vaultwarden
image: vaultwarden/server:latest
imagePullPolicy: Always
env:
- name: DOMAIN - name: DOMAIN
value: https://vault.haven value: "https://vault.haven"
- name: ADMIN_TOKEN - name: ADMIN_TOKEN
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
key: ADMIN_TOKEN
name: vaultwarden-admin-token name: vaultwarden-admin-token
image: vaultwarden/server:latest key: ADMIN_TOKEN
imagePullPolicy: Always
livenessProbe:
failureThreshold: 6
httpGet:
path: /alive
port: vault-port
scheme: HTTP
periodSeconds: 15
successThreshold: 1
timeoutSeconds: 3
name: vaultwarden
ports: ports:
- containerPort: 80 - containerPort: 80
name: vault-port name: vault-port
protocol: TCP # /alive requires no authentication and verifies the Vaultwarden process
readinessProbe:
failureThreshold: 3
httpGet:
path: /alive
port: vault-port
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 3
resources:
limits:
cpu: 250m
memory: 256Mi
requests:
cpu: 250m
memory: 64Mi
startupProbe: startupProbe:
failureThreshold: 30
httpGet: httpGet:
path: /alive path: /alive
port: vault-port port: vault-port
scheme: HTTP scheme: HTTP
periodSeconds: 5 periodSeconds: 5
successThreshold: 1
timeoutSeconds: 3 timeoutSeconds: 3
terminationMessagePath: /dev/termination-log failureThreshold: 30
terminationMessagePolicy: File readinessProbe:
httpGet:
path: /alive
port: vault-port
scheme: HTTP
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
livenessProbe:
httpGet:
path: /alive
port: vault-port
scheme: HTTP
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 6
volumeMounts: volumeMounts:
- mountPath: /data - name: vaultwarden-data
name: vaultwarden-data mountPath: /data
dnsPolicy: ClusterFirst resources:
restartPolicy: Always requests:
schedulerName: default-scheduler cpu: 250m
securityContext: {} memory: 64Mi
terminationGracePeriodSeconds: 30 limits:
cpu: 250m
memory: 256Mi
volumes: volumes:
- name: vaultwarden-data - name: vaultwarden-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: vaultwarden-data claimName: vaultwarden-data
---
# 2) Service
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: vaultwarden name: vaultwarden
namespace: default namespace: default
spec: spec:
internalTrafficPolicy: Cluster type: ClusterIP
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: vault-port
selector: selector:
app: vaultwarden app: vaultwarden
sessionAffinity: None ports:
type: ClusterIP - port: 80
targetPort: vault-port
---
# 3) PersistentVolumeClaim (for /data)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vaultwarden-data
namespace: default
annotations:
nfs.io/storage-path: "vaultwarden-data"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
# 4) Ingress
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
metadata: metadata:
name: vaultwarden name: vaultwarden
namespace: default namespace: default
annotations:
cert-manager.io/cluster-issuer: internal-ca
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec: spec:
ingressClassName: nginx
rules:
- host: vault.haven
http:
paths:
- backend:
service:
name: vaultwarden
port:
number: 80
path: /
pathType: Prefix
tls: tls:
- hosts: - hosts:
- vault.haven - vault.haven
secretName: vaultwarden-tls secretName: vaultwarden-tls
rules:
- host: vault.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vaultwarden
port:
number: 80
---
# 4) Ingress
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: Ingress kind: Ingress
metadata: metadata:
name: vaultwarden-public name: vaultwarden-public
namespace: default namespace: default
spec: spec:
ingressClassName: nginx
rules: rules:
- host: vault.ivanch.me - host: vault.ivanch.me
http: http:
paths: paths:
- backend: - path: /
pathType: Prefix
backend:
service: service:
name: vaultwarden name: vaultwarden
port: port:
number: 80 number: 80
path: /
pathType: Prefix
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vaultwarden-data
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs-client
volumeMode: Filesystem
+202
View File
@@ -0,0 +1,202 @@
# --- ConfigMap for the AMD64 Runner ---
apiVersion: v1
kind: ConfigMap
metadata:
name: gitea-runner-amd64-config
namespace: dev
data:
config.yaml: |
# Registration token and Gitea instance URL should be managed via secrets
runner:
capacity: 4
timeout: 1h
labels:
- "ubuntu-amd64:docker://docker.gitea.com/runner-images:ubuntu-latest"
- "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest"
- "ubuntu-slim:docker://docker.gitea.com/runner-images:ubuntu-latest-slim"
- "runner-full:docker://git.ivanch.me/ivanch/runner-images:full"
- "runner-slim:docker://git.ivanch.me/ivanch/runner-images:slim"
- "runner-full-amd64:docker://git.ivanch.me/ivanch/runner-images:full"
- "runner-slim-amd64:docker://git.ivanch.me/ivanch/runner-images:slim"
---
# --- ConfigMap for the ARM64 Runner ---
apiVersion: v1
kind: ConfigMap
metadata:
name: gitea-runner-arm64-config
namespace: dev
data:
config.yaml: |
runner:
capacity: 4
timeout: 1h
labels:
- "ubuntu-arm64:docker://docker.gitea.com/runner-images:ubuntu-latest"
- "runner-full-arm64:docker://git.ivanch.me/ivanch/runner-images:full"
- "runner-slim-arm64:docker://git.ivanch.me/ivanch/runner-images:slim"
---
# PersistentVolumeClaim for AMD64
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitea-runner-amd64-pvc
namespace: dev
annotations:
nfs.io/storage-path: "gitea-runner-amd64-pvc"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 8Mi
---
# PersistentVolumeClaim for ARM64
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitea-runner-arm64-pvc
namespace: dev
annotations:
nfs.io/storage-path: "gitea-runner-arm64-pvc"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 8Mi
---
# --- Deployment for the AMD64 Runner ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea-runner-amd64
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: gitea-runner-amd64
template:
metadata:
labels:
app: gitea-runner-amd64
spec:
containers:
- name: gitea-runner
image: gitea/act_runner:latest
imagePullPolicy: Always
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "4000m"
memory: "4Gi"
volumeMounts:
- name: config-volume
mountPath: /etc/gitea-runner/config.yaml
subPath: config.yaml
- name: docker-socket
mountPath: /var/run/docker.sock
- name: gitea-runner-amd64-pvc
mountPath: /data
env:
- name: GITEA_RUNNER_REGISTRATION_TOKEN
valueFrom:
secretKeyRef:
name: gitea-runner-token
key: REGISTRATION_TOKEN
- name: GITEA_INSTANCE_URL
value: https://git.ivanch.me
- name: GITEA_RUNNER_NAME
value: k8s-runner-amd64
- name: CONFIG_FILE
value: /etc/gitea-runner/config.yaml
volumes:
- name: config-volume
configMap:
name: gitea-runner-amd64-config
- name: docker-socket
hostPath:
path: /var/run/docker.sock
- name: gitea-runner-amd64-pvc
persistentVolumeClaim:
claimName: gitea-runner-amd64-pvc
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- iris
---
# --- Deployment for the ARM64 Runner ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea-runner-arm64
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: gitea-runner-arm64
template:
metadata:
labels:
app: gitea-runner-arm64
spec:
containers:
- name: gitea-runner
image: gitea/act_runner:latest
imagePullPolicy: Always
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "4000m"
memory: "4Gi"
volumeMounts:
- name: config-volume
mountPath: /etc/gitea-runner/config.yaml
subPath: config.yaml
- name: docker-socket
mountPath: /var/run/docker.sock
- name: gitea-runner-arm64-pvc
mountPath: /data
env:
- name: GITEA_RUNNER_REGISTRATION_TOKEN
valueFrom:
secretKeyRef:
name: gitea-runner-token
key: REGISTRATION_TOKEN
- name: GITEA_INSTANCE_URL
value: https://git.ivanch.me
- name: GITEA_RUNNER_NAME
value: k8s-runner-arm64
- name: CONFIG_FILE
value: /etc/gitea-runner/config.yaml
volumes:
- name: config-volume
configMap:
name: gitea-runner-arm64-config
- name: docker-socket
hostPath:
path: /var/run/docker.sock
- name: gitea-runner-arm64-pvc
persistentVolumeClaim:
claimName: gitea-runner-arm64-pvc
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- nexus
+117
View File
@@ -0,0 +1,117 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: adguardsync-pvc
namespace: dns
annotations:
nfs.io/storage-path: "adguardsync-config"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: adguardsync
namespace: dns
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: adguardsync
template:
metadata:
labels:
app: adguardsync
spec:
containers:
- name: adguardsync
image: ghcr.io/bakito/adguardhome-sync:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
protocol: TCP
name: web-port
env:
- name: CRON
value: "0 * * * *"
- name: RUN_ON_START
value: "true"
- name: LOG_LEVEL
value: "info"
- name: ORIGIN_URL
value: "http://adguard.haven"
- name: ORIGIN_USERNAME
valueFrom:
secretKeyRef:
name: adguardhome-password
key: username
- name: ORIGIN_PASSWORD
valueFrom:
secretKeyRef:
name: adguardhome-password
key: password
- name: REPLICA1_URL
value: "http://adguard2.haven"
- name: REPLICA1_USERNAME
valueFrom:
secretKeyRef:
name: adguardhome-password
key: username
- name: REPLICA1_PASSWORD
valueFrom:
secretKeyRef:
name: adguardhome-password
key: password
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: adguardsync-storage
mountPath: /config
volumes:
- name: adguardsync-storage
persistentVolumeClaim:
claimName: adguardsync-pvc
---
apiVersion: v1
kind: Service
metadata:
name: adguardsync-svc
namespace: dns
spec:
type: ClusterIP
selector:
app: adguardsync
ports:
- name: web
port: 8080
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: adguardsync-ingress
namespace: dns
spec:
rules:
- host: adguardsync.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: adguardsync-svc
port:
number: 8080
+171
View File
@@ -0,0 +1,171 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: adguardhome-pvc
namespace: dns
annotations:
nfs.io/storage-path: "adguardhome-config"
spec:
storageClassName: "nfs-client"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: adguardhome
namespace: dns
spec:
strategy:
type: Recreate
replicas: 1
selector:
matchLabels:
app: adguardhome
template:
metadata:
labels:
app: adguardhome
spec:
containers:
- name: adguardhome
image: adguard/adguardhome:latest
imagePullPolicy: Always
ports:
- containerPort: 53
protocol: TCP
- containerPort: 53
protocol: UDP
- containerPort: 3000
protocol: TCP
name: install-port
- containerPort: 80
protocol: TCP
name: web-port
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 1000m
memory: 2Gi
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
volumeMounts:
- name: adguardhome-storage
mountPath: /opt/adguardhome/work
- name: adguardhome-storage
mountPath: /opt/adguardhome/conf
volumes:
- name: adguardhome-storage
persistentVolumeClaim:
claimName: adguardhome-pvc
---
apiVersion: v1
kind: Service
metadata:
name: adguardhome-svc
namespace: dns
spec:
type: LoadBalancer
selector:
app: adguardhome
loadBalancerIP: 192.168.20.200
ports:
- name: dns-tcp
port: 53
targetPort: 53
protocol: TCP
- name: dns-udp
port: 53
targetPort: 53
protocol: UDP
- name: web
port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: adguardhome-lan-svc
namespace: dns
spec:
type: LoadBalancer
selector:
app: adguardhome
loadBalancerIP: 192.168.15.200
ports:
- name: dns-tcp
port: 53
targetPort: 53
protocol: TCP
- name: dns-udp
port: 53
targetPort: 53
protocol: UDP
- name: web
port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: adguard-install-svc
namespace: dns
spec:
type: ClusterIP
selector:
app: adguardhome
ports:
- name: install
port: 3000
targetPort: 3000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: adguardhome-ingress
namespace: dns
spec:
rules:
- host: adguard.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: adguardhome-svc
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: adguardhome-install-ingress
namespace: dns
spec:
rules:
- host: install.adguard.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: adguard-install-svc
port:
number: 3000
+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
+96
View File
@@ -0,0 +1,96 @@
---
# 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:
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
+112
View File
@@ -0,0 +1,112 @@
---
# 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:
rules:
- host: code-config.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: code-config
port:
number: 8443
+26
View File
@@ -0,0 +1,26 @@
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: csi-driver-nfs
namespace: infra
spec:
repo: https://kubernetes-csi.github.io/csi-driver-nfs
chart: csi-driver-nfs
version: 4.13.4
targetNamespace: infra
valuesContent: |-
controller:
replicas: 1
logLevel: 5
defaultOnDeletePolicy: retain
storageClasses:
- name: nfs-client
annotations:
storageclass.kubernetes.io/is-default-class: "true"
parameters:
server: nfs-config.haven
share: /export/config
subDir: ${pvc.metadata.namespace}/${pvc.metadata.name}
onDelete: retain
reclaimPolicy: Retain
volumeBindingMode: Immediate
+11
View File
@@ -0,0 +1,11 @@
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: external-secrets
namespace: kube-system
spec:
repo: https://charts.external-secrets.io
chart: external-secrets
version: 2.7.0
targetNamespace: external-secrets
createNamespace: true
+128
View File
@@ -0,0 +1,128 @@
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:
rules:
- host: file-nginx.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: file-nginx
port:
number: 80
+85
View File
@@ -0,0 +1,85 @@
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:
rules:
- host: notify.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: haven-notify
port:
number: 8080
+32
View File
@@ -0,0 +1,32 @@
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: traefik
namespace: kube-system
spec:
repo: https://traefik.github.io/charts
chart: traefik
version: 41.2.0
targetNamespace: traefik
createNamespace: true
valuesContent: |-
deployment:
replicas: 2
ingressClass:
enabled: true
isDefaultClass: true
name: traefik
service:
annotations:
metallb.io/ip-allocated-from-pool: default-pool
metallb.io/loadBalancerIPs: "192.168.20.204"
spec:
type: LoadBalancer
externalTrafficPolicy: Local
resources:
requests:
cpu: 100m
memory: 90Mi
limits:
cpu: 1000m
memory: 256Mi
+126
View File
@@ -0,0 +1,126 @@
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:
rules:
- host: vpn.haven
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wg-easy-svc
port:
number: 51821
+10
View File
@@ -0,0 +1,10 @@
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: metallb
namespace: kube-system
spec:
repo: https://metallb.github.io/metallb
chart: metallb
version: 0.15.2
targetNamespace: metallb-system
+125
View File
@@ -0,0 +1,125 @@
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:
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:
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
+9
View File
@@ -35,3 +35,12 @@ spec:
automated: automated:
prune: true prune: true
selfHeal: true selfHeal: true
templatePatch: |
{{- if hasKey . "chart" }}
spec:
source:
$patch: replace
{{- toYaml .spec.source | nindent 4 }}
syncPolicy:
syncOptions: [CreateNamespace=true, ServerSideApply=true]
{{- end }}
+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
@@ -0,0 +1,23 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
labels:
app.kubernetes.io/name: argocd-cm
app.kubernetes.io/part-of: argocd
data:
accounts.api: apiKey
accounts.api.enabled: "true"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
labels:
app.kubernetes.io/name: argocd-rbac-cm
app.kubernetes.io/part-of: argocd
data:
policy.csv: |
g, api, role:readonly
-1
View File
@@ -4,7 +4,6 @@ metadata:
name: argocd-server name: argocd-server
namespace: argocd namespace: argocd
spec: spec:
ingressClassName: nginx
rules: rules:
- host: argocd.haven - host: argocd.haven
http: http:
@@ -9,3 +9,4 @@ resources:
patches: patches:
- path: server-insecure-patch.yaml - path: server-insecure-patch.yaml
- path: argocd-api-user.yaml
-7
View File
@@ -70,13 +70,6 @@ kind: Namespace
metadata: metadata:
name: metallb-system name: metallb-system
--- ---
apiVersion: v1
kind: Namespace
metadata:
name: nfs-provisioner
labels:
name: nfs-provisioner
---
# ============================================================================== # ==============================================================================
# Observability & Monitoring # Observability & Monitoring
# ============================================================================== # ==============================================================================
+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 } }
+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 } }
-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