Compare commits

...
58 Commits
Author SHA1 Message Date
azykov@mail.ru d66fef8f2b jitsi fixed passwords 2026-11-04 14:25:37 +03:00
azykov@mail.ru 9865f5b386 dedicated metallb pool 10.0.64.9 for jitsi 2026-11-04 13:59:34 +03:00
azykov@mail.ru 5ccf1ffffa added public ip to jitsui 2026-11-04 13:02:23 +03:00
azykov@mail.ru 6ca5c7b396 jitsi server 2026-11-04 12:59:48 +03:00
azykov@mail.ru 73e25266df generous retries for ntfy and map_monitor 2026-10-26 14:41:05 +03:00
azykov@mail.ru 0050dbc3af map_monitor 2026-10-26 14:36:42 +03:00
azykov@mail.ru 9505761c9b ntfy acl fix 2026-10-26 12:45:02 +03:00
azykov@mail.ru 224fffe7d2 added ha user to ntfy 2026-10-26 11:50:48 +03:00
azykov@mail.ru 815e29ed16 fixed ntfy NTFY_AUTH_ACCESS error 2026-10-26 11:30:44 +03:00
azykov@mail.ru 2ae3c26880 ntfy image name typo fixed 2026-10-26 11:20:10 +03:00
azykov@mail.ru c5db75828a ntfy users via env+secret 2026-10-26 11:15:14 +03:00
azykov@mail.ru 66a5d976bf ntfy bump to 2.34.0 2026-10-26 11:14:52 +03:00
azykov@mail.ru a1a9dfdcf4 ntfy 2026-10-26 10:53:42 +03:00
azykov@mail.ru 546212679f replaced it-tools with next-tools fork 2026-10-20 14:06:58 +03:00
azykov@mail.ru 8cf2b66fa1 it-tools app 2026-10-20 13:53:12 +03:00
azykov@mail.ru a27119e04b fixed homeassistant manifest error 2026-10-20 12:21:11 +03:00
azykov@mail.ru 51cf68683d moved all apps from authelia to tinyauth 2026-10-20 12:20:39 +03:00
azykov@mail.ru e0e4232bad longhorn-frontend switched to tinyauth 2026-10-20 12:09:11 +03:00
azykov@mail.ru fce161e13e tinyauth oidc clients and keys 2026-10-20 12:05:05 +03:00
azykov@mail.ru e9b0ae25c7 tinyauth background image replaced 2026-10-20 11:11:58 +03:00
azykov@mail.ru a243ccccdb tinyauth 2026-10-20 10:20:11 +03:00
azykov@mail.ru a29ecfdfa6 make HA claim fix on empty cluster also 2026-10-20 08:51:10 +03:00
azykov@mail.ru dd9c364860 HA pvc name fix 2026-10-20 08:45:23 +03:00
azykov@mail.ru 57ed781366 media dir for HA 2026-10-19 21:12:44 +03:00
azykov@mail.ru b992f47264 longhorn staleReplicaTimeout 2880 2026-10-13 18:06:46 +03:00
azykov@mail.ru d909f3e317 custom longhorn storage class 2026-10-13 14:58:00 +03:00
azykov@mail.ru 406678fdc2 gitea ALLOWED_HOST_LIST to ci.aggtaa.com 2026-10-08 11:16:48 +03:00
azykov@mail.ru dedba652d6 zigbee2mqtt experimental_event_entities:true flag in config map 2026-10-07 22:54:25 +03:00
azykov@mail.ru 1e0908ee90 woodpecker grpc key and persistent agent id 2026-10-07 11:40:34 +03:00
azykov@mail.ru ecf03f2b01 woodpecker automountServiceAccountToken and bump to 3.18.1 2026-10-07 11:31:00 +03:00
azykov@mail.ru 44b33f8030 woodpecker errors fixed 2026-10-07 11:26:41 +03:00
azykov@mail.ru a31372b953 woodpecker ci 2026-10-07 11:23:22 +03:00
azykov@mail.ru b8c581eab8 dev.aggtaa.com/m path 2026-09-29 16:22:33 +03:00
azykov@mail.ru 1af1a3527d dev.aggtaa.com/api/admin path auth, prefix stip 2026-09-29 13:17:55 +03:00
azykov@mail.ru 954a0b4000 dev.aggtaa.com, /, /api, /m to 10.0.64.157 2026-09-29 12:50:05 +03:00
azykov@mail.ru 8494263a3f 1 year retries for NAS mounts on webdav 2026-09-28 10:29:49 +03:00
azykov@mail.ru 050f86e503 webdav HostToContainer mountPropagation 2026-09-28 10:19:10 +03:00
azykov@mail.ru 06d92a034f soft nfs mounts for NAS shares 2026-09-18 10:59:04 +03:00
azykov@mail.ru 60fd88b9c1 authelia image name fix 2026-09-14 22:02:44 +03:00
azykov@mail.ru cbbe199633 longhorn generous readiness probe 5s timeout 2026-09-07 09:46:09 +03:00
azykov@mail.ru a10942bb3c gitea ssh address changed to git-ssh.aggtaa.com. flux points to the new in-cluster gitea 2026-09-07 09:36:09 +03:00
azykov@mail.ru 454922b475 gitea port 2222 2026-09-05 22:31:49 +03:00
azykov@mail.ru 4623c7c9f7 gitea config path fix 2026-09-05 16:03:44 +03:00
azykov@mail.ru f2045463c4 gitea ready to start, replicas = 1 2026-09-05 15:52:51 +03:00
azykov@mail.ru 891727b487 gitea 2026-09-05 14:40:02 +03:00
azykov@mail.ru 67284c4d32 openwebui removed 2026-08-28 08:41:30 +03:00
azykov@mail.ru 852ca6c8f3 empty joplin 2026-08-27 20:05:27 +03:00
azykov@mail.ru 489439d330 joplin 2026-08-27 19:37:51 +03:00
azykov@mail.ru 5cabacee84 separate keys per app 2026-08-25 21:25:40 +03:00
azykov@mail.ru 07fd7110e9 fix for photo-stats remnants not cleaned up 2026-08-25 16:53:58 +03:00
azykov@mail.ru 2d78870b94 more generous liveness probe for metallb 2026-08-25 12:18:56 +03:00
azykov@mail.ru a6c72aa3bc cpu & memory limits for st-backup-proxy 2026-08-25 12:18:33 +03:00
azykov@mail.ru 3b833cc575 immich data-old folder deleted 2026-08-24 10:03:11 +03:00
azykov@mail.ru 6600fa660e immich app is back on after folder split 2026-08-24 09:51:28 +03:00
azykov@mail.ru f59b01e290 prepare immich for data volume split 2026-08-23 21:13:49 +03:00
azykov@mail.ru 1df90d3a6e md-vault-mcd longer startup probe 2026-08-23 17:52:22 +03:00
azykov@mail.ru 90014b22c1 fix for authelia and md-vault-mcp image versions 2026-08-23 17:45:35 +03:00
azykov@mail.ru cc7eabfedc fix for mosquitto and fava image versions 2026-08-23 17:39:19 +03:00
109 changed files with 2553 additions and 213 deletions
+3 -1
View File
@@ -1,3 +1,5 @@
migration-from-old-files/ migration-from-old-files/
migration-from-docker/ migration-from-docker/
.decrypted~secret.enc.yaml .decrypted~secret.enc.yaml
gitea-backup/
*.agekey
+50 -2
View File
@@ -1,4 +1,52 @@
creation_rules: creation_rules:
- path_regex: .*\.enc\.yaml$ - path_regex: authelia.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
age: age14x4jjs2fwdgw28g2pkr6jyj2yzmwc544xwmaaglrxwz5e99seutqtr8jv5 age: age1ncw6uagcgncd5m2admqxt73ey0mcd99jsw9sa9lcdv3gu70mcerql8ya0k
- path_regex: davical.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1yx8mey02c0mu6te9hwxwdqp5n4enjz35e8thchsmvhqw7xsrzy0syg367g
- path_regex: immich.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1wmm8qrqnjzvd5nkraed6ylrsxlpn3ru7d7zettdudj7y9sg9dd2su0u3mr
- path_regex: markdown-vault-mcp.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1kct5g7d7eus2hvdnmx99ewsrtz70lhlasxa25euqwdctqhwqqpxqz0n7gg
- path_regex: mosquitto.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1u236u5mzkzj72y4mfxx68gfqw0u0j6tdgkudnm5pdfpe7apq4u6s89jr2h
- path_regex: vaultwarden.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age109zta6l0cwpkxn5wek82kvauxn6yqrq80rsz4mvu5atdnpcn9pusuvnqk5
- path_regex: webdav.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1y8t9v56yuvskznddqfsd4yddzzch0yf2dutvvt8rrx6rjh5f9g0s3qeays
- path_regex: zigbee2mqtt.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1tu4xgfefvgdda0vn2thaqndq4p9tuydq2ud6qa98tzzgnff6749qnlcx29
- path_regex: longhorn-system.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age18hhmdmupj90zjgjpxk3z5cf3lx36hcznnmqsp0fpyg50823pudrqjupydt
- path_regex: s3-backup-proxy.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age15vc3geq4lsec69rnxczluvwgcexeqae02zxzjpswsuatthaasu7qzath48
- path_regex: joplin.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1dertyl956fjsmh00cw468gdnl7lf3frasg4n4c82z0chm6qm2cks6w8gha
- path_regex: gitea.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1ktlxm0hukw6jxm62txqy5x2nl8hpdnal8nv00kfhrf3pw33574aqa6j5cj
- path_regex: woodpecker.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age13qwkssk8esashln2j3gfv6e9csashr4vddkdav5v38hm5wejl36qd2ew9s
- path_regex: tinyauth.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1rvyt7nlnjqeyt6ykwh9fj9d88kjmq9vetcx667ul3ql6fyfz4gqqh9lq9q
- path_regex: ntfy.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1uzs33ep6k3htnuwyyp54y373wkf7urd0qvk5kxcmptr5tt0fffus4v06gn
- path_regex: map_monitor.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1k4j5vyqqt7w7k52gvkpfe6znqr9mlgt076l7xgdtyc75lamsh4aqshj5qs
- path_regex: jitsi.*\.enc\.yaml$
encrypted_regex: ^(data|stringData)$
age: age1zmr32r5mcgyh73asvzl8l690pzwqsx9hle27u3cydngsgv56wykq3v67px
+1 -1
View File
@@ -17,7 +17,7 @@ spec:
app: app:
image: image:
repository: authelia/authelia repository: authelia/authelia
tag: v4.39.20 tag: 4.39.21
probes: probes:
liveness: liveness:
enabled: true enabled: true
File diff suppressed because one or more lines are too long
+10 -10
View File
@@ -4,21 +4,21 @@ metadata:
name: davical-secrets name: davical-secrets
namespace: pim namespace: pim
stringData: stringData:
PGSQL_ROOT_PASS: ENC[AES256_GCM,data:EwTwpAg3FBcZoMDLonNAFjDPDO8wgjwx0aQm85n7BkCTkV6c5JX7tF7TIX4HW4RB,iv:rVeHe4suCagHl1V1EFG3FOfL45tdT1cdr3GyGcHxHu4=,tag:vlJn7lJWhGxKBgZr6XDHPA==,type:str] PGSQL_ROOT_PASS: ENC[AES256_GCM,data:XEhP2zDIz0QCpAXXnZi5JtPsVIfns9zp4fvxcg9/bskHiLU5rR+EgU5n/SdOEDkJ,iv:wR3CGk3csHSCSBqFX+ukOUAmS/p+oZMmdNgvCFNHZlI=,tag:ttAe0GuLmwqA77cWLlne2Q==,type:str]
PASSDAVDB: ENC[AES256_GCM,data:MqEb6sAZc8sPRYq9rB+ESV9UFMGmH3VFRW3nUYPpGp/umAyTJxqc59LzBavYtcZ7,iv:sGw/1HmYvKSizczpDiLZ4zWGLB1vfB+NwfycwOEsZSk=,tag:wxbrjiMSnJssH3o9FOPOXQ==,type:str] PASSDAVDB: ENC[AES256_GCM,data:d9RazQG7xeIH4g3ovti6HwyiIyLTzyaPjAcQvLmhsrwdgev7i0AJK7yQeRXxu/16,iv:9ZdfWTlOuyUNpV2ycykOgx8SY7Golz7/Lb+gfTa9fW0=,tag:eL1euF9uQJ+nDLsR1kxtvg==,type:str]
ADMINDAVICALPASS: ENC[AES256_GCM,data:Mx3z8QUCWasAqqoUF/FOsT11iBAGtHNrYI/iMdkIRhc=,iv:kBnQjC5VvDolB1Uwe3DDzbfTTU3lHbziXR5X3bQRXvU=,tag:yP74K66C5Np1fu2DmJN04w==,type:str] ADMINDAVICALPASS: ENC[AES256_GCM,data:1Zq7duPTIfJX2Stp6AEhKcLHkmQB6vWf4y1f0Iju/zI=,iv:PMOyD3O7UzVPRlFmHTOQSAys032iD4CF8JbI51agWcg=,tag:eDgMbg2lRMnl57NCf5iIkw==,type:str]
sops: sops:
age: age:
- recipient: age1yx8mey02c0mu6te9hwxwdqp5n4enjz35e8thchsmvhqw7xsrzy0syg367g - recipient: age1yx8mey02c0mu6te9hwxwdqp5n4enjz35e8thchsmvhqw7xsrzy0syg367g
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1T2dJTmlMWXQrWldYM3lZ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjNTVJZEFuRFhGazF6T1Yv
SDNTZjZ0NnpDNklCalR6dm9oNHU2Z2REczBnCjJhZkFaU1dsZHZlRktVbjVOZStP dnhhTHVUb1h4SEN2eEtDUGREUERkR2wrTUVVCktGSkVLZmlYR3BjekhjNmk4L3Q4
b3hPN1BMNjJMUDNFa3REV0RNUkhVS2cKLS0tIEVUWUhtWU1UWXcwWU5RYTY1M2Zj UEJsQXJjZXY1VlNDaFV2eER4NTdwRGcKLS0tIDJxb1AyYm01YlNUVk93Tm1Hb0xK
a0hUaksyZW1KV2Jvd2RWM04ramJjNGMK6qIWzTHhAMJRNtIU+t/QR6iZsVN05bOv NVh0K25yOFUya0dYSGt2SGdJQURnQ2sKaKgwThB5ePeM091/zYgQXmiPeBeIhTdo
CX8233dl+T76qrrfYV5+EQ3AKCDnIIkU6Mg1jdnaaorTpfo/qOAHgQ== 60PkAPNs659xyVqnlZw2YKEhxbEHS3ZMTBgC4tgng0yjClBizcTv5w==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-26T10:29:45Z" lastmodified: "2026-08-08T16:37:01Z"
mac: ENC[AES256_GCM,data:FtJ1YtmHAj9FIMDILjm5R2ZO3CElJN3j6uAZkw9z4Odu6GaIhPfeAp0HOdgvu/NiU1LC4K/GGPZ+NdEbOLgAtLKW22TBG2iCnuS1PUguogI8IG8+rpGbuatJhn9ntkEZFfMQqPSxKK4DijIj5YrNvezHTJPvKyxY5R6pcegV8h4=,iv:+aLt4+soEa8+AgGiHPG1+87Mhvjs8lxVcdiChUqg1mo=,tag:DHt5lXFDx9911k7MpI8ZrA==,type:str] mac: ENC[AES256_GCM,data:OoE6fml/3DSH2EMafCoeYz/JHo89Fb9uwQ58k7hxQDU6YMazdO/eGM0jKX5ghjuMKJqA/zQs4FeZ4yASkIUT1CBbxRpycmE9qo6Zny121NLeqveBp3YMjmUZGt3tgw2dNqtkhpOnl/BmHQCtTTvX2brtSVmANx3LbFxjqbK2OGM=,iv:8Us7LJSZ2aHZQnYIjnsvkR7Av0KeHFEYEETZd7WZX4Q=,tag:7NbbEOq1k2c5IcsnqpvlOQ==,type:str]
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
version: 3.11.0 version: 3.11.0
+104
View File
@@ -0,0 +1,104 @@
# Three Ingresses on one host because middlewares and router priority are
# per-Ingress. Traefik matches pathType Prefix character-wise (not per path
# segment), so public prefixes carry a trailing slash and priorities are
# explicit - by default Traefik orders routers by rule length, which would
# not reliably put /api/admin above /api/.
# 300 dev-admin /api/admin -> Authelia -> api (prefix /api stripped)
# 200 dev-public /api/ /m /m/ -> api / m, public (prefixes stripped)
# 100 dev / -> Authelia -> ui (also bare /api)
# All carry the tls section (otherwise the router is http-only), but only
# "dev" has the cert-manager annotation so a single Certificate owns dev-tls.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dev
namespace: dev
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.priority: "100"
traefik.ingress.kubernetes.io/router.middlewares: auth-tinyauth@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: dev.aggtaa.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dev
port:
name: ui
tls:
- secretName: dev-tls
hosts:
- dev.aggtaa.com
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dev-admin
namespace: dev
annotations:
traefik.ingress.kubernetes.io/router.priority: "300"
traefik.ingress.kubernetes.io/router.middlewares: auth-tinyauth@kubernetescrd,dev-strip-prefix@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: dev.aggtaa.com
http:
paths:
# no trailing slash on purpose: also catches bare /api/admin
# (and /api/administrator...), all of which stay behind Authelia
- path: /api/admin
pathType: Prefix
backend:
service:
name: dev
port:
name: api
tls:
- secretName: dev-tls
hosts:
- dev.aggtaa.com
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dev-public
namespace: dev
annotations:
traefik.ingress.kubernetes.io/router.priority: "200"
traefik.ingress.kubernetes.io/router.middlewares: dev-strip-prefix@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: dev.aggtaa.com
http:
paths:
- path: /api/
pathType: Prefix
backend:
service:
name: dev
port:
name: api
- path: /m
pathType: Exact
backend:
service:
name: dev
port:
name: m
- path: /m/
pathType: Prefix
backend:
service:
name: dev
port:
name: m
tls:
- secretName: dev-tls
hosts:
- dev.aggtaa.com
@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- service.yaml
- middleware.yaml
- ingress.yaml
+13
View File
@@ -0,0 +1,13 @@
# Shared by dev-admin and dev-public: /api/a -> /a, /api/admin/b -> /admin/b,
# /m/main.css -> /main.css. Only the first matching prefix is stripped, and
# none of these routes can match both.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-prefix
namespace: dev
spec:
stripPrefix:
prefixes:
- /api
- /m
+40
View File
@@ -0,0 +1,40 @@
# dev.aggtaa.com backends live outside the cluster on 10.0.64.157:
# selector-less Service + hand-written EndpointSlice point at them.
apiVersion: v1
kind: Service
metadata:
name: dev
namespace: dev
spec:
ports:
- name: ui
port: 10001
- name: api
port: 10002
- name: m
port: 10003
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: dev-external
namespace: dev
labels:
kubernetes.io/service-name: dev
endpointslice.kubernetes.io/managed-by: flux
addressType: IPv4
ports:
- name: ui
port: 10001
protocol: TCP
- name: api
port: 10002
protocol: TCP
- name: m
port: 10003
protocol: TCP
endpoints:
- addresses:
- 10.0.64.157
conditions:
ready: true
+2 -2
View File
@@ -29,7 +29,7 @@ spec:
app: app:
image: image:
repository: yegle/fava repository: yegle/fava
tag: "1.30.4" tag: "v1.30"
env: env:
TZ: Europe/Moscow TZ: Europe/Moscow
BEANCOUNT_FILE: /documents/main.beancount BEANCOUNT_FILE: /documents/main.beancount
@@ -59,7 +59,7 @@ spec:
className: traefik className: traefik
annotations: annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: auth-authelia@kubernetescrd traefik.ingress.kubernetes.io/router.middlewares: auth-tinyauth@kubernetescrd
hosts: hosts:
- host: acc.aggtaa.com - host: acc.aggtaa.com
paths: paths:
+105
View File
@@ -0,0 +1,105 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: gitea
namespace: git
spec:
interval: 10m
chartRef:
kind: OCIRepository
name: gitea
driftDetection:
mode: enabled
values:
controllers:
app:
replicas: 1
containers:
app:
image:
repository: gitea/gitea
tag: "1.27.0"
env:
USER_UID: "1000"
USER_GID: "1000"
GITEA__server__DOMAIN: git.aggtaa.com
GITEA__server__ROOT_URL: https://git.aggtaa.com/
GITEA__server__SSH_DOMAIN: git-ssh.aggtaa.com
GITEA__server__SSH_PORT: "2222"
GITEA__server__LFS_START_SERVER: "true"
GITEA__webhook__ALLOWED_HOST_LIST: ci.aggtaa.com
GITEA__security__INSTALL_LOCK: "true"
GITEA__security__PASSWORD_HASH_ALGO: pbkdf2
GITEA__security__INTERNAL_TOKEN:
valueFrom:
secretKeyRef:
name: gitea-secrets
key: INTERNAL_TOKEN
GITEA__oauth2__JWT_SECRET:
valueFrom:
secretKeyRef:
name: gitea-secrets
key: OAUTH2_JWT_SECRET
GITEA__server__LFS_JWT_SECRET:
valueFrom:
secretKeyRef:
name: gitea-secrets
key: LFS_JWT_SECRET
probes:
startup:
enabled: true
spec:
periodSeconds: 5
failureThreshold: 60
liveness:
enabled: true
readiness:
enabled: true
resources:
requests:
cpu: 50m
memory: 256Mi
limits:
memory: 1Gi
service:
app:
controller: app
ports:
http:
port: 3000
ssh:
controller: app
type: LoadBalancer
labels:
loadBalancerPool: default
ports:
ssh:
port: 2222
targetPort: 22
protocol: TCP
ingress:
app:
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: git.aggtaa.com
paths:
- path: /
pathType: Prefix
service:
identifier: app
port: http
tls:
- secretName: gitea-tls
hosts:
- git.aggtaa.com
persistence:
data:
type: persistentVolumeClaim
existingClaim: gitea-data
globalMounts:
- path: /data
@@ -0,0 +1,10 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: gitea
namespace: git
spec:
interval: 10m
url: oci://ghcr.io/bjw-s-labs/helm/app-template
ref:
tag: "5.0.1"
@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- helmrepository.yaml
- secret.enc.yaml
- pvc.yaml
- helmrelease.yaml
+12
View File
@@ -0,0 +1,12 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitea-data
namespace: git
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 3Gi
+24
View File
@@ -0,0 +1,24 @@
apiVersion: v1
kind: Secret
metadata:
name: gitea-secrets
namespace: git
stringData:
INTERNAL_TOKEN: ENC[AES256_GCM,data:+eS1bjyL/NKvOpkXa1Kiyk9dBPemWvfRplJJCXG+cLXipbMlyHBDQLEMeFEnz9VdnwN/MFRWYL5ink3Lh6yNGf0vmVNR+OLMlvlaKDNaCA28HuUBxJbWLAqhITsRImH6rKcy/EfqEEcK,iv:ModTSwxLMj4Gx0e/bVKY8GVWzBjgNxATKnkWYfvKiIs=,tag:6gFkA/0toCBJ7QKL16654A==,type:str]
OAUTH2_JWT_SECRET: ENC[AES256_GCM,data:9626EzmbEmyAsTqfbRhASqLkzS+4xnJxpSEgJDSLzkjfuNfAq7uMfc/HsA==,iv:lMCRwHt4gT31hYZ5Ywt4GU/DJNj7Fya2GOUkyr/l6ro=,tag:aQ8UlVwpaVNDoOHju1oiMQ==,type:str]
LFS_JWT_SECRET: ENC[AES256_GCM,data:gZ37cb9uqSF1MkdoUFbVM3VhqBxEIyUv3IxiV4c3gbIVTk7FMHncZekM7Q==,iv:m7JxF7S1OnHy86s5s1tJHQ8kqhRJq5kFRoArLL/ab0o=,tag:GZuE7f9pNq15T+R+PlSZdA==,type:str]
sops:
age:
- recipient: age1ktlxm0hukw6jxm62txqy5x2nl8hpdnal8nv00kfhrf3pw33574aqa6j5cj
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkRVpZRTVLWU80RjEvZnRs
TU5YVko4V1M5Q0d5SXZGT1h2NStCYk5UZWpRCmNxVXVtR3VCZXJqNHYyaDUyZmdr
WXJKZHhJaFJhZll0VVBld0QrSGhtbncKLS0tIGZZREFXRUMvTi8rRHIyaXBUOE45
d2JBTG5oT2kvZzdLejMzdEpRbm03QmcKItsS4nKJRu3pRC8xLTf9kYpqFiD9S1tx
BFnJf3IsbGpl1RM8RmkN9O5QsZSq1hbw5pFhlx0XKeH5KgoJInbnog==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-09-05T11:29:22Z"
mac: ENC[AES256_GCM,data:906TL61C7WcxxTAfH3zpXLTLQn2WSIg2RPm0y/6LeVA6pvI6fMbFA32Usvg+EkpWVRGqcm8JPgHy63WQDhq1Ap3imJXpgVDgTgfo64TxJLmdUtCAdm0/CaGxtGeU2J3gm653wl75hOGoCyfBR3iOt0Vo1kUdUxS9B5h3u5L6P6o=,iv:C+CZe31F/AWk0ZqM6lgV248ys/L3mG6S8Tz44b5o628=,tag:C9LdYCtsiHWiHSZWaYCGWA==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.11.0
@@ -75,3 +75,11 @@ spec:
retain: true retain: true
globalMounts: globalMounts:
- path: /config - path: /config
media:
type: persistentVolumeClaim
accessMode: ReadWriteOnce
size: 1Gi
storageClass: longhorn
retain: true
globalMounts:
- path: /media
@@ -189,15 +189,47 @@ spec:
server: server:
mergerfs: mergerfs:
- path: /dev/fuse - path: /dev/fuse
# everything immich keeps outside "library" (backups, encoded-video, # split out of the old combined "other" volume below: each of these
# profile, thumbs, upload) - plain volume, no mergerfs involved. # purposes now gets its own Longhorn volume instead of sharing one
other: # 200Gi disk, so a single hot subtree (e.g. thumbs) doesn't force
# replica rebuilds/relocation to drag the whole 200Gi with it.
data:
type: persistentVolumeClaim type: persistentVolumeClaim
accessMode: ReadWriteOnce accessMode: ReadWriteOnce
size: 200Gi size: 20Gi
storageClass: longhorn-single-replica storageClass: longhorn
retain: true retain: true
advancedMounts: advancedMounts:
server: server:
app: app:
- path: /data - path: /data
encoded-video:
type: persistentVolumeClaim
accessMode: ReadWriteOnce
size: 100Gi
storageClass: longhorn
retain: true
advancedMounts:
server:
app:
- path: /data/encoded-video
thumbs:
type: persistentVolumeClaim
accessMode: ReadWriteOnce
size: 100Gi
storageClass: longhorn
retain: true
advancedMounts:
server:
app:
- path: /data/thumbs
backups:
type: persistentVolumeClaim
accessMode: ReadWriteOnce
size: 30Gi
storageClass: longhorn
retain: true
advancedMounts:
server:
app:
- path: /data/backups
+8 -8
View File
@@ -4,19 +4,19 @@ metadata:
name: immich-secrets name: immich-secrets
namespace: photo namespace: photo
stringData: stringData:
DB_PASSWORD: ENC[AES256_GCM,data:OumSCUdG9NT5FNCApJ9D04VIXgRf2X/ieCjJbWqGsc0=,iv:IAC9IXiVeOqFngqOqxReBPYNwPg3p/e4auHeUCudcS0=,tag:uhg56rWb5IzJApJm3tqDHA==,type:str] DB_PASSWORD: ENC[AES256_GCM,data:+bJ/+NEQCT3ZEqjnkzep4OsbLkJ9gyLZmGxCpoww2Wo=,iv:n1sLYgLl3zNZBdjMBhIrbSv/qHJFQ/nvZfBknFAUdug=,tag:DFnoc5K6XvHngVIrgJlM6Q==,type:str]
sops: sops:
age: age:
- recipient: age1wmm8qrqnjzvd5nkraed6ylrsxlpn3ru7d7zettdudj7y9sg9dd2su0u3mr - recipient: age1wmm8qrqnjzvd5nkraed6ylrsxlpn3ru7d7zettdudj7y9sg9dd2su0u3mr
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqTnlDR1dRUDNaTlQ2RHpM YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtd1hFZy82d0c0RXZMd28r
OFArT3BjOEhleXhVaTRlVDRPcFF1ZFpyakR3Ck1BUHhYQXdsTXMveGs2OG9mbzFQ dWs1VWNlVG9tTE5WZDVLdGpFSXhuVXQ5anprCk5LWWMrWUVCd1N4Y1JENnRyWExC
QWR6dW4zL2VsQWtDNWgrVVBDeWd4L1kKLS0tIDY3WHo1MEl4SzJKM0ROalBKNERa d2VRNDJON2RzSHRyMGhRdmMva1NNY3cKLS0tIDNML2Q0YXFRMEs3V3prUkZYSS85
SlQwMEdnZmhSWUdYUnBGRk9MdjV0cGMKxhON65iPrpNMnqBYYUN6rQfhAo3EhhE4 MWRGa3duNHgvbjNvVnZLOXpYMW1mbGcKaN7h3W0YKsRjnyk6P9+bziDy6If5xLQ8
Vun3sqVBk1OH8aekutrYSOj/RhrIbzYlbFXTY+pDnp3U3o1+xrknig== kxFJf4CX9XU5jgYXqw2UNqRDusphclqfULUMRaEzMJkkPhd1cZ9PvQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-26T10:29:46Z" lastmodified: "2026-08-21T12:23:20Z"
mac: ENC[AES256_GCM,data:/CgR0m+7TJh2hHQ+pee5bP1lbJhWMrJiPjTrS8pP05z4OobLXxScEzPKtuO8+0Ru0eyjg/i1eAk5tzDMzDPwW3/JiU9elQKeFYxzGn6LmT17KIf+ofPP9vuVFP93FW4vqYNZIO4qUtF/RUfZBRgPlf6rokUMRLILTRGI00lvfR4=,iv:z9xX6wGL7I+NZzybQd0j4+rkU+Xb2JKO2sedrrGUss4=,tag:P5E3e/s8bXJBDUhxTqaf8g==,type:str] mac: ENC[AES256_GCM,data:vGSUhl73AIQr1Ld1SHc+i4GpvOc0b0j8Vx9DmoRDjNs17TQSfmfulEZwIua12P5DHIz36P9yqggHfSyyrrEoMjfaUSTIDxCIGGvsfTl4vBt9Vis0e2gALaHtAgWZwxr5QMe+OP1tkXOZoX9E8VcoBq1ULvbIjqsPs2uxPVKsEWk=,iv:1exDUqurlFdBt1eZYWWMlkar75HbTLZ6RZelC0RsYcU=,tag:A0IhJHjVum2HBT5RwHo5qg==,type:str]
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
version: 3.11.0 version: 3.11.0
@@ -0,0 +1,57 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: it-tools
namespace: tools
spec:
interval: 10m
chartRef:
kind: OCIRepository
name: it-tools
driftDetection:
mode: enabled
values:
controllers:
it-tools:
containers:
app:
image:
repository: ghcr.io/willjayyyy/next-tools
tag: 1.10.3
probes:
liveness:
enabled: true
readiness:
enabled: true
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 128Mi
service:
app:
controller: it-tools
ports:
http:
port: 80
ingress:
app:
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: auth-tinyauth@kubernetescrd
hosts:
- host: tools.aggtaa.com
paths:
- path: /
pathType: Prefix
service:
identifier: app
port: http
tls:
- secretName: it-tools-tls
hosts:
- tools.aggtaa.com
@@ -0,0 +1,10 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: it-tools
namespace: tools
spec:
interval: 1h
url: oci://ghcr.io/bjw-s-labs/helm/app-template
ref:
tag: "5.0.1"
+77
View File
@@ -0,0 +1,77 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: jitsi
namespace: video
spec:
interval: 30m
chart:
spec:
chart: jitsi-meet
version: ">=1.0.0 <2.0.0"
sourceRef:
kind: HelmRepository
name: jitsi
install:
remediation:
retries: -1
upgrade:
remediation:
retries: 5
driftDetection:
mode: enabled
# fixed xmpp credentials: the chart generates random ones on every render, which
# desyncs jicofo/jvb from the already running prosody after an upgrade
valuesFrom:
- kind: Secret
name: jitsi-secrets
valuesKey: JICOFO_AUTH_PASSWORD
targetPath: jicofo.xmpp.password
- kind: Secret
name: jitsi-secrets
valuesKey: JICOFO_COMPONENT_SECRET
targetPath: jicofo.xmpp.componentSecret
- kind: Secret
name: jitsi-secrets
valuesKey: JVB_AUTH_PASSWORD
targetPath: jvb.xmpp.password
values:
publicURL: https://v.aggtaa.com
web:
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: v.aggtaa.com
paths: ["/"]
tls:
- secretName: jitsi-tls
hosts:
- v.aggtaa.com
# media traffic: UDP 10000 must be forwarded from the WAN router to the JVB service ip (kubectl get svc -n video)
jvb:
UDPPort: 10000
# wan ip first, then the service ip for clients on the lan (no hairpin nat needed);
# the chart passes this string as-is to JVB_ADVERTISE_IPS, which accepts a comma separated list
publicIPs:
- 91.233.45.43,10.0.64.9
service:
enabled: true
type: LoadBalancer
externalTrafficPolicy: Cluster
postRenderers:
- kustomize:
patches:
# dmz-pool has a single ip held by traefik, so use the dedicated public-ports-pool (see infra/metallb/config.yaml)
- target:
kind: Service
name: jitsi-jitsi-meet-jvb
patch: |
- op: add
path: /metadata/annotations/metallb.io~1address-pool
value: public-ports-pool
@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: jitsi
namespace: video
spec:
interval: 1h
url: https://jitsi-contrib.github.io/jitsi-helm/
@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- helmrepository.yaml
- helmrelease.yaml
- secret.enc.yaml
+24
View File
@@ -0,0 +1,24 @@
apiVersion: v1
kind: Secret
metadata:
name: jitsi-secrets
namespace: video
stringData:
JICOFO_AUTH_PASSWORD: ENC[AES256_GCM,data:OUEZWPlqpkuhEI8kf65kKUooeahC3z03STevw7oOWx8=,iv:KSfrwehZG9kW7bfzQ5yavq6gYj+TrYKS/LZPNzaZdLI=,tag:/FiUF75ib3RXPuzO0EhIpw==,type:str]
JICOFO_COMPONENT_SECRET: ENC[AES256_GCM,data:y8a84NdaClBRUFDPtXhulJxrX68Tn+4s1NrNqyhBjvI=,iv:fdqlgRwHNTdqegvfOrvX1Iph5aMKAtyW5zJUr+kBNx0=,tag:yNQ53KQQWafV7CSbNttJUQ==,type:str]
JVB_AUTH_PASSWORD: ENC[AES256_GCM,data:GWSHUDfEZmLbKzU02U+ImCR0nVIp5ebngWVKBOmjOjY=,iv:uG7/4QPD8Wd2Q6lqPgKTD533GdbkXjdtZqJnshmNzjI=,tag:jhj5L86v0ZFalyhfQOTgCA==,type:str]
sops:
age:
- recipient: age1zmr32r5mcgyh73asvzl8l690pzwqsx9hle27u3cydngsgv56wykq3v67px
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuSXZWTzloNTMzWk5samRU
amhURmFvdDlSMjd1RVFjUWxuWEtoVE13WUNJClptVUgxblRWL1RIYmZPZ2x2MG1H
Y3ovWWlwL2dLeW9xbndXWEMvQVZONGcKLS0tIGxZNDZkRDBkZ0NDVWxMNGxoUFR4
VG1wSm5wUWRVUEZ3cWlMb2lBNTVkZGcKAa4/S8EWBMt+uvT/6hYRl1GascDul+ox
qlSV/20eA9uamssuId63ewaO4hL7ekwKwqM5cAFH+wx2dno9Ajuang==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-11-04T11:21:36Z"
mac: ENC[AES256_GCM,data:r7iq6iTFL70Zat0sVw9nAHiueZNUWzV2j6q6POBUjeGLqn53ygJAXqG31REBLNKBwabLxsNlTmZRujpP6ySkbFve6fN1C6+6HFWdHnkeJx1nk4iJTvb5+Ld7Exp8yt6b46M/hHZxOffO23jVHuAa47hZ4lHphFpSrlgif79W/CI=,iv:bP9ll+tuTFOfh3kE7xiXtF0cH0sCfhaIs9fhNt0JoEU=,tag:hpBZBwRZxyAgn20aaGoGWA==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.11.0
@@ -0,0 +1,80 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: joplin
namespace: pim
spec:
interval: 10m
dependsOn:
- name: joplin-postgres
chartRef:
kind: OCIRepository
name: joplin
driftDetection:
mode: enabled
values:
controllers:
app:
containers:
app:
image:
repository: joplin/server
# verified via Docker Hub API (hub.docker.com/v2/repositories/
# joplin/server/tags): "3.7.1" and "latest" were pushed at the
# same timestamp, i.e. this is what "latest" (the original
# compose.yaml's tag) currently resolves to.
tag: "3.7.1"
env:
APP_PORT: "22300"
APP_BASE_URL: https://notes.aggtaa.com
DB_CLIENT: pg
POSTGRES_DATABASE: joplin
POSTGRES_USER: joplin
POSTGRES_HOST: joplin-postgres
POSTGRES_PORT: "5432"
POSTGRES_PASSWORD:
valueFrom:
secretKeyRef:
name: joplin-secrets
key: POSTGRES_PASSWORD
probes:
startup:
enabled: true
spec:
periodSeconds: 5
failureThreshold: 60
liveness:
enabled: true
readiness:
enabled: true
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 1Gi
service:
app:
controller: app
ports:
http:
port: 22300
ingress:
app:
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: notes.aggtaa.com
paths:
- path: /
pathType: Prefix
service:
identifier: app
port: http
tls:
- secretName: joplin-tls
hosts:
- notes.aggtaa.com
@@ -0,0 +1,50 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: joplin-postgres
namespace: pim
spec:
interval: 10m
chartRef:
kind: OCIRepository
name: joplin
driftDetection:
mode: enabled
values:
controllers:
postgres:
containers:
app:
image:
repository: postgres
# not alpine: joplin server needs a glibc-collation postgres,
# matching the official (Debian-based) image used everywhere
# else this pattern is followed in this repo.
tag: "16.15"
env:
TZ: Europe/Moscow
POSTGRES_USER: joplin
POSTGRES_DB: joplin
POSTGRES_PASSWORD:
valueFrom:
secretKeyRef:
name: joplin-secrets
key: POSTGRES_PASSWORD
service:
app:
controller: postgres
ports:
postgres:
port: 5432
persistence:
data:
type: persistentVolumeClaim
accessMode: ReadWriteOnce
size: 20Gi
storageClass: longhorn
retain: true
globalMounts:
- path: /var/lib/postgresql/data
subPath: data
@@ -0,0 +1,10 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: joplin
namespace: pim
spec:
interval: 10m
url: oci://ghcr.io/bjw-s-labs/helm/app-template
ref:
tag: "5.0.1"
@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- helmrepository.yaml
- secret.enc.yaml
- helmrelease-postgres.yaml
- helmrelease-joplin.yaml
+22
View File
@@ -0,0 +1,22 @@
apiVersion: v1
kind: Secret
metadata:
name: joplin-secrets
namespace: pim
stringData:
POSTGRES_PASSWORD: ENC[AES256_GCM,data:cI3zfdaJUbo2344RuLIG4DjPsgx4Afl6GKMJVHrOHOg=,iv:8QliTSQ44ScVUchDxh0DF/wU9bTLK9Jq4ojxSMXsNC8=,tag:VB2mf4uhLnRnafWg0mRRpg==,type:str]
sops:
age:
- recipient: age1dertyl956fjsmh00cw468gdnl7lf3frasg4n4c82z0chm6qm2cks6w8gha
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFQ2JKZWxseVAxUWU5NlhL
S0xWQUh1NFpBU2ZpQkJXbkg4TlZ2cTR5TXhjCjlMTGdGMEF4UzBEbS9wdndJb1VE
OXBOanpJS1lpTFVhWm52K2xhaGx2M00KLS0tIGF5b3FZOWRNVWNpenRmUDVOT3Vp
aHpCbXkvdXM3SjYxWTlsT0cxU2RDOFkKximxt9bmd6FZAtU/SBirzDH3NMitQKrZ
PfwicphmFEXVN+HtD9vwRbP4TMa1swOMbOVINGKuA8YZwEk1GyPe4w==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-27T10:16:36Z"
mac: ENC[AES256_GCM,data:85dGV8WPFqd+4l6k7PLqDROm5+kwiAjO80ElNKfSJFOgeI9y7DyhyhDklcBJXMJljatbEfAmCFFqnoyRGmeIjc6FBfteUGQtpLgI6e8RTlRHPvaRzqDwA1OhnlQbAW9TTf5vha6tyYso/PprCWjJSMjBEfiu7ApiRMdZcLd1V4c=,iv:Dh9z/r7UbhdREwXT787HkmVM04X/BAtQmQteN3j7AKU=,tag:oUMYT5CL85PAYfItf5TIdw==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.11.0
@@ -0,0 +1,63 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: map-monitor
namespace: iot
spec:
interval: 30m
chartRef:
kind: OCIRepository
name: map-monitor
install:
remediation:
retries: -1
upgrade:
remediation:
retries: 5
driftDetection:
mode: enabled
values:
defaultPodOptions:
securityContext:
runAsUser: 65534
runAsGroup: 65534
runAsNonRoot: true
controllers:
map-monitor:
containers:
app:
image:
repository: python
tag: 3.12-slim
command:
- sh
- -c
- pip install --no-cache-dir --target /tmp/site -r /app/requirements.txt && exec python /app/map_mqtt.py
env:
PYTHONPATH: /tmp/site
PYTHONUNBUFFERED: "1"
PYTHONDONTWRITEBYTECODE: "1"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
HOME: /tmp
envFrom:
- secretRef:
name: map-monitor-secrets
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 128Mi
persistence:
code:
type: configMap
name: map-monitor-code
globalMounts:
- path: /app
readOnly: true
tmp:
type: emptyDir
globalMounts:
- path: /tmp
@@ -0,0 +1,10 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: map-monitor
namespace: iot
spec:
interval: 1h
url: oci://ghcr.io/bjw-s-labs/helm/app-template
ref:
tag: "5.0.1"
@@ -0,0 +1,15 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- helmrepository.yaml
- secret.enc.yaml
- helmrelease.yaml
configMapGenerator:
- name: map-monitor-code
namespace: iot
files:
- src/map_mqtt.py
- src/map_read.py
- src/requirements.txt
generatorOptions:
disableNameSuffixHash: true
@@ -0,0 +1,36 @@
apiVersion: v1
kind: Secret
metadata:
name: map-monitor-secrets
namespace: iot
stringData:
SERIAL_HOST: ENC[AES256_GCM,data:6cCm6shCy2kymQ==,iv:mqUOG6RZ6HGh3dBWy2LdWWXklU2FFhra9Xi3VgFdQi4=,tag:sJq/S+QslUWxeuK1JgQuBA==,type:str]
SERIAL_PORT: ENC[AES256_GCM,data:ZiqkpB0=,iv:SryQv6eUrTZwA896oG83xHeLjzABqnn4pwlDPA56S3A=,tag:WPmz9uw+G+QQZW4L/Qfm1Q==,type:str]
SERIAL_POLL_INTERVAL: ENC[AES256_GCM,data:Cw==,iv:myGXUNhVKALsedj1Do56clisSiAvqE19rqTGQjtYxCI=,tag:XvTcCvI9B5YyizJ4skfB1A==,type:str]
SERIAL_TIMEOUT: ENC[AES256_GCM,data:mMtT,iv:nz7nJXuLU3tpO2l04+aRTczCwnmgubLOw7a/cg6Yjic=,tag:nMed9+RH6VKZDQURwuMd/g==,type:str]
MQTT_HOST: ENC[AES256_GCM,data:qyjHpa6fNG+IaQvj6LsUGF8iPxWU4NzdzPCt1ZcodFE=,iv:dxCP9gfasYovuBI6pxkynKkNAh99bUIEr8fn9Qbqxww=,tag:mz1BgruIsUXE/sJdiCqH6g==,type:str]
MQTT_PORT: ENC[AES256_GCM,data:PPxXlQ==,iv:s4b8gCGna5Va0B9BN02ZLlofYlIQiVn9KU8qJNECK5I=,tag:neXH/dwhfW1u1UfkRuY2mg==,type:str]
MQTT_USER: ENC[AES256_GCM,data:Xs6v,iv:DlyX9ErjbcJ4WxQFBhCdiBCRUqOpEYwWSg/X3uA4sSk=,tag:4N/85y5h2u754qP4iyIeiw==,type:str]
MQTT_PASSWORD: ENC[AES256_GCM,data:CD/cSnauAZ29KHuwGblqqDxSdrw=,iv:1mUsaq847o4rjbMIpSRErVIF8ZI+4oS4LDJGwbas0Jo=,tag:y7uSxXNFFM5HD/1IMRj5og==,type:str]
MQTT_TLS: ENC[AES256_GCM,data:lhmlb+8=,iv:mZiHwfgmkpWiBBRF5SvuSP/wlXMnAQmNnSZTYc0kMRM=,tag:VNxesMInF1Vj/gbeK/FlJA==,type:str]
MQTT_CLIENT_ID: ENC[AES256_GCM,data:Epdn4DGpQtgl53sc+Qo=,iv:gjVPQA1g0IFrzNJuqOWAAUgaFZsnZ2te5V32Q/SPTJg=,tag:zSauVShmjG0K8FDVevvolQ==,type:str]
MQTT_DISCOVERY_PREFIX: ENC[AES256_GCM,data:9ThR6rD3yXShgW1TmA==,iv:yn+89NBc6rXqbUnnKSqiWhzCruf0HPt3DfgxrhGZq7g=,tag:YoWynvyWRxdJ+UCbfjCJtQ==,type:str]
MQTT_BASE_TOPIC: ENC[AES256_GCM,data:Y8fGesOJmsXOvdE0khg=,iv:CyyDnQkUzGivWWAQnzWyElqoLeXmT7uLns5te03Hq0E=,tag:5CCuHp2ZeaZyXF4LmoLTUg==,type:str]
DEVICE_ID: ENC[AES256_GCM,data:RF2wLrHe/+CA0em/Yc0=,iv:IQx6zlX31rywQlhxchmGu3pjYg7JJlyNw1KT1/ELsL4=,tag:wVQ2s6XDN1l+tM9EQnjX/g==,type:str]
DEVICE_NAME: ENC[AES256_GCM,data:fHTyCXeA0aDlNqp7HRCuppTGkYnP,iv:wMp+umM2Y6S/54t5nc4h9PYCZd5NloOYIpCp3/4vOoE=,tag:PcP3e3hrJ+r78oPC7etm5g==,type:str]
LOG_LEVEL: ENC[AES256_GCM,data:/oGKyg==,iv:3A/971xbMz+dm+czrslz2nS3J1PRaM+7zLNACEHX7OM=,tag:OmLgstVHAT4GpviplU5V8Q==,type:str]
sops:
age:
- recipient: age1k4j5vyqqt7w7k52gvkpfe6znqr9mlgt076l7xgdtyc75lamsh4aqshj5qs
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZZ0R5UzhmOXV6T01KbTdr
Y2tCR2owUHZVV2VVakpEWXZjK3BNcTkzOVdZCjAzUnJaK2lMTWFhS0tTY2EvSU9u
OGh4SlI0em9YYUxkTGZZOUR2SG0yZjAKLS0tIFE5MWt4b3VWcmtVWXpwWjlTbHlT
R3ZQckkwMVJTekNRZW1uQVZVYXAvcTQKTeXGo0pBSFXderktCrgX6/hDRBraiOMF
SPr6hDe0ejMfWyOUSA9XQRQnn20gssRtA5DzNfpkjA3Gam+CU9Lu0w==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-10-26T11:29:23Z"
mac: ENC[AES256_GCM,data:jVnj8WpNjbFHwDfU2NBCcoqyfRYotmvc7w1b2A7e40GPYBKmkqVkZUlxBp4oc2p4qj+SRfPPKSUqDfxNqIS+oiqidui41/EhsJtPBjqyjoWmLfMMriwYUMlxrdFeCNNL97cUJin+Cy/OvPXgIU1BX9hnqyz0mTU5ce1zWju25Mc=,iv:4GJialKu0t9WmGoxKAA1P6wuan5gD8fSn3sToEL0QRU=,tag:yZbaQpIfUFh5eZvWQlKwxg==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.11.0
@@ -0,0 +1,10 @@
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY map_read.py map_mqtt.py ./
ENV PYTHONUNBUFFERED=1
USER nobody
CMD ["python", "map_mqtt.py"]
@@ -0,0 +1,259 @@
#!/usr/bin/env python3
"""
Service: polls a MAP inverter via ser2net and publishes to MQTT for Home Assistant
(MQTT discovery). Values are also written to the console for diagnostics.
All configuration is done via environment variables (see .env.example).
"""
import json
import logging
import os
import signal
import sys
import threading
import paho.mqtt.client as mqtt
from map_read import MapClient, MapError, read_status
log = logging.getLogger("map_mqtt")
def env(name, default=None, cast=str):
v = os.environ.get(name, "")
if v == "":
if default is None:
sys.exit("Environment variable %s is not set" % name)
return default
return cast(v)
SERIAL_HOST = env("SERIAL_HOST", "10.0.64.24")
SERIAL_PORT = env("SERIAL_PORT", 33333, int)
SERIAL_TIMEOUT = env("SERIAL_TIMEOUT", 1.0, float)
SERIAL_POLL_INTERVAL = env("SERIAL_POLL_INTERVAL", 5.0, float)
MQTT_HOST = env("MQTT_HOST")
MQTT_PORT = env("MQTT_PORT", 1883, int)
MQTT_USER = env("MQTT_USER", "")
MQTT_PASSWORD = env("MQTT_PASSWORD", "")
MQTT_CLIENT_ID = env("MQTT_CLIENT_ID", "map-inverter")
MQTT_TLS = env("MQTT_TLS", "false").lower() in ("1", "true", "yes")
DISCOVERY_PREFIX = env("MQTT_DISCOVERY_PREFIX", "homeassistant")
BASE_TOPIC = env("MQTT_BASE_TOPIC", "map_inverter")
DEVICE_ID = env("DEVICE_ID", "map_inverter")
DEVICE_NAME = env("DEVICE_NAME", "MAP Energy")
STATE_TOPIC = "%s/state" % BASE_TOPIC
AVAIL_TOPIC = "%s/availability" % BASE_TOPIC
HA_STATUS_TOPIC = "%s/status" % DISCOVERY_PREFIX
# (key, component, name, unit, device_class, state_class, extra)
ENTITIES = [
("mode_text", "sensor", "Mode", None, None, None, {"icon": "mdi:state-machine"}),
("mode", "sensor", "Mode code", None, None, None,
{"icon": "mdi:numeric", "entity_category": "diagnostic"}),
("u_acc", "sensor", "Battery voltage", "V", "voltage", "measurement", {}),
("i_acc", "sensor", "Battery current", "A", "current", "measurement", {}),
("p_acc", "sensor", "Battery power", "W", "power", "measurement", {}),
("p_load", "sensor", "Load power", "W", "power", "measurement", {}),
("u_net", "sensor", "Grid voltage", "V", "voltage", "measurement", {}),
("i_net", "sensor", "Grid current", "A", "current", "measurement", {}),
("p_net", "sensor", "Grid power", "W", "power", "measurement", {}),
("f_net", "sensor", "Grid frequency", "Hz", "frequency", "measurement", {}),
("u_out", "sensor", "Output voltage", "V", "voltage", "measurement", {}),
("f_out", "sensor", "Output frequency", "Hz", "frequency", "measurement", {}),
("t_acc", "sensor", "Battery temperature", "°C", "temperature", "measurement", {}),
("t_tor", "sensor", "Transformer temperature", "°C", "temperature", "measurement", {}),
("t_trans", "sensor", "Transistor temperature", "°C", "temperature", "measurement", {}),
("u_net_limit", "sensor", "Last out-of-range grid voltage", "V", "voltage", None,
{"entity_category": "diagnostic"}),
("e_net", "sensor", "Energy from grid", "kWh", "energy", "total_increasing", {}),
("e_acc", "sensor", "Energy from battery", "kWh", "energy", "total_increasing", {}),
("e_acc_charge", "sensor", "Battery charge energy", "kWh", "energy", "total_increasing", {}),
("errors_text", "sensor", "Errors", None, None, None,
{"icon": "mdi:alert-circle-outline", "entity_category": "diagnostic"}),
("problem", "binary_sensor", "Problem", None, "problem", None,
{"entity_category": "diagnostic"}),
("net_reverse", "binary_sensor", "Grid export", None, None, None,
{"icon": "mdi:transmission-tower-export"}),
("relay1", "binary_sensor", "Relay 1", None, None, None, {"icon": "mdi:electric-switch"}),
("relay2", "binary_sensor", "Relay 2", None, None, None, {"icon": "mdi:electric-switch"}),
]
def to_payload(s):
"""Flatten the read_status result into JSON for the state topic."""
p = {k: s[k] for k in ("mode", "mode_text", "u_acc", "i_acc", "p_acc", "p_load", "u_net",
"i_net", "p_net", "f_net", "u_out", "f_out", "t_acc", "t_tor",
"t_trans", "u_net_limit")}
# counter unit is 0.01 kWh (malina html/meters.js; confirmed against integrated grid power)
p["e_net"] = s["e_net_raw"] / 100
p["e_acc"] = s["e_acc_raw"] / 100
p["e_acc_charge"] = s["e_acc_charge_raw"] / 100
for k in ("net_reverse", "relay1", "relay2"):
p[k] = "ON" if s[k] else "OFF"
p["problem"] = "ON" if s["errors"] else "OFF"
texts = ["%s: %s" % (name, "; ".join(e["bits"])) for name, e in s["errors"].items()]
p["errors_text"] = (" | ".join(texts) or "none")[:255]
p["errors"] = {name: e["value"] for name, e in s["errors"].items()}
return p
def discovery_messages():
device = {
"identifiers": [DEVICE_ID],
"name": DEVICE_NAME,
"manufacturer": "microART",
"model": "MAP",
}
for key, component, name, unit, dev_class, state_class, extra in ENTITIES:
cfg = {
"name": name,
"unique_id": "%s_%s" % (DEVICE_ID, key),
"state_topic": STATE_TOPIC,
"value_template": "{{ value_json.%s }}" % key,
"availability_topic": AVAIL_TOPIC,
"device": device,
}
if unit:
cfg["unit_of_measurement"] = unit
if dev_class:
cfg["device_class"] = dev_class
if state_class:
cfg["state_class"] = state_class
if key == "errors_text":
cfg["json_attributes_topic"] = STATE_TOPIC
cfg["json_attributes_template"] = "{{ value_json.errors | tojson }}"
cfg.update(extra)
topic = "%s/%s/%s/%s/config" % (DISCOVERY_PREFIX, component, DEVICE_ID, key)
yield topic, json.dumps(cfg, ensure_ascii=False)
class Publisher:
def __init__(self):
self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=MQTT_CLIENT_ID)
if MQTT_USER:
self.client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
if MQTT_TLS:
self.client.tls_set()
self.client.will_set(AVAIL_TOPIC, "offline", qos=1, retain=True)
self.client.on_connect = self._on_connect
self.client.on_disconnect = self._on_disconnect
self.client.on_message = self._on_message
self.online = False
self._connack = threading.Event()
self._connect_error = None
def start(self, timeout=10):
"""Connect to the broker; raise on failure (the program will exit).
Later disconnects are handled by paho's automatic reconnect."""
self.client.connect(MQTT_HOST, MQTT_PORT, keepalive=60) # OSError on a bad address
self.client.loop_start()
if not self._connack.wait(timeout):
self.client.loop_stop()
raise ConnectionError("broker did not respond within %d s" % timeout)
if self._connect_error:
self.client.loop_stop()
raise ConnectionError("broker refused connection: %s" % self._connect_error)
def stop(self):
self.client.publish(AVAIL_TOPIC, "offline", qos=1, retain=True).wait_for_publish(2)
self.client.disconnect()
self.client.loop_stop()
def _on_connect(self, client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
log.error("MQTT: broker refused connection: %s", reason_code)
if not self._connack.is_set():
self._connect_error = reason_code
self._connack.set()
return
self._connack.set()
log.info("MQTT: connected to %s:%d", MQTT_HOST, MQTT_PORT)
client.subscribe(HA_STATUS_TOPIC)
self.publish_discovery()
self.set_available(self.online)
def _on_disconnect(self, client, userdata, flags, reason_code, properties):
log.warning("MQTT: disconnected (%s), reconnecting...", reason_code)
def _on_message(self, client, userdata, msg):
# Home Assistant restarted - republish discovery
if msg.topic == HA_STATUS_TOPIC and msg.payload == b"online":
log.info("MQTT: Home Assistant online, republishing discovery")
self.publish_discovery()
self.set_available(self.online)
def publish_discovery(self):
for topic, payload in discovery_messages():
self.client.publish(topic, payload, qos=1, retain=True)
def set_available(self, online):
self.online = online
self.client.publish(AVAIL_TOPIC, "online" if online else "offline", qos=1, retain=True)
def publish_state(self, payload):
self.client.publish(STATE_TOPIC, json.dumps(payload, ensure_ascii=False), qos=0)
def format_line(p):
return ("mode=%d (%s) Ubat=%.1fV Ibat=%.1fA Pbat=%dW Pload=%dW Ugrid=%dV Igrid=%.1fA "
"Pgrid=%dW Fgrid=%sHz Uout=%dV Fout=%sHz Tbat=%d Ttr=%d Ttrans=%d "
"Egrid=%.2f Ebat=%.2f Echarge=%.2f errors=%s") % (
p["mode"], p["mode_text"], p["u_acc"], p["i_acc"], p["p_acc"], p["p_load"],
p["u_net"], p["i_net"], p["p_net"], p["f_net"], p["u_out"], p["f_out"],
p["t_acc"], p["t_tor"], p["t_trans"], p["e_net"], p["e_acc"], p["e_acc_charge"],
p["errors_text"])
def main():
logging.basicConfig(level=env("LOG_LEVEL", "INFO").upper(), stream=sys.stdout,
format="%(asctime)s %(levelname)s %(message)s")
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", line_buffering=True)
stop = threading.Event()
signal.signal(signal.SIGTERM, lambda *a: stop.set())
signal.signal(signal.SIGINT, lambda *a: stop.set())
log.info("Starting: ser2net %s:%d, poll every %.1f s, MQTT %s:%d, topic %s",
SERIAL_HOST, SERIAL_PORT, SERIAL_POLL_INTERVAL, MQTT_HOST, MQTT_PORT, STATE_TOPIC)
pub = Publisher()
try:
pub.start()
except OSError as e: # ConnectionError is an OSError too
log.error("MQTT: failed to connect to %s:%d: %s", MQTT_HOST, MQTT_PORT, e)
sys.exit(1)
client = None
while not stop.is_set():
try:
if client is None:
client = MapClient(SERIAL_HOST, SERIAL_PORT, SERIAL_TIMEOUT)
client.connect()
log.info("ser2net: connected to %s:%d", SERIAL_HOST, SERIAL_PORT)
payload = to_payload(read_status(client))
log.info(format_line(payload))
pub.publish_state(payload)
if not pub.online:
pub.set_available(True)
except (MapError, OSError) as e:
log.error("ser2net: %s", e)
if client is not None:
client.close()
client = None
if pub.online:
pub.set_available(False)
stop.wait(SERIAL_POLL_INTERVAL)
log.info("Stopping")
if client is not None:
client.close()
pub.stop()
if __name__ == "__main__":
main()
@@ -0,0 +1,385 @@
#!/usr/bin/env python3
"""
Read the current operating parameters of a MAP inverter (microART) via ser2net (raw TCP).
Protocol reverse-engineered from thirdparty/malina/usr/bin/mapd.c:
* 19200 8N1 (configured on the ser2net side);
* every byte sent by the host is echoed back by the MAP; every byte of the MAP
reply must be echoed back by the host, otherwise the MAP won't send the next one;
* request: cmd('r'=0x72) | count-1 | addr_hi | addr_lo | crc | '\\n'
* reply: 'o'(0x6F) | data... | crc | '\\n' ('e'=0x65 means error)
* byte 0x0A inside a frame is encoded as DB DC, byte 0xDB as DB DD;
* crc = (0x100 - sum of all frame bytes in encoded form) & 0xFF;
if crc == 0x0A it also serves as the frame terminator.
Usage:
python map_read.py # single read, 10.0.64.24:33333
python map_read.py --loop 2 # poll every 2 s
python map_read.py --json # JSON output
python map_read.py --host 1.2.3.4 --port 4001
The ser2net port must be in raw mode (not telnet), for example:
33333:raw:0:/dev/ttyUSB0:19200 8DATABITS NONE 1STOPBIT
"""
import argparse
import json
import select
import socket
import sys
import time
DEFAULT_HOST = "10.0.64.24"
DEFAULT_PORT = 33333
CMD_READ = 0x72 # 'r'
ANS_OK = 0x6F # 'o'
ANS_ERR = 0x65 # 'e'
LF = 0x0A
ESC = 0xDB
MODES = {
0: "System: OFF, Input grid: OFF",
1: "System: OFF, Input grid: ON",
2: "System: ON, Input grid: OFF, Local grid: BATTERY, Battery: DISCHARGING",
3: "System: ON, Input grid: ON, Local grid: PASS-THROUGH",
4: "System: ON, Input grid: ON, Local grid: PASS-THROUGH, Battery: CHARGING",
}
# Error bit field descriptions (from html/local/RU/local.inc)
ERROR_BITS = {
"RSErrJobM": [
"Battery fully discharged, voltage critically low",
"Battery overvoltage, inverting/charging suspended",
"Battery short circuit while charging, charging suspended",
"Battery short circuit, inverting/charging suspended",
"Main relay may be stuck",
"Short circuit on 220V grid",
"External voltage on 220V output, inverting disabled",
"Firmware reset (interference)",
],
"RSErrJob": [
"Battery discharged, inverting will stop in 1 minute",
"Battery overload",
"Load above rated power (limited operating time)",
"Overheating, inverting/charging suspended",
"Fan not spinning",
"Grid overload, switching to inverter",
"Operating mode failure (power loss/interference)",
"Shutdown after repeated short circuits while charging (cleared after 2 h)",
],
"RSWarning": [
"No action for START+CHARGE button combination",
"Grid voltage out of range",
"External voltage on output or voltage spikes from load",
"Voltage spikes on grid input",
"START or CHARGE button stuck",
"Charging not possible - no grid",
"Load power above EE_LCD_NetMaxPow, switching to inverter",
"Grid unstable",
],
"RSErrDop": [
"Invalid data from previous phase / no link",
"Phase shift outside 60 deg synchronization window",
"BMS I2C error",
"MPPT I2C error",
"Outdated MPPT firmware",
"Parallel MAP synchronization error",
"Slave MAP has no grid while master switched to grid",
None,
],
"I2C_Err": ["I2C Err Ack", "I2C Err Sum", "I2C Err Data Size", "I2C Err Protocol",
None, None, None, None],
"F_Acc_Over": [
"Shutdown: battery overcurrent",
"Shutdown: battery overcurrent while charging",
"Shutdown: battery voltage critically low",
"Shutdown: fan failure",
"Shutdown: power above rated for more than 30 min",
"Shutdown: battery fully discharged",
"Shutdown: temperature sensor out of range",
"Full shutdown after repeated battery overloads (press START)",
],
"F_Net_Over": [
"Inverting disabled - external voltage on output or faulty relay",
"Switched to inverter - grid overload",
None,
"Shutdown after repeated grid overloads (press START)",
None, None, None, None,
],
}
class MapError(Exception):
pass
class MapClient:
def __init__(self, host, port, timeout=1.0):
self.host = host
self.port = port
self.timeout = timeout
self.sock = None
def connect(self):
self.sock = socket.create_connection((self.host, self.port), timeout=5)
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.sock.settimeout(self.timeout)
self.flush()
def close(self):
if self.sock:
self.sock.close()
self.sock = None
def __enter__(self):
self.connect()
return self
def __exit__(self, *exc):
self.close()
# --- byte level ------------------------------------------------------
def flush(self):
"""Discard everything accumulated in the input buffer."""
while True:
r, _, _ = select.select([self.sock], [], [], 0.05)
if not r:
return
if not self.sock.recv(4096):
raise MapError("connection closed by ser2net")
def _recv1(self):
try:
b = self.sock.recv(1)
except socket.timeout:
raise MapError("MAP response timeout")
if not b:
raise MapError("connection closed by ser2net")
return b[0]
def _put(self, a):
"""Send a byte and wait for its echo (resyncing to the stream, as mapd.c does)."""
self.sock.sendall(bytes([a]))
for _ in range(21):
if self._recv1() == a:
return
raise MapError("no echo from MAP (0x%02X)" % a)
def _put_coded(self, a, csum):
if a == LF:
seq = (ESC, 0xDC)
elif a == ESC:
seq = (ESC, 0xDD)
else:
seq = (a,)
for b in seq:
self._put(b)
csum += b
return csum
# --- frames ----------------------------------------------------------
def _send_command(self, cmd, addr, count):
csum = 0
for b in (cmd, count - 1, addr >> 8, addr & 0xFF):
csum = self._put_coded(b, csum)
crc = (-csum) & 0xFF
self._put(crc)
if crc != LF:
self._put(LF)
def _read_answer(self):
raw = bytearray()
while True:
b = self._recv1()
raw.append(b)
self.sock.sendall(bytes([b])) # echo back to the MAP
if b == LF or len(raw) > 600:
break
if raw[0] == ANS_ERR:
raise MapError("MAP returned an error ('e')")
if raw[0] != ANS_OK or len(raw) < 3:
raise MapError("unexpected reply: %s" % raw[:16].hex())
if ((-sum(raw[:-2])) & 0xFF) == raw[-2]:
body = raw[1:-2]
elif ((-sum(raw[:-1])) & 0xFF) == LF:
body = raw[1:-1] # crc == 0x0A is itself the frame terminator
else:
raise MapError("checksum error")
data = bytearray()
i = 0
while i < len(body):
if body[i] == ESC and i + 1 < len(body) and body[i + 1] in (0xDC, 0xDD):
data.append(LF if body[i + 1] == 0xDC else ESC)
i += 2
else:
data.append(body[i])
i += 1
return bytes(data)
def read(self, addr, count, retries=3):
"""Read count (1..256) bytes of MAP memory starting at addr."""
last = None
for _ in range(retries):
try:
self._send_command(CMD_READ, addr, count)
data = self._read_answer()
if len(data) < count:
raise MapError("short reply: %d of %d bytes" % (len(data), count))
return data[:count]
except MapError as e:
last = e
time.sleep(0.2)
self.flush()
raise last
# --- data decoding -------------------------------------------------------
def decode_bits(name, value):
texts = ERROR_BITS.get(name, [])
out = []
for bit in range(8):
if value >> bit & 1:
t = texts[bit] if bit < len(texts) and texts[bit] else "bit %d" % bit
out.append(t)
return out
def phase_current(lo, hi):
v = (lo + (hi & 0x7F) * 256) / 10
return -v if hi & 0x80 else v
def read_status(client):
m = client.read(0x400, 256)
x = lambda addr: m[addr - 0x400]
e = client.read(0x527, 96)
y = lambda addr: e[addr - 0x527]
unet = x(0x422) + 100 if x(0x422) else 0
uout = x(0x427) + 100 if x(0x427) else 0
inet_raw = x(0x423)
hz = lambda t: round(6250 / t, 1) if t else None
s = {
"mode": x(0x400),
"mode_text": MODES.get(x(0x400), "unknown mode"),
"status_char": x(0x402),
"u_acc": (x(0x405) * 256 + x(0x406)) / 10,
"i_acc": round(x(0x432) / 16 + x(0x433) * 16, 1),
"i_acc_coarse": x(0x408) * 2,
"p_load": x(0x409) * 100,
"u_net": unet,
"i_net": round(x(0x431) / (16 if inet_raw < 16 else 4), 1),
"p_net": x(0x424) * 100,
"f_net": hz(x(0x425)),
"u_out": uout,
"f_out": hz(x(0x426)),
"u_net_limit": x(0x429) + 100,
"f_net_limit": round(2500 / x(0x428), 1) if x(0x428) else None,
"t_acc": x(0x42E) - 50,
"t_tor": x(0x42F) - 50,
"t_trans": x(0x430) - 50,
"net_reverse": (x(0x443) + x(0x444) * 256) > 32768,
"e_net_raw": x(0x44D) | x(0x44E) << 8 | x(0x44F) << 16,
"e_acc_raw": x(0x450) | x(0x451) << 8 | x(0x452) << 16,
"e_acc_charge_raw": x(0x453) | x(0x454) << 8 | x(0x455) << 16,
"flag_eco": y(0x585),
"relay1": bool(y(0x586) & 1),
"relay2": bool(y(0x586) & 2),
# meaningful only in a three-phase setup, otherwise these cells hold garbage
"i_phase": [phase_current(y(0x528), y(0x529)),
phase_current(y(0x52A), y(0x52B)),
phase_current(y(0x52C), y(0x52D))],
"errors": {},
}
s["p_acc"] = round(s["u_acc"] * s["i_acc"])
for name, addr in (("RSErrSis", 0x42A), ("RSErrJobM", 0x42B), ("RSErrJob", 0x42C),
("RSWarning", 0x42D), ("RSErrDop", 0x447), ("I2C_Err", 0x45A),
("F_Acc_Over", 0x41C), ("F_Net_Over", 0x41D)):
v = x(addr)
if v:
s["errors"][name] = {"value": v, "bits": decode_bits(name, v)}
return s
def print_status(s):
rows = [
("Mode", "%d - %s" % (s["mode"], s["mode_text"])),
("Battery voltage", "%.1f V" % s["u_acc"]),
("Battery current", "%.1f A (coarse %d A)" % (s["i_acc"], s["i_acc_coarse"])),
("Battery power (calc.)", "%d W" % s["p_acc"]),
("MAP load power", "%d W" % s["p_load"]),
("Grid voltage", "%d V" % s["u_net"]),
("Grid current", "%.1f A" % s["i_net"]),
("Grid power", "%d W%s" % (s["p_net"], " (export to grid)" if s["net_reverse"] else "")),
("Grid frequency", "%s Hz" % s["f_net"]),
("Output voltage", "%d V" % s["u_out"]),
("Output frequency", "%s Hz" % s["f_out"]),
("Battery temp.", "%d °C" % s["t_acc"]),
("Transformer temp.", "%d °C" % s["t_tor"]),
("Transistor temp.", "%d °C" % s["t_trans"]),
("Last out-of-range U", "%d V" % s["u_net_limit"]),
("Relay 1 / Relay 2", "%s / %s" % ("on" if s["relay1"] else "off",
"on" if s["relay2"] else "off")),
("Energy from grid", "%.2f kWh" % (s["e_net_raw"] / 100)),
("Energy from battery", "%.2f kWh" % (s["e_acc_raw"] / 100)),
("Battery charge energy", "%.2f kWh" % (s["e_acc_charge_raw"] / 100)),
]
w =max(len(r[0]) for r in rows)
print(time.strftime("%Y-%m-%d %H:%M:%S"))
for k, v in rows:
print(" %-*s : %s" % (w, k, v))
if s["errors"]:
print(" Errors:")
for name, err in s["errors"].items():
print(" %s = 0x%02X: %s" % (name, err["value"], "; ".join(err["bits"])))
else:
print(" No errors")
def main():
ap = argparse.ArgumentParser(description="Read MAP inverter parameters via ser2net")
ap.add_argument("--host", default=DEFAULT_HOST)
ap.add_argument("--port", type=int, default=DEFAULT_PORT)
ap.add_argument("--timeout", type=float, default=1.0, help="per-byte timeout, s")
ap.add_argument("--loop", type=float, metavar="SEC", help="poll every SEC seconds")
ap.add_argument("--json", action="store_true", help="JSON output")
args = ap.parse_args()
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8")
with MapClient(args.host, args.port, args.timeout) as c:
while True:
try:
s = read_status(c)
if args.json:
print(json.dumps(s, ensure_ascii=False), flush=True)
else:
print_status(s)
print(flush=True)
except MapError as e:
print("Error: %s" % e, file=sys.stderr)
if not args.loop:
return 1
if not args.loop:
return 0
time.sleep(args.loop)
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
pass
except OSError as e:
print("ser2net connection error: %s" % e, file=sys.stderr)
sys.exit(2)
@@ -0,0 +1 @@
paho-mqtt==2.1.0
@@ -17,7 +17,7 @@ spec:
app: app:
image: image:
repository: ghcr.io/pvliesdonk/markdown-vault-mcp repository: ghcr.io/pvliesdonk/markdown-vault-mcp
tag: "3.1.0" tag: "v3.1.0"
env: env:
MARKDOWN_VAULT_MCP_SOURCE_DIR: /vault MARKDOWN_VAULT_MCP_SOURCE_DIR: /vault
MARKDOWN_VAULT_MCP_INDEX_PATH: /data/state/index.db MARKDOWN_VAULT_MCP_INDEX_PATH: /data/state/index.db
@@ -44,6 +44,16 @@ spec:
name: markdown-vault-mcp-secrets name: markdown-vault-mcp-secrets
key: BEARER_TOKEN key: BEARER_TOKEN
probes: probes:
# first boot downloads embedding models from HuggingFace Hub
# and builds the vector index/embeddings before binding the
# port - the default liveness probe budget (periodSeconds=10,
# failureThreshold=3 = 30s) isn't enough, so it gets killed
# mid-startup on every restart, in an endless loop.
startup:
enabled: true
spec:
periodSeconds: 5
failureThreshold: 36
liveness: liveness:
enabled: true enabled: true
readiness: readiness:
@@ -4,21 +4,21 @@ metadata:
name: markdown-vault-mcp-secrets name: markdown-vault-mcp-secrets
namespace: mcp namespace: mcp
stringData: stringData:
BEARER_TOKEN: ENC[AES256_GCM,data:UFy6rlwEWje7mO8WJFFvgq8SEKaf0dP9mx00BLPxfKTzgcZqbce2Sr1U5aWVf8iphKapcBWRPEKqa/f1dC93tw==,iv:G8owKrU8ueTnQJ5p6O4U5x5H5WVriqqTUH5iAlW3ZoU=,tag:wuDlQmY622xLZvdJ17cxkQ==,type:str] BEARER_TOKEN: ENC[AES256_GCM,data:vRsc/kmZ1qySDUrI1dMeLqSSn+jEgvPivw3dRYTLr7NRm6T7SYnmhhBb81CG6xQAl0Wz8RfOCescQpUJlbPl4w==,iv:Zha9o8uIdR6DPMk+KRL6ZnKdh3zPXuyjCCK9yKnuYFI=,tag:l+8oHY/hgZcKVQx9yiaEmg==,type:str]
OIDC_CLIENT_SECRET: ENC[AES256_GCM,data:NEAQqXz7zICa+3bSTRxfnURV0+toWhTexHBYXPB42tBomNg4kf3k9sspSJQYATcxuNIK5xv6XOCna4J1fm/+cw==,iv:T9gj9Jb/RcQcpFQUeqnH6S6Ri/XrlVntv9oU7bwnjEM=,tag:UqR4VqMe75s40TgcggbOrw==,type:str] OIDC_CLIENT_SECRET: ENC[AES256_GCM,data:2unvRtI/V0aTULgSacGvEoYYQN0uWBR3b2TKjl7hjvQTXesuglTtSNz9YsjDqc8ZHGNBndiz2Q6VZagS3ERprw==,iv:fjMhiKamjbIrNYAK9TU2PPn0smJR+Kr3qhRhEUbjm04=,tag:wuUZbccOBoFcdiTPqb8QCQ==,type:str]
OIDC_JWT_SIGNING_KEY: ENC[AES256_GCM,data:AwY8V9EQ1Z/NJmc/8msG2RpyCIhEKMzLTLdsLHpC2QfgB652aYEHAYg6u8quRvSypw1cG2zdJJQEym/MJ7CGdw==,iv:FjMwdBE51PuQCqhrojAWiwxoYkemSxO1+9ebw3kRRrk=,tag:X/b/t4e5qZmFJ1FSrWQOfw==,type:str] OIDC_JWT_SIGNING_KEY: ENC[AES256_GCM,data:63S/0TsEis959MJRLd5atPXQrv5pNMC5iyMHJvT67luLLPkd2bdgfmX4Pu7R3Hr5YIGoujn1/xCuOdZH0RtIog==,iv:3Gi2ZukSzU9SIy7fPzmlvWbA4ykaNgta95DuviHf5zk=,tag:BPUXUPMIxW9kbyyQqoz0gQ==,type:str]
sops: sops:
age: age:
- recipient: age1kct5g7d7eus2hvdnmx99ewsrtz70lhlasxa25euqwdctqhwqqpxqz0n7gg - recipient: age1kct5g7d7eus2hvdnmx99ewsrtz70lhlasxa25euqwdctqhwqqpxqz0n7gg
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqUm5qNE93WUhKTlRIUXl3 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPOVJCaXpjeUEzWkN4SEdz
TmZMREJSQmVOWXlDdW9DZlFheGpDUHIvYmxFClozek5MVitWYVhIY3I2Nmk3YlJS RnFkZmtXa0IwaE9xdGtBdVRwV1B5MkdDbVMwCmVYeUJpQmQwZ29BbnRPK1AzWGJx
cEhJS0NKNy90emovR1dCeHZ4UjNvVWcKLS0tIHFmTGMrVjRaSTFCNGQwSzB4NitT NHV0NnRxenRWV2M4MytrZ3llNmxaMzQKLS0tIHVOcDVyTDkrNHJRMkhRVzFPSGpW
K2hIS3FuTHNoRHhxMEFIcFkvQjZmTHMKQls9vNvWQ+zc27JmQXxJilkiVTDqjEzD V1RRNTdiUTBjSWZlMlEvRXF3OUowZ00KXf/+ucgZuDAczE1WlEW7D2/cR+sB66BQ
55M/azYogOsp0Kfe3MBm740tdaPiD6iyLXrEuZKNywD2mPtNT8+3JA== 7JC42VJiyEf2djlODmvK26bDTeiJ/Uk9HsSuvamGUM8b8vAj2w/r/A==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-26T10:29:46Z" lastmodified: "2026-07-27T06:42:40Z"
mac: ENC[AES256_GCM,data:sfdT7eRXMpgbWtJhu5U6/T03ckavKNcojfkg9Tu0uLwigsAsaxgiMhBA3kQWYU3tV9gxy6BXIak8cdF8EfCEAWBYSQC6iQLgOZPXvLpWFoaiigNHbKSYLYcimlD1wmHlRb5L0x5+QNANxD22Hkjlkw0kgPQ+GZKP7SzDsixXpBA=,iv:1rC3zYmRWM1/8O8xgitTwvQ9qORXNR20qABDKPqGpSc=,tag:YkJNRwxoGfGXnfcX5AJXtw==,type:str] mac: ENC[AES256_GCM,data:XRxbyHL4iJMoRMb4X6C2y1ChTNSkqou01bwtIb6zNfwcGCt7uMGbm1HxvmjbdeJI/zQIIVfgxbbTxhZw+sL2FF+PYAjPcaGvaJ86O97lr9fUB30NC0tMluW82pt2iiisK+6dEEPH9i/kGFrJHN6RlaYVkEfv6U8xjlpIcaNx0+A=,iv:YnqMRlHl1WVYNqqN9hRH88hgIALrfgv6WrJGyeNDstI=,tag:M1DYFr3G9ub/R2kNF8ZnNQ==,type:str]
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
version: 3.11.0 version: 3.11.0
@@ -1,6 +1,6 @@
image: image:
repository: eclipse-mosquitto repository: eclipse-mosquitto
tag: "2.1.2" tag: "2.1.2-alpine"
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
# not really needed, as StatefulSet has hardcoded "replicas: 1" # not really needed, as StatefulSet has hardcoded "replicas: 1"
@@ -18,7 +18,7 @@ spec:
values: values:
image: image:
repository: eclipse-mosquitto repository: eclipse-mosquitto
tag: "2.1.2" tag: "2.1.2-alpine"
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
replicaCount: 1 replicaCount: 1
+9 -9
View File
@@ -4,19 +4,19 @@ metadata:
name: mosquitto-passwd name: mosquitto-passwd
namespace: mqtt namespace: mqtt
stringData: stringData:
passwd: ENC[AES256_GCM,data:RHKtt4UgqElKW/rw9BnpZKOnhchsqmO5jVVVRJBy82b+9KrbUN5iGQ3k5X4/6gjrKKOcs5QabJahR+i/aVSr5fVyCJR7Y45YvKk+eyKqGuDTFCkzjxpSDvO4AseQvOHSGTMRXQx73jdpCnEEYtoXZNCLuhGtueKvrhkNDDCLFwtybaWsrI4g3fwo5auHbyWwjoxXskv0+pwi7vnESIJudJl4ul3Ie4NpyispZ+H0O6p9cn+Hwu2qoTJbR/zbpA==,iv:Blp/QsgEVD73JhGFzM3GNFPfUcwB58fzKJlQIGBw5Yg=,tag:InRGXl9vB/DF778pbhoqfw==,type:str] passwd: ENC[AES256_GCM,data:YpTczjZ73h4Td+TmEZIgil9DN7WfVwBsi75F/t3D1oWGjGsifklnRsZUJqXiXyUwIxTxlFiWgfb01LjmILE0nB9rxoEbFdrASaLgtnCoYz/fvSZLqwCK+FSSub3+r0nJTNvxzD+bC/av8JEPNDW/8CSrHMrTrVCcLhAchOAvviRLgO18862XWcvPh1plO8luaZG38cuh+VbKKwidkwozc6B7Ghys5NfZuB1rP4vLZ6/f1PzcFRugtJ0fzzl/BA==,iv:9FKm4V/lYXott/6aVBKg4g8GtzVqgT/xndj0CKqMm+Y=,tag:41gCVbrK6Rp2IvVUt98Hpw==,type:str]
sops: sops:
age: age:
- recipient: age1u236u5mzkzj72y4mfxx68gfqw0u0j6tdgkudnm5pdfpe7apq4u6s89jr2h - recipient: age1u236u5mzkzj72y4mfxx68gfqw0u0j6tdgkudnm5pdfpe7apq4u6s89jr2h
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBudUhrOVlvUU82cG45K3ll YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBobk1BQW0yemEzV1FQM2ZJ
R05Dc0NUdFN3b0k2UmZoSWlxd1diQ0dNcjBFCm1icmlvQUhIaFYwSFZ0MURmQVUy YkZJbjJPZlRiSDlyamx1WVdyNTJBNXo1YlQwCnhBT01SeE0rZDVOcFRkZm9KQ0t2
bTJPWk5mWVpQb1JNQXhCOFhpNXByNTAKLS0tIE1vR0t3SUdGTnRiWFVXOG5yYVhk dUIyOHpzenVZc3lsa2hmMjF3LzRGMFUKLS0tIENmM05Ud2xGblNpdjZjWWFPeWg3
UVBsRDlZSVpJbk1KYmJGYlRmelNWWVkKBVabDs2XS6XAVKiKbAtk3CqraoBXOD5I UEtuZWZ5enkwSGk0MXR1czBibzQ1M2sKfp+76C0X2DgsffWZVtBevI4lCs8RM09p
+0uq5YeFvMxhrnX6wXffCS7S3zbvNV6/2Enbt7SBJ8EhCvKbfaOtfg== tzsrneX4iIX6WCn1kRLsWTZpN6Vg8Wsl6kUWnSA3WfHWI9W4CJdrag==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-26T10:29:47Z" lastmodified: "2026-07-21T19:09:49Z"
mac: ENC[AES256_GCM,data:/NEEVA/hO2NUIWOhVjL7ZmtD9Ac169CL4D71qPueZbNSHtTqx8GHeo8DI1tpsUBuGytVBQxh1MXK3REt/bD2GzU1dCpvtTtQmQb+B57H6Za3RIyPU51QTt9nHYHiWend3tcxlR3Xc8h5TBa0kKwjXvlij4jEUM+dA5UESE/QGFk=,iv:yTyP3PVw+pVzPPqMN7EyIfM/BN/wK9nIHx84F65A+/k=,tag:oTpIqbAOq5aqlCWnVqaKNw==,type:str] mac: ENC[AES256_GCM,data:qecTVUaC/3j/oP1DVnXEKV9Ub+k5gvGHfteol4W5I3gq8IB2YYZ+vfuinPVJJeSsN5HvK62ftnIgRNu0dtzDOrnuZENczEMwPrOz90RCUbX7WBeKh23KZ8gSSw2MBn6gsDsz1gXFWENIyjQTJFZ4qAJY6jfeH2IFC9RkpoU3j14=,iv:ksLG/aHHBAkl6aA52Xufs2vijZlpsaXccoxheYV3Gm4=,tag:4YiaPGMBWSWhVBO1tNF6vQ==,type:str]
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
version: 3.11.0 version: 3.13.2
+96
View File
@@ -0,0 +1,96 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: ntfy
namespace: notify
spec:
interval: 30m
chartRef:
kind: OCIRepository
name: ntfy
install:
remediation:
retries: -1
upgrade:
remediation:
retries: 5
driftDetection:
mode: enabled
values:
controllers:
ntfy:
containers:
app:
image:
repository: binwiederhier/ntfy
tag: v2.34.0
args:
- serve
env:
NTFY_BASE_URL: https://notify.aggtaa.com
NTFY_LISTEN_HTTP: ":80"
NTFY_BEHIND_PROXY: "true"
NTFY_CACHE_FILE: /var/lib/ntfy/cache.db
NTFY_AUTH_FILE: /var/lib/ntfy/user.db
NTFY_AUTH_DEFAULT_ACCESS: deny-all
NTFY_ATTACHMENT_CACHE_DIR: /var/lib/ntfy/attachments
NTFY_ENABLE_LOGIN: "true"
envFrom:
- secretRef:
name: ntfy-secrets
probes:
liveness:
enabled: true
custom: true
spec:
httpGet:
path: /v1/health
port: 80
readiness:
enabled: true
custom: true
spec:
httpGet:
path: /v1/health
port: 80
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 256Mi
service:
app:
controller: ntfy
ports:
http:
port: 80
ingress:
app:
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: notify.aggtaa.com
paths:
- path: /
pathType: Prefix
service:
identifier: app
port: http
tls:
- secretName: ntfy-tls
hosts:
- notify.aggtaa.com
persistence:
data:
type: persistentVolumeClaim
storageClass: longhorn
accessMode: ReadWriteOnce
size: 2Gi
retain: true
globalMounts:
- path: /var/lib/ntfy
@@ -1,8 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1 apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository kind: OCIRepository
metadata: metadata:
name: openwebui name: ntfy
namespace: ai namespace: notify
spec: spec:
interval: 1h interval: 1h
url: oci://ghcr.io/bjw-s-labs/helm/app-template url: oci://ghcr.io/bjw-s-labs/helm/app-template
@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- helmrepository.yaml
- helmrelease.yaml
- secret.enc.yaml
+25
View File
@@ -0,0 +1,25 @@
apiVersion: v1
kind: Secret
metadata:
name: ntfy-secrets
namespace: notify
stringData:
#ENC[AES256_GCM,data:XneiueSaauWV0MEAzJcs730dwgrSt970o99Jq9iYbAF/RGAXPq0aMhSdNtK4PIgY5nSuVDsaGjXMEu+hF7kX4zWdIQg8MQ2n82fpcNc15RCMsyMNn9WVI1WKr5y7gonz5w==,iv:SPiP6hSzqdBk9WUVKT6TYp3qdZ3gje70CclLXaQNPkw=,tag:H8RtqMaF3bmMNUvfZMJtXQ==,type:comment]
NTFY_AUTH_USERS: ENC[AES256_GCM,data:G6VE3ZkkiHU1dFY52J2XVKg4RL55SBTNZRp9DKeUHFJho/PURi+UIzS0gk2377ZAIRdX7gn8DOqfCZ4gvUw40TnlC/wFRAgZO1a6G1WKEZHceUr3cwFGQQhN0pwbc7RfK+amWR4Ov3m/lsNCZjU2KxUps8jNCvnGNR9IxHXBGoNt7PzeNnn6vL5JDBFba3MNsNDUVOm3Q4pTwG9pb1cvQYUU89OMIq/MkZfwotlR0HyEG/8hDiClr9PGJSuVSerL3JmtV10txM2bKRCFRtDb1DtYrRu8EzQ=,iv:IukIdkjr1vRdQJBMI+adN8O0UPo4Vv25gB5vCzim74A=,tag:QYGGERjRr7n1Pww9bg6Oiw==,type:str]
#ENC[AES256_GCM,data:WDat/Zx7qfe6gY1t8npcfvHz+EpTbtEuHJMoUijlJq5FJEu6nVbrASJOkkWDxfhT,iv:6GX4Jff9YZ27RHaCym1ziSkZiF7VifRkxjleLcYVYHY=,tag:RhsfsYqNc9dyFc53TxAFcw==,type:comment]
NTFY_AUTH_ACCESS: ENC[AES256_GCM,data:Mc4c8bDGwIEUZnx2x1N5IDiLPCdHZw==,iv:LCFN1XXjGfzel/3O4FHumcJpn1DhqdqOhjjpzBSUpYs=,tag:QCqj6hIClhXTnINq+hQuow==,type:str]
sops:
age:
- recipient: age1uzs33ep6k3htnuwyyp54y373wkf7urd0qvk5kxcmptr5tt0fffus4v06gn
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4ZTg3RVF1Y2sxUjAzTVgw
d2ZkQVpmdzNqYzIwRVR1RGExUjM4SVRMSVN3Cis1bEpZNWZkY1VaWDkzUUlnN2RZ
TVUyZ2VNb0MvS3R5c283Ui9DRzkydUEKLS0tIFZMKzhJWnVZUVRsSFNHYStUaGti
aGFRSVg5alBnNnA4emxkeFFpZ3BnUFkKUPOqUjkjSl77Xvb3i+THWINbvtzrioqF
QBkQh5SSzq8fWrHoHHp7XHu02+p34dEJMOmEg1rlhu7pYRMNbbaRsQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-10-26T09:44:43Z"
mac: ENC[AES256_GCM,data:0z4ety9M00u1vIGn1NyqO9K42IGOQt28L08sKU4D3aB6sroqbY6UkafabBYGyzB7LCaOc3iukDCFtB8hBwg6UarNPbeQlZqG3yuHmVuNB2vOubE4U1YFxeTJ5bNvyP4kpSp6DwUiC9QeJtQ0T98aqj9R7dB3x4vU1s/hPmYSLxU=,iv:+5kSIP8prdAOaYCUhQzUlugncFH54tTMweMfo+tP4BY=,tag:bpjq0X3gCy+LbLeSmIX5Jw==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.11.0
@@ -1,73 +0,0 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: openwebui
namespace: ai
spec:
interval: 30m
chartRef:
kind: OCIRepository
name: openwebui
driftDetection:
mode: enabled
values:
controllers:
app:
containers:
app:
image:
repository: ghcr.io/open-webui/open-webui
tag: v0.11.0
env:
TZ: Europe/Moscow
probes:
liveness:
enabled: true
readiness:
enabled: true
startup:
enabled: true
spec:
periodSeconds: 10
failureThreshold: 30 # first boot pulls the embedding model
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
memory: 2Gi
service:
app:
controller: app
ports:
http:
port: 8080
ingress:
app:
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: ai2.aggtaa.com
paths:
- path: /
pathType: Prefix
service:
identifier: app
port: http
tls:
- secretName: openwebui-tls
hosts:
- ai2.aggtaa.com
persistence:
data:
type: persistentVolumeClaim
accessMode: ReadWriteOnce
size: 10Gi
storageClass: longhorn
retain: true
globalMounts:
- path: /app/backend/data
@@ -40,7 +40,7 @@ spec:
# behind Authelia the same way fava/zigbee2mqtt are. # behind Authelia the same way fava/zigbee2mqtt are.
annotations: annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: auth-authelia@kubernetescrd traefik.ingress.kubernetes.io/router.middlewares: auth-tinyauth@kubernetescrd
hosts: hosts:
- host: sync.aggtaa.com - host: sync.aggtaa.com
paths: paths:
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long
@@ -0,0 +1,108 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: tinyauth
namespace: auth
spec:
interval: 10m
chartRef:
kind: OCIRepository
name: tinyauth
driftDetection:
mode: enabled
values:
controllers:
tinyauth:
containers:
app:
image:
repository: ghcr.io/tinyauthapp/tinyauth
tag: v5.2.0
env:
TZ: Europe/Moscow
TINYAUTH_APPURL: https://login.aggtaa.com
TINYAUTH_UI_TITLE: "Welcome"
TINYAUTH_RESOURCES_PATH: /resources
TINYAUTH_UI_BACKGROUNDIMAGE: /resources/background.webp
TINYAUTH_AUTH_USERSFILE: /config/users
TINYAUTH_AUTH_SECURECOOKIE: "true"
TINYAUTH_LABELPROVIDER: none
TINYAUTH_ANALYTICS_ENABLED: "false"
TINYAUTH_DATABASE_DRIVER: memory
TINYAUTH_OIDC_PRIVATEKEYPATH: /oidc/key.pem
TINYAUTH_OIDC_PUBLICKEYPATH: /oidc/key.pub
TINYAUTH_OIDC_CLIENTS_FILEBROWSER_CLIENTID: filebrowser-quantum
TINYAUTH_OIDC_CLIENTS_FILEBROWSER_NAME: files.aggtaa.com
TINYAUTH_OIDC_CLIENTS_FILEBROWSER_TRUSTEDREDIRECTURIS: https://files.aggtaa.com/api/auth/oidc/callback
TINYAUTH_OIDC_CLIENTS_FILEBROWSER_CLIENTSECRET:
valueFrom:
secretKeyRef:
name: tinyauth-secrets
key: TINYAUTH_OIDC_CLIENTS_FILEBROWSER_CLIENTSECRET
TINYAUTH_OIDC_CLIENTS_MDVAULT_CLIENTID: markdown-vault-mcp
TINYAUTH_OIDC_CLIENTS_MDVAULT_NAME: markdown-vault-mcp
TINYAUTH_OIDC_CLIENTS_MDVAULT_TRUSTEDREDIRECTURIS: https://md-vault.mcp.aggtaa.com/auth/callback
TINYAUTH_OIDC_CLIENTS_MDVAULT_CLIENTSECRET:
valueFrom:
secretKeyRef:
name: tinyauth-secrets
key: TINYAUTH_OIDC_CLIENTS_MDVAULT_CLIENTSECRET
probes:
liveness:
enabled: true
readiness:
enabled: true
resources:
requests:
cpu: 20m
memory: 64Mi
limits:
memory: 256Mi
service:
app:
controller: tinyauth
ports:
http:
port: 3000
ingress:
app:
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: login.aggtaa.com
paths:
- path: /
pathType: Prefix
service:
identifier: app
port: http
tls:
- secretName: tinyauth-tls
hosts:
- login.aggtaa.com
persistence:
users:
type: secret
name: tinyauth-secrets
globalMounts:
- path: /config/users
subPath: users
readOnly: true
oidc-key:
type: secret
name: tinyauth-oidc-key
globalMounts:
- path: /oidc
readOnly: true
resources:
type: configMap
name: tinyauth-resources
globalMounts:
- path: /resources
readOnly: true
@@ -0,0 +1,10 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: tinyauth
namespace: auth
spec:
interval: 1h
url: oci://ghcr.io/bjw-s-labs/helm/app-template
ref:
tag: "5.0.1"
@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- helmrepository.yaml
- helmrelease.yaml
- middleware.yaml
- secret.enc.yaml
- configmap.yaml
- oidc-key.enc.yaml
@@ -0,0 +1,14 @@
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: tinyauth
namespace: auth
spec:
forwardAuth:
address: http://tinyauth.auth.svc.cluster.local:3000/api/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- Remote-User
- Remote-Groups
- Remote-Name
- Remote-Email
@@ -0,0 +1,23 @@
apiVersion: v1
kind: Secret
metadata:
name: tinyauth-oidc-key
namespace: auth
stringData:
key.pem: ENC[AES256_GCM,data:1LGq+HOfsvba2hrMBgwTiaJY4oSXNVRRyBQt7k+Dv5ZunCNbc57qxGIAi259zekb4yqdnP9SHUo0Uzmz1lgOUwhw4pPNq8YmWkjoLthjBB5FaNqilifUzFXYG3+fC4WP0IxHWUFDhB4tgP9zwnc682/QnQBMKzDXAL9bnRf8OTemNLnAjHt/+vbOtW6ySSmjnF+RRS0gz9/CjPwnCwfqqqRoIaiHT2I5nKuzSggGDNRENlGHsUCBypP08ZbuyQ+wRMWNDhtuG8KY8pWxkvOfwAjbJtJ34/xTGeWwCUQ0fs6khMXzMmvnPKmG/HTAAWrgLeMlhicDLB74nBMY7zbjX1y2HzJPVXAnWNE2sbWXhBIhi+/koyRdWSoUoD7flEu1O44cgRJSh2QqEa4cw4NC+tRm0Dm0R37vOEgMHF/D3cKBxSmgKLxUKw+ZCn5aYF2+Dwo5L+HtuI0VnQ5caq0HCrPN17m63gceVS/mbGYT/A9XSvxhWh5hrtzzn1v3xER/HdOEK1GlZBOL2QPfzyRx9Yx05f/NLGghK+Jk/OBtaV8+0ORGfjJFi+uU2tus8gznESTtZGG6Oz25aIYj7cYCNM93szk2q2lle8wCK8DX48wD5LEwS/7zcWJ2J0FeH/+ihIi2N6cRAgT2P5CHATFX4ghQkAH3UZF0oC2x0EfRXmVE9K2twuD70CSqmBIZvSqC1zEb5KpL283E5lsfVZu3RRryplONMaZZM2bIJqwaEq0wqbtvTnKFTWQEA4JRzgy4i8cFyxfq8ELoEacsjVgZCrASvMjK9NmuqUt4dbKMSuIz0S780Om1V0OoshJiGi9Thu7AWIi0WURRi0ZkvaiLDuqwx+VS5Vt+1y6JByUlsKZg4Dg6saYePKEqClD5nqTv4KsVU3psldOdlS/S4MhBCK31RPAI0yZbDF7job1K4pYWpzuJNH3jh/EDqzbBbNpnTnt6VA9KJ6obJ1vzOVAQB804WB0+hM8ap8vcqHLRPhqa7wZ9mJKVkPBxcr0B3ZulXn428FpjEzCLhUPXVNdJsxpPq0LtmN4yHsfGA/BVwXveT2gLxF+cT5IHOpQVR1ZI/I62ebCfupHYPhZ5aU/UFbCykc2PPQTVM5acVKnwbkU9nR8PmrrZxNmgTQ1akrrQoAMkPkKBsE8R3MIH4md4QhH1B7NV79OwJHrShGDajCPPkGnvFP6w+S4ecdwLkutg57Vhf9Gpq81ztzaq1W/W5y1ax4rGowlRU5nOHy5hDfyLUr0O79319oit4LD7VVWzFiBxWFNaky617V9B/xXFqxInGmQAJVPoPkCE9cHCNVbrblAeND36zjcr/tEWYlNCakt3sNFRQTWaO0MoF1+iLxCQvK7dpRpA3IhOPhFdBRj11J9PI+zpeSXZHYXP2XrfYna062KYb7BxiRiCrO2HAtgKY2TTcA8LHYQ+QiNefSqiNSN3zI7vclb4D6RmDpbm8Hg+SZV1KdvBYw/laj0ctNOnTqwZY51foDgjDnhbRcR5M1mbpq3LUY6TUUoV4+blR1cUKk3pQD5eHVEB/gI5r/mLoK/aesDhVsqEsI1fmwzGESJOL2zRXwyzuPrWj65yfcN0dDK/d06o7vnHdaJIPrEaAmX0M+jD92thIrrUMgkD6r6WqtCYKy81tKqBkMIb4M4wO1ttZ/6WjM7LjNfGAeYhMOTI/LWjmHR/JKrAantSgRgsE6hNGA+C8VUQTpO1rDWFNXRb/vxsibFSypi481ydaySw6Qum/f+eLrLGJdIgWCtI3H+L42N80+k5l03pyuJixqCk0kqVrDQmITNdkGJvbqA7uR3YncSOjPIWn7q79KUca4qW+O1CwtgBJYJqSJiDCVwi3tZ6zv7eDBcq7PGrN4R3hcarY9GVo0Vr4YdSN9VU5PkaXFfG10XZyrHBOLmm4/2iy7NxxWY8d4Z3nXUjitEIAkUa/CGhA3WMiqMkkbeLeiRtmbXZ8ZcCBO5FEUSG/z11PyLRi0o5kct3GlaUtl/QRg4SBGx4t3iDf5gHPb298+ulSErL40GjKLyiZh+YfNPDqRdIXBpKTjOand1EZe1yiY3otCj9NVUdrvkSFL6lYXcCQvVtzLIvrfev/QVqpIOf9d0gxp/em4JTZ+YauRNu5fXcnqezncpQAcXy27O4y2+4cko8tk7aoBwcrl/fKp13nx22I62IJGzsLA7BNjRHUG13iddZoBOzhbJ3xlgNR0nNlIYNSQ==,iv:ZO366aMLKSUkaMdlejlGGZ8rLAQITDJ+rtqHIDjHJCk=,tag:m3eUo6FRn5Gmrf/ozMGldQ==,type:str]
key.pub: ENC[AES256_GCM,data:F0YQFFEAwB+bruva4HB4BKeIjAPhnssnd13JLMRMyLMfF8JdIBbjyKcz8NUVq8l5FxFDy3W3jAJmOfVqUwHfajgdT61S2Rj+dBXwV947ruwBiLS3rTnJvgfLIEOf1f4mG2hZH4X7jodInh7wL+IPC7N9Wqmccdfc1eTvn7GjM5KgU6yzsDIdq8CMTOWciRIgLz81sDCP6mLAy4AG+a6nyzhCYSfoQogbl00CP9i6JdV1FcJfrzQY2lgshgrDOynK2bNxwaRP0CVajAn5ccj8sawvIA5EgcEdoasfGGf3WCUnFKDVmYZDZjpeTGDnCRZMFIpKfXbNavaNn2Gwl1icR58UFKsi7tXCePY8MW12gseONFXDoU6OKo+xtgiEk5SPryrskK3m7Dq+9O7No12f2XNhISGlf/sRtY+WmxxXceUTywoOHVRkbcapODRs0svmJBeQZKJ85tE+Fre1l+1mjLGB4ZeaA0euKEZDtTXT95icepMaHIcbZHURwp5jRfYxp7a9VzpWY+m1rwNtnpHe4A16JoU+0ZARPLCs8kX/DqgZDaSy9f+IUjvQYbNTsUNEJCvtMZPWmr/Vz8Je61/8pGz99g==,iv:L5JDiEFJnBPcXGZMmFUVC8Uq12Bb08qYFWhdZCYBivY=,tag:Lpz7BAOaQ1GK9W6WaZ4LLw==,type:str]
sops:
age:
- recipient: age1rvyt7nlnjqeyt6ykwh9fj9d88kjmq9vetcx667ul3ql6fyfz4gqqh9lq9q
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFZGwrK2lEVXBWQnBaeVlx
aUVPQVRyOTB2WE5HdWR2VldUbmQxZTBuTFIwCks1dkc5aGVXWnpIN1NYNVA0em14
c3h4K3hLYkZlU0VhRUEyajFEZzI3UDgKLS0tIFpLQkxlWGtUTGhoVUpiSW9WOStT
blZOcjI3TS84K1hZY0ZxbS95SndsUUEKt3ZYkU2/5uPeU7C72luZYP3tLJJ73ERX
A8eUMsVOI5T7Y6/i4B1OQSt1xFBXkMSJu3YU739fxFumdb8juALxzg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-10-20T08:48:16Z"
mac: ENC[AES256_GCM,data:IcHXiY7PJpNy/sGirwh7rm8XzyuC/ZfajaxolOgfP0p1bMF6yn2tzgf1vyHJszVEwXE7rLZEriR357PaHyGgkIxDnb76FwLTiw+feEBgpl/77HY1oOnI4yc0LrHh5fkanxadw/krJoQ8JD827Ek9h1LR7ICTJ40EDSpdfwdH/1E=,iv:OXMfJyXrXH4S5v2cTdwJijzE2/4L475ZArA/nw06s48=,tag:ykl0rz2xmhJ9NZIjuU111A==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.11.0
@@ -0,0 +1,24 @@
apiVersion: v1
kind: Secret
metadata:
name: tinyauth-secrets
namespace: auth
stringData:
users: ENC[AES256_GCM,data:C3Dhfymnr+WLWy18EpY9upXn+f7sY6w0cf3oXO9352PTPCP+rLYQTm8eiJtsU8gp/Uea7lMpsRPHSy8CoRdI8DOGT+hyPyK1ovjLdyO7bEiq0TtYOIFlHSR7Kwo0Cm+pZegSN9e1dm/v51+YyFfX3JpLaymYrlmXL3sFPQ0yOzEuMp3zXSV3VAkJe6gZrswIAUNhY0xmaGItr4Vc2isItDuc16lx7tW2wJHI7NgLI98p9FyU8HlXd9MsiPMxDaXSGeAwnjGU+TEvQv9NbhcZO0sUocSi2zgnRbi0i8Zt,iv:5OVryBqH4VNC71QdZkqZsoDrfbT5++Z5xZjgCFNOAu0=,tag:V+OC7Mad6Dw38u91gp6d/Q==,type:str]
TINYAUTH_OIDC_CLIENTS_FILEBROWSER_CLIENTSECRET: ENC[AES256_GCM,data:kBA3jDc+xFyvLNtfPjO+gYtRrT3HgdsoC1MktIgFomA=,iv:JKStNWy9BHcpkSwAG917sC4zzStKcgtU7ZXpKdSraRQ=,tag:VV96+U1EaIQevZHnCDmQag==,type:str]
TINYAUTH_OIDC_CLIENTS_MDVAULT_CLIENTSECRET: ENC[AES256_GCM,data:SiipG4qFp536YfrMqfjeZyIA5hxXfDH1JgGGDrkz0bb2mwdSV2S3szLffxNwibL9xAF/dST6I++N5ErLXGH10w==,iv:hzJyoLO1z4eBuDmBYJ5AfSh4RPggJN07EWeKbP+FFV4=,tag:R46K78XLC0BmQ0n6b+irQA==,type:str]
sops:
age:
- recipient: age1rvyt7nlnjqeyt6ykwh9fj9d88kjmq9vetcx667ul3ql6fyfz4gqqh9lq9q
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJS1llcTdHUmx2ZXdjVFh1
ekJheVN2MVBwa0V1a2JOY2pFM1pmYUJrMzFRCllaRnlnTGFVYTlQeXVxNXFsNEZu
bWUxd3BWZTlEOWxsZVRnREx6YUJ6em8KLS0tIGNDd1Znb0dlSDZEUE1JUkdWMlBG
ajBNVW1pa20zKzhISmFBUmdiazA4T1UK0wyvTMGsyJthx2Py1LITvRePQsjKN/9v
jqKjeeCuVutQCo3FvoVrlkkmiD19jqPDoXjWz95sTN6QjWmP7u6AIA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-10-20T08:39:59Z"
mac: ENC[AES256_GCM,data:pKMx3OwJ5x41WghATDCwDNJADs2KCQkDmDsjfbL6Vx98jFOL+MwvCRujOM2UzzAl8QnNLFddimmpNwoFBifQEbeHTpVnT1uqZkiSV89mM1nIoyYIo3exNoynVUoqp1NXxhfwRrRVC3beF3keTxES//kh8/W3as16R1vgBSLoj6s=,iv:AAiLBicP0tywLWXA67cin51TqV8TBXtlpjazta2PyFs=,tag:wSkJBOIhryUNVlGeKrgmnA==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.11.0
+10 -10
View File
@@ -4,20 +4,20 @@ metadata:
name: vaultwarden-secrets name: vaultwarden-secrets
namespace: vaultwarden namespace: vaultwarden
stringData: stringData:
#ENC[AES256_GCM,data:bFZ/wUZ6oINU4pYH5EnWQSyOsLngnr8CMgIMDDNTN9F1HCqPQMp2TQjh5nLpBIWegJ8VxhaGc5+d,iv:Jvrw4+Cuec/X/AV8iK+SmO3P7C8Ua2ko9vTOBUAhSEI=,tag:bQ66e+Uz4BAbMEPzbONFaw==,type:comment] #ENC[AES256_GCM,data:b8Yuhc+eN6k2X6xPhUUBAMyNmJhZl+cpszwhb9OUiZeDJUNC1CUFTSD4//XH81kud+iM3/6o854S,iv:0F4/f5sjeFBqp7zZfJhYd8tkiDRlfKJ6p5n337THiWo=,tag:ZNHbaR0tbHT6aWzyfqeqPw==,type:comment]
ADMIN_TOKEN: ENC[AES256_GCM,data:yYxQ9/DkzA8KCZYqsDnQbkDBynnLTHZ/CTnZQvl2sijMaeHAeAgFDUhFC8ew8ud6Gu9vvh9V1hUHeBnsxKASxmuNP9HOOSu0irpcgUDYU5nxcAhEVzgKvP9JGhc/XinbA/7jVxXiEf2I6hEApGylPQmoIe9QJw==,iv:hqNaSZovw+3ao5059kgwJQ024HohHRXf02FR2/rLWFM=,tag:ez/enc/6s8CHoJ+264gURg==,type:str] ADMIN_TOKEN: ENC[AES256_GCM,data:pYY8Nc2PevrFGU2pbZaFH7O+WNJzOHSEXMM3TAFYHO46/GGzQjrxYbc9IY/8PgY86N73kQAFLvw2bpVnjiliilLpnxm9Z0LyYDlAhui8545J67IqoQFDyZt5o3NHceWRP2eCxEL4Bf9gD6j4YfObRVYrp38x2Q==,iv:IvzPkKi/w/S8ueYl/m8kckWiddfl1oQKYiT8S77IVAA=,tag:rD5RsmPt4uihMHWSt4gGwA==,type:str]
sops: sops:
age: age:
- recipient: age109zta6l0cwpkxn5wek82kvauxn6yqrq80rsz4mvu5atdnpcn9pusuvnqk5 - recipient: age109zta6l0cwpkxn5wek82kvauxn6yqrq80rsz4mvu5atdnpcn9pusuvnqk5
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBncHhhUW9MNkUwcHlMS1Vm YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6RVkzM041NFZDMzlYRllw
dERHL0dyN2ZIVklSSkJwRnNQR2dtZjhpWHlBCjVVVGN5ZUhtdGpNSEhXUjhCWkln a3BRS0JGZ1B0UGYyaTRLUkp2UTJ1S1pybDFnCnZhVWNYMFRlSXlFVTMvRXlpdlF3
b0h3TkpaN0UrRWFuZzd2d1JiWlRIV3MKLS0tIGJKNE1jSTVxamsva2tUSUVpbzhj Vk1xT1pKV1hzSUYwQXd4ZG92cnhDY0UKLS0tIEtLWXZiZUFDeGhFdytKbTJHSjRH
KzVYek9IZldwSStXN3BCSXNvcFVNTUEKRivXr6lUXL/45wgxPIuQAqfgSzZe6Ofz YkJhS2FJOTdLbHlRelBpUWRNUTdTMDQKhgb66tV4L0PKkPY8DvbypTpcBlavUvRH
vT5NsMte67eCYwmAF/+wSPZP5Y7OI7UzPedTUIMvn5vUMy70vssB/Q== 0+Rz/UxfZpX8u8G9GeqF5glIpNG2x28VSOHchJxhvMKHIUq9ZUrHGw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-26T10:29:48Z" lastmodified: "2026-07-18T19:13:05Z"
mac: ENC[AES256_GCM,data:Td2STb6nbH3Q383YxUMgecnmBRnsH1cNq0Iv1/q5OvESpIzhVCeVgJMIKBWEQJNfuxdyXR9mbFJ5zELIBS2ymGihtr4pHMKxj/kFpj1zPBqy+OKhP+E67juiRVMg6DJDSbdFHP21dOetllkcRgG6SImuOPQXQ3eozmPg/vpi3Xw=,iv:Q1B8KUrzBGIb2wBTZPjdb0a7XMyp5BpDf9qa8wx0smw=,tag:k54imcB/x03ttFVQKamKiQ==,type:str] mac: ENC[AES256_GCM,data:Ufdytxb9uH6JVXsWu39YhE1AfD/oG7bXhIwoTXYhYBE4IWvugrwZLCvBrgkfuazVXyKBuDIuEa5Vj/bzlTGDSlRNx7SDqabvilhmYAsBcuU5DAVU47pDz1YnXeHd9TuQI1dHAc1Q3uvkZwWNxh2YYXKZGwNMy3tOAkaOxwI1rMw=,iv:iUZenTP/9JhmjcR4p5+RM5K2YuXBLnRj0cd/RHseqFY=,tag:pesF8Pp8vBoQXcniwrep9g==,type:str]
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
version: 3.11.0 version: 3.13.2
@@ -90,13 +90,18 @@ spec:
existingClaim: ganesha-export existingClaim: ganesha-export
globalMounts: globalMounts:
- path: /webdav/export - path: /webdav/export
# NAS may be off at pod start: the PVs mount with "bg", so the NFS
# mount lands on the host later. HostToContainer relays that late
# mount into the container instead of leaving an empty dir.
sda2: sda2:
type: persistentVolumeClaim type: persistentVolumeClaim
existingClaim: nas1-sda2 existingClaim: nas1-sda2
globalMounts: globalMounts:
- path: /webdav/nas1/sda2 - path: /webdav/nas1/sda2
mountPropagation: HostToContainer
sdb2: sdb2:
type: persistentVolumeClaim type: persistentVolumeClaim
existingClaim: nas1-sdb2 existingClaim: nas1-sdb2
globalMounts: globalMounts:
- path: /webdav/nas1/sdb2 - path: /webdav/nas1/sdb2
mountPropagation: HostToContainer
@@ -48,6 +48,11 @@ spec:
mountOptions: mountOptions:
- nfsvers=3 - nfsvers=3
- nolock - nolock
- soft
- bg
- retry=525600 # bg gives up after 10000 min by default; ~1 year instead
- timeo=50
- retrans=2
nfs: nfs:
server: 10.0.64.80 server: 10.0.64.80
path: /mnt/sda2 path: /mnt/sda2
@@ -85,6 +90,11 @@ spec:
mountOptions: mountOptions:
- nfsvers=3 - nfsvers=3
- nolock - nolock
- soft
- bg
- retry=525600 # bg gives up after 10000 min by default; ~1 year instead
- timeo=50
- retrans=2
nfs: nfs:
server: 10.0.64.80 server: 10.0.64.80
path: /mnt/sdb2 path: /mnt/sdb2
+9 -9
View File
@@ -4,20 +4,20 @@ metadata:
name: webdav-secrets name: webdav-secrets
namespace: webdav namespace: webdav
stringData: stringData:
WEBDAV_USERNAME: ENC[AES256_GCM,data:leD9Xfk=,iv:PLT4ctrHZUoSVCxkel99UyaI2FQVCjWq/pgCC7u/W18=,tag:W7ypg88sYu9tNJOxjlgeSA==,type:str] WEBDAV_USERNAME: ENC[AES256_GCM,data:BeN5VS8=,iv:XpUr5PG9RWTRGXs7/EzDyLGwUm05Mr0YWpW7PwzNfm8=,tag:3py47tIcTeFxuuAidk2wBg==,type:str]
WEBDAV_PASSWORD: ENC[AES256_GCM,data:mpVRd5XzbQJdJKrEuixG8DVBmez4IW4+rp3/47QfGs7TLnMkJ3olPbHHleYFv1z4,iv:O066w8Sf4V8V6SFNjV3YoFUTm22vlSg8458GdSmo56Y=,tag:AHd0KrEqpBbsj3Pm4AcR/A==,type:str] WEBDAV_PASSWORD: ENC[AES256_GCM,data:Rh1ryftcmxgJAHvC/N1o5QeagCvyb3XIpDS7jOTCpJm3Wb5rUcLNmtcW22f0xljw,iv:GmgQJEuzUzSEql+fx5KURKv8CnhXF/z5UlmEn4ta+K0=,tag:AZD1CEbVRm6DiBt66pB9Vg==,type:str]
sops: sops:
age: age:
- recipient: age1y8t9v56yuvskznddqfsd4yddzzch0yf2dutvvt8rrx6rjh5f9g0s3qeays - recipient: age1y8t9v56yuvskznddqfsd4yddzzch0yf2dutvvt8rrx6rjh5f9g0s3qeays
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bGRvZEc5Q3dVNHM1ZnIv YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5UkRLNWVyZUpLREtZcm8r
cDhGKzVhR2UwSnE2VW5JQVo5UEcyTzlXQ1M4CnptNERCNEMyUFh6aTFrc1FHV2pW V0VPTkZDN1pSWDNxWFZXOUlON1c3cHhSZFVzClpEMmhvQnlFMnF6aXV6MjJnMnBu
RWUvUGRNL3U5dnJhK0kwSEFFUzhneHMKLS0tIHgrWHFGMDgzdVpMRjFFWEU2ZDZC c2pxTDBGZVBUYjZNR1doQ3UraDQ3dlEKLS0tIE42dTZWMUhZdnVMYUNLTGdrSWZC
SUN3WFMyODdxdlNoMlhmbFJUbEJQejAKRoY0DzKyIQmeyq9yf0IZe2A5LPVyA+Mi UFM0V08reGVmZTRMUWw5T1R2OUdGWW8KWAZq4TG36Njlw4//vPCxzM1Qqr/6Ievw
aShqop1PqAytaLmizdqryTR9gfGACILuY/bzBhs2MzvTLaaXzkExbw== taBvLXpw9wkrjfZJ8gBU+0Q8WrsEy+tYtR9chfz1n/SmiGHCkskFGQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-26T10:29:49Z" lastmodified: "2026-07-29T18:57:24Z"
mac: ENC[AES256_GCM,data:9iTiXBfG3Ua05tye8fhtcccE+Px6nef6ZvUrM1IlLNNvMg71A7s24/4WTHrSptLtcswOXLKtLrV9uHBg4ssT3HMOer7RBb2kmkUBLKJPwNKBZdQOo/YB2ETA302IGEeBQNLLsNSrw4PiwSkzTMI6PErsAbdxRn80hjFig+gyozw=,iv:YetHHW1EpB/saKxvEWO90JQTEPPF1EOHD8h+KNbQECA=,tag:B8z9mcYTQibaGi4pUGcJeA==,type:str] mac: ENC[AES256_GCM,data:bW2QvjSbA4JmfmZFzC1oIfEnVBxf1pE4jtKDsp2sf7mozVEdqMTD2nNpq4Pro9zRiXQfzxQAXAyzT48dBhIunXKDNIS7vbPpLUbs0nwSF4Dgp/q02ZX3Vrewx80iz5wdMYvLk6c2viZ9ndMwRqBbdBBsuSa5O9nIVj1edG9tj70=,iv:8bgXaA9RiQtbd1Xaht5r5NB3lFedX7Wi/X4z10VLLuA=,tag:Qnr8Ij6XN/FMcQf9Kx1IvA==,type:str]
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
version: 3.11.0 version: 3.11.0
@@ -0,0 +1,115 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: woodpecker
namespace: ci
spec:
interval: 10m
chartRef:
kind: OCIRepository
name: woodpecker
driftDetection:
mode: enabled
values:
defaultPodOptions:
securityContext:
fsGroup: 1000 # non-root image, fresh longhorn volume is root-owned
fsGroupChangePolicy: OnRootMismatch
controllers:
server:
replicas: 1
strategy: Recreate # sqlite on RWO volume
containers:
app:
image:
repository: woodpeckerci/woodpecker-server
tag: v3.18.1
env:
WOODPECKER_HOST: https://ci.aggtaa.com
WOODPECKER_OPEN: "false"
WOODPECKER_ADMIN: anton
WOODPECKER_GITEA: "true"
WOODPECKER_GITEA_URL: https://git.aggtaa.com
WOODPECKER_DATABASE_DRIVER: sqlite3
WOODPECKER_DATABASE_DATASOURCE: /var/lib/woodpecker/woodpecker.sqlite
envFrom:
- secretRef:
name: woodpecker-secrets
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 512Mi
agent:
replicas: 1
strategy: Recreate # RWO volume for agent id
serviceAccount:
name: woodpecker-agent # created in rbac.yaml
pod:
automountServiceAccountToken: true # kubernetes backend uses in-cluster config
containers:
app:
image:
repository: woodpeckerci/woodpecker-agent
tag: v3.18.1
env:
WOODPECKER_SERVER: woodpecker:9000
WOODPECKER_BACKEND: kubernetes
WOODPECKER_BACKEND_K8S_NAMESPACE: ci
WOODPECKER_BACKEND_K8S_STORAGE_CLASS: longhorn-no-retain
WOODPECKER_BACKEND_K8S_VOLUME_SIZE: 5G
WOODPECKER_MAX_WORKFLOWS: "2"
envFrom:
- secretRef:
name: woodpecker-secrets
resources:
requests:
cpu: 20m
memory: 64Mi
limits:
memory: 256Mi
service:
server:
controller: server
ports:
http:
port: 8000
grpc:
port: 9000
ingress:
server:
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: ci.aggtaa.com
paths:
- path: /
pathType: Prefix
service:
identifier: server
port: http
tls:
- secretName: woodpecker-tls
hosts:
- ci.aggtaa.com
persistence:
data:
type: persistentVolumeClaim
existingClaim: woodpecker-data
advancedMounts:
server:
app:
- path: /var/lib/woodpecker
agent-config:
type: persistentVolumeClaim
existingClaim: woodpecker-agent-config
advancedMounts:
agent:
app:
- path: /etc/woodpecker
@@ -0,0 +1,10 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: woodpecker
namespace: ci
spec:
interval: 10m
url: oci://ghcr.io/bjw-s-labs/helm/app-template
ref:
tag: "5.0.1"
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- helmrepository.yaml
- secret.enc.yaml
- pvc.yaml
- rbac.yaml
- helmrelease.yaml
+25
View File
@@ -0,0 +1,25 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: woodpecker-data
namespace: ci
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: woodpecker-agent-config
namespace: ci
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 1Gi
+33
View File
@@ -0,0 +1,33 @@
# Agent runs pipeline steps as pods in this namespace (kubernetes backend)
apiVersion: v1
kind: ServiceAccount
metadata:
name: woodpecker-agent
namespace: ci
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: woodpecker-agent
namespace: ci
rules:
- apiGroups: [""]
resources: [pods, pods/log]
verbs: [create, get, list, watch, delete]
- apiGroups: [""]
resources: [persistentvolumeclaims, services, secrets, configmaps]
verbs: [create, get, list, watch, update, delete]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: woodpecker-agent
namespace: ci
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: woodpecker-agent
subjects:
- kind: ServiceAccount
name: woodpecker-agent
namespace: ci
@@ -0,0 +1,25 @@
apiVersion: v1
kind: Secret
metadata:
name: woodpecker-secrets
namespace: ci
stringData:
WOODPECKER_AGENT_SECRET: ENC[AES256_GCM,data:UyYPJpkHb2G1Vpe2tEtkNDMktp6zFbeNtDF34Kp8vf6nAY5/2EYXBJvT4+Ns2oOGNZOupdCUmTowrJ+ITNFwhQ==,iv:EtXILwt+FFYvqvLw9kLODh0wyhrHG+GJNGZRu6wzXd4=,tag:Ly5dsTWio741VxgtPy51XQ==,type:str]
WOODPECKER_GITEA_CLIENT: ENC[AES256_GCM,data:hXBo5EwhHuCTQ4vQBAmjnLTlQ6cY2dKeJ5vZJ27wMFUsKZpS,iv:Pzt78GVSWngjVvpVSEgvsImK+siHEHOqQy+jOAxrGkc=,tag:JYu+Jf+n9J4L4LPwRPPbSw==,type:str]
WOODPECKER_GITEA_SECRET: ENC[AES256_GCM,data:nu4FTBjdJe7oHx2Vt1ElxJXVnYzONC3kKYlXwvtmVI3/DokjSGA0YwLy0jI58l7K9nBbzGKuuZM=,iv:U6mfIG+LYhSfooIK3yhJay+fQh5oCNyGUggN+MrY4os=,tag:Oho0FnhePMLhFzsyhFISZQ==,type:str]
WOODPECKER_GRPC_SECRET: ENC[AES256_GCM,data:rLiZToTcqf03gY/psoboYUsDUC16/0ZPkgOo/5ABrOvuyvHFoJPnAlvYhvSu/+qCIU1vlv7AvhfEJyEig+ULaA==,iv:q42abGbWSQb1noVhkXwiAWoIXcDUT01MTbnhaV5Plos=,tag:gioDH0cIPGoRD4auPEq71w==,type:str]
sops:
age:
- recipient: age13qwkssk8esashln2j3gfv6e9csashr4vddkdav5v38hm5wejl36qd2ew9s
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1anNnNDlnSjhMU2h4clVV
eFJnNEx6VmU1RjR0a1RQQTRML3lBaUphSkU4CnNuM0I2NkFPMlU1clFNY3V5Sk9Y
b0NJNzlUMjJsRVZMdndzUWYyNWhjeHMKLS0tIEp0Y2E3dTRwRTUyUmprRktFNUJp
TXlXWjViSGNFb1FhQ0xDT0gvajdwSlEKXB7N64RmaWgCCSvG/nNWOziO7QnN729i
jJLB6SPiHjgNMn1GP+rsAstVFStB0fn2mh6QpWrdhAM3/NFBqQfy7A==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-10-07T08:38:39Z"
mac: ENC[AES256_GCM,data:+beUA2t2YPqjsk7KXnPgJBiiQSjB7qypni669F2W3YNdTXx/ChenMxo7srjvejW3YfzPccbnBY6/TJgKCLPGm/Kf0hntejbBgYeu9hltSrIZX28efoE6tq4NicqJkPT8oD24v++H5jxRZP4epmef5t6rzJSq1xc4ETZerK0f9PU=,iv:wekT7WxL/Xm+vqCYCsZ4nvwl8VeOugURNvbHs/KQhRk=,tag:gH5efL/YUtJEewcY0G3Ctw==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.11.0
@@ -38,7 +38,7 @@ spec:
zigbee2mqtt: zigbee2mqtt:
homeassistant: homeassistant:
enabled: true enabled: true
legacy_entity_attributes: true experimental_event_entities: true
permit_join: false permit_join: false
timezone: Europe/Moscow timezone: Europe/Moscow
@@ -75,7 +75,7 @@ spec:
ingressClassName: traefik ingressClassName: traefik
annotations: annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: auth-authelia@kubernetescrd traefik.ingress.kubernetes.io/router.middlewares: auth-tinyauth@kubernetescrd
hosts: hosts:
- host: zigbee2mqtt.aggtaa.com - host: zigbee2mqtt.aggtaa.com
paths: paths:
@@ -4,19 +4,19 @@ metadata:
name: zigbee2mqtt-secrets name: zigbee2mqtt-secrets
namespace: zigbee2mqtt namespace: zigbee2mqtt
stringData: stringData:
secret.yaml: ENC[AES256_GCM,data:RdMKYeSHeJpHVJqglYAOyfdDyZyuc7KQheiq0vaxb5fs0DrgOYP2EgMKOcKA8Lh933W7u9qx/VrkOjBz+fm66vyAxwQKXvGZjnrM4RqtN0rW5UMG9C5s9zyQrepVECsqDiNpC2NCzRE9DsWX7Ux6Ak0RpMV0wkX1fAzVVwE=,iv:Uee5eN7qFyI2K7VgUEnTP506zy+ZK1uNFkemax48DhU=,tag:gvFAAUva/gJy1gBeZek50w==,type:str] secret.yaml: ENC[AES256_GCM,data:mDb7Vt4sOSaqn9+T3QEAR0yutRIFwiYpBBZDMQI4FMvAPWPkAdsYTIfvv+41JbpBafklDJ5gzPPKZJ6Qk1f8KBvjRuHP/bJqTUPrv+Q46ZKsuXECQq+qB8TKAj51FiJssyx0jYy9bCYgn+Z9r+WBT4YSAvv1XS/UHs4S8UA=,iv:8RHjs0g1ee38yUSHjvQv06Z7xbzMOG6LNzCJ/7s4+Pc=,tag:jb+VgAVApqIpN3lERqrfBg==,type:str]
sops: sops:
age: age:
- recipient: age1tu4xgfefvgdda0vn2thaqndq4p9tuydq2ud6qa98tzzgnff6749qnlcx29 - recipient: age1tu4xgfefvgdda0vn2thaqndq4p9tuydq2ud6qa98tzzgnff6749qnlcx29
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0Z3ZTdXMreGl0OWdjSkI0 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzcUFqU2RpTXUwR3hsSEJr
T3lWRTNrUUJrSk9KVm12eW1na3AxZCtuNURVCk9UMHpOY1R0Uk9ZT2V4K0RMeDk1 WUV1NkpqQitDRDF6MUpWMWlnSS9TaStRZ1hNClNkSEIzOVVSTUh6LzdXdTdld3Zj
cFBzcEE1RTJnUHFPVWZVaDh3R0xkQVUKLS0tIGxVVVNWSlAzVzR2dWdDZXFtTkNR U1pVcE5IaFhuQ0lTZlZ0RzFDSHVkREUKLS0tIDdTbEJLbE9KUmxRei9lVlc4TW1t
L3JVQ1ZCY3lRL25SLzBka2RBRFVNRWsKu3/3f9tH7GyzCEENtCTGDyK6l7lvQMw0 TE0yUERpQkpqZFg3aGpvSkg4M04zMGcKcYmSgyzhpZ8BGAp+0zkQ7+ZnF2w/Nk9D
GSOZzZVClKfbCDrdjAqgly8ouGeYx0X19KtoHmggfH95nvcYheFFTA== ZT8zwqCJcaRldg59JzmhLBfiuYAtnH2wqWj06LiIfvt+P3pgY79f3g==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-26T10:29:50Z" lastmodified: "2026-07-19T09:01:11Z"
mac: ENC[AES256_GCM,data:ZJAwVpj9OKeLqN3kEVtHdlouks29xb5a9DxRUnq7pNmf5lww8GKG1r8nfm9t6Z4JeuOBjN3zELjuEC9zBJ5Xpcdp2Ru8e3oD2816fu0feuiVkTXjaSrq1ZfHezvN5eHQqTU6Pmx5vSQcy8c7e61mI0VyYvOIUSXY4zfd1mDrIGc=,iv:qqFi/nUg+KWi/aBfJqlXqU3lnn8NZ8LS9CzbJcLOlpg=,tag:oykm3cfYCn3m63ajTS4rRQ==,type:str] mac: ENC[AES256_GCM,data:fKfDsAg1Ya8c//x0xw9ytQR7hiF/BssuqlwA57PYiB1yjCW3Kddcdt95UF1amF+9tvJb2XwRjb2LwN4RrYj0RyScLmLa3u/l1r79EoUKQWQ4qF7GNdD5ZGaxqnWjgorxkfYcU7ahJBRDEIsvJXJivk6Wjm309ISzl8xR/tUpFCM=,iv:TmFo1gbRsATh6O0uJvJxwyj7hSzCfpPPuo55EkQQabk=,tag:CJ0cTn9BJs+87AvdwJ0MsA==,type:str]
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
version: 3.11.0 version: 3.13.2
@@ -24,6 +24,28 @@ spec:
upgrade: upgrade:
remediation: remediation:
retries: 3 retries: 3
postRenderers:
- kustomize:
patches:
- target:
kind: DaemonSet
name: longhorn-manager
# chart hardcodes readinessProbe without timeoutSeconds, so it falls back to the
# k8s default of 1s; under CPU contention (e.g. busy nodes) that's too tight and
# causes spurious "Readiness probe failed ... Client.Timeout exceeded" flapping
patch: |
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: longhorn-manager
spec:
template:
spec:
containers:
- name: longhorn-manager
readinessProbe:
timeoutSeconds: 5
failureThreshold: 5
values: values:
defaultSettings: defaultSettings:
defaultReplicaCount: 3 # по числу нод control-plane defaultReplicaCount: 3 # по числу нод control-plane
@@ -39,10 +61,9 @@ spec:
# чтобы не разъезжались реплики при 3 нодах # чтобы не разъезжались реплики при 3 нодах
replicaSoftAntiAffinity: false replicaSoftAntiAffinity: false
persistence: persistence:
defaultClass: true # the default "longhorn" StorageClass lives in ../storage-classes/longhorn.yaml -
defaultClassReplicaCount: 3 # the chart hardcodes staleReplicaTimeout: "30" and offers no value to override it
defaultFsType: ext4 createStorageClass: false
reclaimPolicy: Retain
csi: csi:
attacherReplicaCount: 3 attacherReplicaCount: 3
provisionerReplicaCount: 3 provisionerReplicaCount: 3
@@ -5,7 +5,7 @@ metadata:
namespace: longhorn-system namespace: longhorn-system
annotations: annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.middlewares: auth-authelia@kubernetescrd traefik.ingress.kubernetes.io/router.middlewares: auth-tinyauth@kubernetescrd
spec: spec:
ingressClassName: traefik ingressClassName: traefik
rules: rules:
@@ -4,21 +4,21 @@ metadata:
name: longhorn-backup-target-secret name: longhorn-backup-target-secret
namespace: longhorn-system namespace: longhorn-system
stringData: stringData:
AWS_ACCESS_KEY_ID: ENC[AES256_GCM,data:Yf3CM07ST98zuo595rsWL0ltYmc=,iv:+JOv1TEjFGygopzMHsQvpdaKfX3tWg1woLRtgw+hY4Q=,tag:+9flP+/Ap3cUwMWJ4LZj5w==,type:str] AWS_ACCESS_KEY_ID: ENC[AES256_GCM,data:QPvQnrkMSKK4Whj7DaMONoDW7LQ=,iv:K1x/VDp4VZUnC7u86wpEfVBDW7T9cI4CZojMZMCaS+A=,tag:eFAAnCMCVjNBHnFAhzFS3w==,type:str]
AWS_SECRET_ACCESS_KEY: ENC[AES256_GCM,data:rzAUUcv4de7AbvAziJVfAYlwwn4TZhzrYwNhICPp+CNtn5hlhoZOTA==,iv:EQhgZ8iJlI7yHX0Pu1GKGACfGiyNT9ui8p6MhSrdvn0=,tag:Oku1Q/o54jx1pY5K73u8Dw==,type:str] AWS_SECRET_ACCESS_KEY: ENC[AES256_GCM,data:zFjxH1uqnYRrxM4Yrll9Na+mDj16aLtiJC2ZlNO+2r70JcXJqnTASQ==,iv:B7eV80h4APk3QCuoDR9EFpKs5dkMF8GjQ2v9lSF/ev8=,tag:xLbWN0Gy2idB9j2G8HZYKQ==,type:str]
AWS_ENDPOINTS: ENC[AES256_GCM,data:upvLav5iZdAwfcZ7pJd7hgVRrTEn800Yh+js8KvCukZf1ybNSA2QAX9Dn3/c6gsmI9iJN8Ztg93MGCI85w==,iv:usiTtOM+uLcxVghNeoHo8+/bTIR9QfWiMz+ke+xPFDs=,tag:WPVw/nVNUSBbCUeVlxoKrg==,type:str] AWS_ENDPOINTS: ENC[AES256_GCM,data:HWVPThBb5GLpiRqAuSLvmAat0XiUu5R5CMv2Fv6jdieZ11lUmbS2MBD6lx0vQDSIzC36hd6Tu0hgqtknQA==,iv:XLqWSNHgj8O0uQPeBbnN05Za8G9VW6F802CCPoUoq9A=,tag:dsKXCq21hDlJy4KTyQTp/w==,type:str]
sops: sops:
age: age:
- recipient: age18hhmdmupj90zjgjpxk3z5cf3lx36hcznnmqsp0fpyg50823pudrqjupydt - recipient: age18hhmdmupj90zjgjpxk3z5cf3lx36hcznnmqsp0fpyg50823pudrqjupydt
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqSmJQYTFDN2tSZDBPYVpt YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqRndobTFybElvakN3c013
M2tub09CRkpUd0ZtS3M5TDdoU0k0N2FKdzJRCkNndkhaenBIQ0lUVWtjY0FCTTMx T3d6ekVUM0V4OVNEMjl1ak5XTzNPS0xKRmtVCjBXMjVybEs0bFZKN3ByWmFpWm9K
ZGg2eWh6eEFudzg0d0lnRFN5VnN1MzAKLS0tIDZqUEswbmdmQ2d4aXlUVFdFREFM TVVLVVBPYmE2NnJMT3Q3dVJRa250aXcKLS0tIEVKeXBGQ0x4ZDlIWENMa3k3S3Zz
WUkzNUZSRDgxeDVlN29aMFdWOWZRSHcKvoZaEwnjkOVw8pvkdpaTQvpbBSTuWUYN YUdxZXoycXBPbm5raWxFZ3owc2xkQmsKNzw+5kn73FZhApzfN/RBj4yAd7GLl5/X
XkzUPKQmtRLNp0bOgXyWpWWJj2i8ue5SI313g8GiJJWZZ4WesA/atA== wyXVy0te83ZDeVQvqhXLkUPsbF8CYewC6xibZSdSY/WBh2ojedJXrQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-26T10:29:51Z" lastmodified: "2026-08-11T19:04:53Z"
mac: ENC[AES256_GCM,data:Gq7jEOcEjsJmJutnkUQZ/mr4aU11bCgHFz1cK0U3k61A9IULUqyV1j0/+ZNAF4JsXv255XcFsn6qouvFbKlAw3mg0LtjwP4zIkhIj+eThZ+EqfuoJ8F0vvLFijohu+kRNyafLJpNGHEqNvTFyd8OXKMNZET74UTHV4qseBMIf5o=,iv:EdEQVzK3yRa4RgP09m2gZirszwLv8Efwpt74jS/lpA0=,tag:YpZOC1kvpZMye24Ha1KEXA==,type:str] mac: ENC[AES256_GCM,data:SO2tMq3UA0JcsTZyH3oNhmCsWZ2OhxEmJSn1b/3sH78cZvWalXUsdQqD1GBArFgS+oCSaSgarl2Ve06iNJtM8k6oq0fDkm0dxsLou+67qHlle4CbkP1KPFEQid2Nx8o/5b6cjI2zHz3bYmQTQh9DD7mE8roqo6lX3rYY35MpPJw=,iv:Ad13teWF+lZfzOY/PIALI3/Wohn/0BBoSuE0mtiH8/Y=,tag:sQVQfH0mFFMLnF4zbRC2VQ==,type:str]
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
version: 3.11.0 version: 3.11.0
@@ -88,6 +88,13 @@ spec:
enabled: true enabled: true
readiness: readiness:
enabled: true enabled: true
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 750m
memory: 512Mi
service: service:
app: app:
@@ -4,24 +4,24 @@ metadata:
name: s3-backup-proxy-secrets name: s3-backup-proxy-secrets
namespace: longhorn-system namespace: longhorn-system
stringData: stringData:
WASABI_ACCESS_KEY_ID: ENC[AES256_GCM,data:okf70ogiDqqAkpnSCBYkW9oATC4=,iv:iVs6x/2z2+gejRQMyXwpa/hXQ6xq7eXdMVA27IHiPqw=,tag:qXcpGptnTCipk/NX3Ph9Gw==,type:str] WASABI_ACCESS_KEY_ID: ENC[AES256_GCM,data:YP9bOIarPh6DJVo0vevVN2T3c3A=,iv:bTKwVx/Ij+GMCisO4WEDCJz8rsHe8PtSET/h4+P2dyM=,tag:fdXr+wAx/SvKNwlYlmKvRw==,type:str]
WASABI_SECRET_ACCESS_KEY: ENC[AES256_GCM,data:Pltg8OsVpXcET0qe5Zdwu38KBlR+oR6lT4yVi2rwNMmJxu4HryqMEw==,iv:oROBr2u09mdUVeffu/dyCIwmKabFMpOK17BL9uHx0VU=,tag:Tgmnqy4RWe4tvbEuqz5t7w==,type:str] WASABI_SECRET_ACCESS_KEY: ENC[AES256_GCM,data:Aa0ZYxiVipW2g4mGzyhCspVF67m7hnouMg4Tcr6kSinY+ULpHtEDBA==,iv:txr1ual5z7dXi/27T3FOMsccQtSPObHpg+7IfxvfWuw=,tag:MW7fl0iRB/6flbN+iA+2QA==,type:str]
CRYPT_PASSWORD: ENC[AES256_GCM,data:PbwJlJfz6jTM67qGi0tGkF9b5nVd0OhzRu3cTUn8IZVHIaDq7JQpykmq3g==,iv:UNkWK3DpY3Ux5g/SG5tAY5RU0spm1D/B+j5lp4eTxkg=,tag:0l9WxfSalrKaCDF4RAWoew==,type:str] CRYPT_PASSWORD: ENC[AES256_GCM,data:x8kMqkX4OzduFbW1BGc2EcFxaVm4ah4BBgLNxzQfIHeuL3UuJXt+Cj8isg==,iv:xoFXHqUDK/yamkDaSaQaMIkC7p+q/4qtsrYNwKT7944=,tag:92nej0/10KVTsugwFzdB0w==,type:str]
CRYPT_PASSWORD2: ENC[AES256_GCM,data:An7PTYE6Kp4ym17cFJ4HD+kiXbFgz0qOxRUTPjXFpoj++4QFzgG5PwnZuQ==,iv:TwcnYlyuN3o/tvQrxhuirBNL48lD66GzL4XUTdKzyIA=,tag:NoEp7DfdLCFQ8ju6Tk8adQ==,type:str] CRYPT_PASSWORD2: ENC[AES256_GCM,data:TdcfVmkgd36Hq80XaLRM8lY2dUq95h4v4ONCy/O7DOMoRwVxDUktgCRgNA==,iv:9EluJOnjKjCo2272sIeJ58iFu54DKfbkX785ZySj3C0=,tag:WUMKDLASA9grCilKNLVmZA==,type:str]
PROXY_ACCESS_KEY: ENC[AES256_GCM,data:AIpKJPLYOrda/lyklBKtkbfyBx8=,iv:b0xXnfhsp4gh/v+wYYSinKCpj0fZcgm+yrrOC5MYJWs=,tag:CNMVLrAouXmDxLqlkYI3CQ==,type:str] PROXY_ACCESS_KEY: ENC[AES256_GCM,data:A1ZF8ejApPiH3nISLvrO7+vWTzM=,iv:fSFwYL/6zxtK4wkz+G3WItzq6P92eGviQ7WaVquDYuk=,tag:n9fjuW/cXU7QWe5IUkXf/w==,type:str]
PROXY_SECRET_KEY: ENC[AES256_GCM,data:hz3GyFtbbTnVqWi8O+P7aRUS2IA0oDipgtBq8KPpDEyclv1rdBGkfQ==,iv:UqL4wYouaVuJwBlgJzfbrPfs7hksgyaY2a2cBPPUQn4=,tag:w4cYoFVkRodXC8dK9CG3IQ==,type:str] PROXY_SECRET_KEY: ENC[AES256_GCM,data:iU6GHL09tzxgyxcl6wKlDNhaO6YVYS/8yy/qCPbSxE3RlHk0Yw9EnQ==,iv:i0R6SzOJlEDsyEhxLrLMVaJu7ZAzNsnnmdyrxbv6ys0=,tag:h290ZIcFevBQeImcfqQbjg==,type:str]
sops: sops:
age: age:
- recipient: age15vc3geq4lsec69rnxczluvwgcexeqae02zxzjpswsuatthaasu7qzath48 - recipient: age15vc3geq4lsec69rnxczluvwgcexeqae02zxzjpswsuatthaasu7qzath48
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoVVVHYXlTM00ydVJjdXFL YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxR0hwMUhqVHFJSVBUckZW
U0k5clRFY2VlMStzZE9DeVlweDNDelJUTjNBCmNwbWw1UldxNG5XZzdZZzh1UStw cTdCeUx0bC8zdjB5dmhRNDJrc1ZOTnY4WmxVCjJNZEUrMEFEYmNGUWQxbHRZL3Bj
eFlQa2k4UU1DWUdlMUF6N20rOUNwSmsKLS0tIEtsbUdBNnZYT1BmRmg3enF5Nmp0 S3dnOGhENlJkV2oxR1RhSmdCWXczVWsKLS0tIHIxSnVSdGtKaE52UHNtaWVnRUNW
Z3p0UzZkS0VHdDhRbEU4Q3M5bnA0RDQKWcny31QSOW0t/XDeBspoymNK5vN1ltqI Unc4QUJ3YmF3N2xVVDg2VjBrZW80QmsKZ5HiOTiYRCrfWJf2Qpxyc4D+1wNWxHzK
mdWodYgxrZ7qgaBABL18QENAJMm2i/C3VY6upqGjTjxSq7+huKfTQg== hCe6G9ANAX/EAlGwmOIn3k7cFbN1VRt50TV/UCj+FsJBuifUYVMvTQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2026-08-26T10:29:52Z" lastmodified: "2026-08-11T18:28:22Z"
mac: ENC[AES256_GCM,data:knkRyXRoKdjhyWZIWDCRW5MuhxtxeTm42Fm+rtVZawECWCSIqxTYSfQ6xW9RlalMcsHpdW4QTHDBgZhp6APDe3f+9Frn59UCDa415hqm4VCreDFhqy+x5kUslpT8Zg1ggsiWTv6/XxI2NM0LswoDu6akAEKnBRYJ6S7aam1++oc=,iv:Z8y1HAlzSgbEjX8sh8drHQj6Oigt13m3kYLvWAJZTGs=,tag:iunewDP8PGlj6iTaqAnbnw==,type:str] mac: ENC[AES256_GCM,data:eICyFKuJAyCE8ZbdpWc4rOHRxtnrFhdxTeznIWXie5CrHRyo+n21CPg5L/VQUy2LF1rXINIDJRNqQWcLXJEXDKWGpqyaL/YcW+OawpbCq0DMJAe/DsvBdy5ySwGFdRlwU5x7ivVjsHKnsr8UgcbHdE/ZVRq93nZOfZlUepjb1u0=,iv:l+SFwIobBbxeZNUmwK6U9W5YpLh4t5GOtnRIH10Z7W4=,tag:6Zs+tneNcF1d5i43ns6Rmw==,type:str]
encrypted_regex: ^(data|stringData)$ encrypted_regex: ^(data|stringData)$
version: 3.11.0 version: 3.11.0
@@ -1,5 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
resources: resources:
- longhorn.yaml
- longhorn-no-retain.yaml - longhorn-no-retain.yaml
- longhorn-single-replica.yaml - longhorn-single-replica.yaml
@@ -4,9 +4,11 @@ metadata:
name: longhorn-no-retain name: longhorn-no-retain
annotations: annotations:
storageclass.kubernetes.io/is-default-class: "false" storageclass.kubernetes.io/is-default-class: "false"
# StorageClass parameters are immutable - let Flux recreate the object on change
kustomize.toolkit.fluxcd.io/force: enabled
provisioner: driver.longhorn.io provisioner: driver.longhorn.io
reclaimPolicy: Delete reclaimPolicy: Delete
allowVolumeExpansion: true allowVolumeExpansion: true
parameters: parameters:
numberOfReplicas: "3" numberOfReplicas: "3"
staleReplicaTimeout: "30" staleReplicaTimeout: "2880"
@@ -4,9 +4,11 @@ metadata:
name: longhorn-single-replica name: longhorn-single-replica
annotations: annotations:
storageclass.kubernetes.io/is-default-class: "false" storageclass.kubernetes.io/is-default-class: "false"
# StorageClass parameters are immutable - let Flux recreate the object on change
kustomize.toolkit.fluxcd.io/force: enabled
provisioner: driver.longhorn.io provisioner: driver.longhorn.io
reclaimPolicy: Retain reclaimPolicy: Retain
allowVolumeExpansion: true allowVolumeExpansion: true
parameters: parameters:
numberOfReplicas: "1" numberOfReplicas: "1"
staleReplicaTimeout: "30" staleReplicaTimeout: "2880"
@@ -0,0 +1,27 @@
# Default Longhorn class. Managed here instead of by the chart (persistence.createStorageClass: false
# in the HelmRelease) because the chart hardcodes staleReplicaTimeout: "30".
# Parameters otherwise mirror what chart 1.12.1 rendered.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn
annotations:
storageclass.kubernetes.io/is-default-class: "true"
# StorageClass parameters are immutable - let Flux recreate the object on change
kustomize.toolkit.fluxcd.io/force: enabled
provisioner: driver.longhorn.io
reclaimPolicy: Retain
allowVolumeExpansion: true
volumeBindingMode: Immediate
parameters:
numberOfReplicas: "3"
# minutes a failed replica is kept for reuse before it is discarded and fully rebuilt;
# 30 was shorter than a planned node shutdown and caused a ~1.2TB full rebuild of kube-3
staleReplicaTimeout: "2880"
fromBackup: ""
fsType: ext4
dataLocality: disabled
unmapMarkSnapChainRemoved: ignored
disableRevisionCounter: "true"
dataEngine: v1
backupTargetName: default
+16 -1
View File
@@ -10,7 +10,8 @@ spec:
- 10.0.64.6/32 - 10.0.64.6/32
# exclude 10.0.64.7, its in dmz-pool # exclude 10.0.64.7, its in dmz-pool
# exclude 10.0.64.8, its in mqtt-pool # exclude 10.0.64.8, its in mqtt-pool
- 10.0.64.9-10.0.64.19 # exclude 10.0.64.9, its in public-ports-pool
- 10.0.64.10-10.0.64.19
autoAssign: true autoAssign: true
serviceAllocation: serviceAllocation:
serviceSelectors: serviceSelectors:
@@ -62,6 +63,19 @@ spec:
- matchLabels: - matchLabels:
loadBalancerPool: mqtt loadBalancerPool: mqtt
--- ---
# ip for services that need individual ports forwarded from WAN router (e.g. jitsi jvb udp)
# not auto-assigned: a service gets it only with an explicit annotation
# metallb.io/address-pool: public-ports-pool
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: public-ports-pool
namespace: metallb-system
spec:
addresses:
- 10.0.64.9/32
autoAssign: false
---
apiVersion: metallb.io/v1beta1 apiVersion: metallb.io/v1beta1
kind: L2Advertisement kind: L2Advertisement
metadata: metadata:
@@ -73,3 +87,4 @@ spec:
- dmz-pool - dmz-pool
- mqtt-pool - mqtt-pool
- nfs-pool - nfs-pool
- public-ports-pool
@@ -15,3 +15,29 @@ spec:
namespace: metallb-system namespace: metallb-system
driftDetection: driftDetection:
mode: enabled mode: enabled
postRenderers:
- kustomize:
patches:
# frr's liveness/startup probes default to timeoutSeconds: 1,
# which is too tight when the node is under CPU pressure (an
# HTTPS healthz call can't always complete in 1s) - kubelet then
# kills+restarts the container, which itself adds more CPU load,
# compounding the problem. Give the probes more slack instead.
- target:
kind: DaemonSet
name: metallb-frr-k8s
patch: |
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: metallb-frr-k8s
spec:
template:
spec:
containers:
- name: frr
livenessProbe:
timeoutSeconds: 5
failureThreshold: 6
startupProbe:
timeoutSeconds: 5
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: ci
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: dev
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: git
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: iot
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: notify
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: tools
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: video
+1 -1
View File
@@ -18,4 +18,4 @@ spec:
decryption: decryption:
provider: sops provider: sops
secretRef: secretRef:
name: sops-age name: sops-age-authelia
+1 -1
View File
@@ -18,4 +18,4 @@ spec:
decryption: decryption:
provider: sops provider: sops
secretRef: secretRef:
name: sops-age name: sops-age-davical
+18
View File
@@ -0,0 +1,18 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: dev
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: GitRepository
name: flux-system
path: ./clusters/base/apps/dev
prune: true
wait: true
dependsOn:
- name: namespaces
- name: infra
- name: auth
retryInterval: 5s
+21
View File
@@ -0,0 +1,21 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: gitea
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: GitRepository
name: flux-system
path: ./clusters/base/apps/gitea
prune: true
wait: true
dependsOn:
- name: namespaces
- name: infra
retryInterval: 5s
decryption:
provider: sops
secretRef:
name: sops-age-gitea
+1 -1
View File
@@ -18,4 +18,4 @@ spec:
decryption: decryption:
provider: sops provider: sops
secretRef: secretRef:
name: sops-age name: sops-age-immich
@@ -1,14 +1,14 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1 apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization kind: Kustomization
metadata: metadata:
name: openwebui name: it-tools
namespace: flux-system namespace: flux-system
spec: spec:
interval: 10m interval: 10m
sourceRef: sourceRef:
kind: GitRepository kind: GitRepository
name: flux-system name: flux-system
path: ./clusters/base/apps/openwebui path: ./clusters/base/apps/it-tools
prune: true prune: true
wait: true wait: true
dependsOn: dependsOn:
+21
View File
@@ -0,0 +1,21 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: jitsi
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: GitRepository
name: flux-system
path: ./clusters/base/apps/jitsi
prune: true
wait: true
dependsOn:
- name: namespaces
- name: infra
retryInterval: 5s
decryption:
provider: sops
secretRef:
name: sops-age-jitsi
+21
View File
@@ -0,0 +1,21 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: joplin
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: GitRepository
name: flux-system
path: ./clusters/base/apps/joplin
prune: true
wait: true
dependsOn:
- name: namespaces
- name: infra
retryInterval: 5s
decryption:
provider: sops
secretRef:
name: sops-age-joplin
+10 -3
View File
@@ -7,12 +7,19 @@ resources:
- authelia.yaml - authelia.yaml
- mosquitto.yaml - mosquitto.yaml
- homeassistant.yaml - homeassistant.yaml
- openwebui.yaml
- markdown-vault-mcp.yaml - markdown-vault-mcp.yaml
- nfs-ganesha.yaml - nfs-ganesha.yaml
- webdav.yaml - webdav.yaml
- fava.yaml - fava.yaml
- infcloud.yaml - infcloud.yaml
- syncthing.yaml - syncthing.yaml
- photo-stats.yaml - immich.yaml
- immich.yaml - joplin.yaml
- gitea.yaml
- dev.yaml
- woodpecker.yaml
- tinyauth.yaml
- it-tools.yaml
- ntfy.yaml
- map-monitor.yaml
- jitsi.yaml
+21
View File
@@ -0,0 +1,21 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: map-monitor
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: GitRepository
name: flux-system
path: ./clusters/base/apps/map_monitor
prune: true
wait: true
dependsOn:
- name: namespaces
- name: infra
retryInterval: 5s
decryption:
provider: sops
secretRef:
name: sops-age-map-monitor
+1 -1
View File
@@ -19,4 +19,4 @@ spec:
decryption: decryption:
provider: sops provider: sops
secretRef: secretRef:
name: sops-age name: sops-age-markdown-vault-mcp
+1 -1
View File
@@ -15,4 +15,4 @@ spec:
decryption: decryption:
provider: sops provider: sops
secretRef: secretRef:
name: sops-age name: sops-age-mosquitto
+21
View File
@@ -0,0 +1,21 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: ntfy
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: GitRepository
name: flux-system
path: ./clusters/base/apps/ntfy
prune: true
wait: true
dependsOn:
- name: namespaces
- name: infra
retryInterval: 5s
decryption:
provider: sops
secretRef:
name: sops-age-ntfy

Some files were not shown because too many files have changed in this diff Show More