From 69186d807e8af566ca580dae6e9a84d63c429efc Mon Sep 17 00:00:00 2001 From: Ultradesu Date: Thu, 30 Jul 2026 12:18:12 +0100 Subject: [PATCH] Added firefly-iii --- k8s/apps/firefly-iii/app.yaml | 21 +++ k8s/apps/firefly-iii/external-secrets.yaml | 49 +++++++ k8s/apps/firefly-iii/kustomization.yaml | 75 ++++++++++ k8s/apps/firefly-iii/network-policy.yaml | 59 ++++++++ k8s/apps/firefly-iii/postgres.yaml | 49 +++++++ k8s/apps/firefly-iii/storage.yaml | 73 ++++++++++ k8s/apps/firefly-iii/traefik-ai.yaml | 160 +++++++++++++++++++++ k8s/apps/firefly-iii/values.yaml | 69 +++++++++ 8 files changed, 555 insertions(+) create mode 100644 k8s/apps/firefly-iii/app.yaml create mode 100644 k8s/apps/firefly-iii/external-secrets.yaml create mode 100644 k8s/apps/firefly-iii/kustomization.yaml create mode 100644 k8s/apps/firefly-iii/network-policy.yaml create mode 100644 k8s/apps/firefly-iii/postgres.yaml create mode 100644 k8s/apps/firefly-iii/storage.yaml create mode 100644 k8s/apps/firefly-iii/traefik-ai.yaml create mode 100644 k8s/apps/firefly-iii/values.yaml diff --git a/k8s/apps/firefly-iii/app.yaml b/k8s/apps/firefly-iii/app.yaml new file mode 100644 index 0000000..e0251dc --- /dev/null +++ b/k8s/apps/firefly-iii/app.yaml @@ -0,0 +1,21 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: firefly-iii + namespace: argocd +spec: + project: apps + destination: + namespace: firefly-iii + server: https://kubernetes.default.svc + source: + repoURL: ssh://git@gt.hexor.cy:30022/ab/homelab.git + targetRevision: HEAD + path: k8s/apps/firefly-iii + syncPolicy: + automated: + selfHeal: true + prune: true + syncOptions: + - CreateNamespace=true + diff --git a/k8s/apps/firefly-iii/external-secrets.yaml b/k8s/apps/firefly-iii/external-secrets.yaml new file mode 100644 index 0000000..ea0022e --- /dev/null +++ b/k8s/apps/firefly-iii/external-secrets.yaml @@ -0,0 +1,49 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: firefly-secrets +spec: + refreshInterval: 1h + target: + name: firefly-secrets + creationPolicy: Owner + deletionPolicy: Delete + template: + type: Opaque + data: + # Firefly and CloudNativePG deliberately share this generated Secret. + username: firefly + password: |- + {{ .db_password }} + DB_PASSWORD: |- + {{ .db_password }} + APP_KEY: |- + {{ .app_key }} + STATIC_CRON_TOKEN: |- + {{ .cron_token }} + data: + - secretKey: db_password + sourceRef: + storeRef: + name: vaultwarden-login + kind: ClusterSecretStore + remoteRef: + key: a1867c81-715c-47cd-978d-14ea5bcedea9 + property: fields[0].value + - secretKey: app_key + sourceRef: + storeRef: + name: vaultwarden-login + kind: ClusterSecretStore + remoteRef: + key: a1867c81-715c-47cd-978d-14ea5bcedea9 + property: fields[1].value + - secretKey: cron_token + sourceRef: + storeRef: + name: vaultwarden-login + kind: ClusterSecretStore + remoteRef: + key: a1867c81-715c-47cd-978d-14ea5bcedea9 + property: fields[2].value + diff --git a/k8s/apps/firefly-iii/kustomization.yaml b/k8s/apps/firefly-iii/kustomization.yaml new file mode 100644 index 0000000..e6bf48f --- /dev/null +++ b/k8s/apps/firefly-iii/kustomization.yaml @@ -0,0 +1,75 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - app.yaml + - external-secrets.yaml + - postgres.yaml + - storage.yaml + - traefik-ai.yaml + - network-policy.yaml + +helmCharts: + - name: firefly-iii + repo: https://harish2k01.github.io/helm-charts + version: 0.1.2 + releaseName: firefly-iii + namespace: firefly-iii + valuesFile: values.yaml + includeCRDs: true + +patches: + # Chart 0.1.2 requires postgres.enabled=true. Remove its StatefulSet and + # redirect the generated database Service to the CloudNativePG primary. + - target: + group: apps + version: v1 + kind: StatefulSet + name: firefly-iii-postgres + patch: |- + $patch: delete + apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: firefly-iii-postgres + - target: + version: v1 + kind: Service + name: firefly-iii-postgres + patch: |- + - op: remove + path: /spec/selector + - op: replace + path: /spec/type + value: ExternalName + - op: add + path: /spec/externalName + value: firefly-postgres-rw.firefly-iii.svc.cluster.local + # The upstream chart does not expose scheduling settings for its CronJob. + - target: + group: batch + version: v1 + kind: CronJob + name: firefly-iii-cron + patch: |- + - op: add + path: /spec/jobTemplate/spec/template/spec/nodeSelector + value: + kubernetes.io/hostname: ai.tail2fe2d.ts.net + - op: add + path: /spec/jobTemplate/spec/template/spec/tolerations + value: + - key: workload + operator: Equal + value: ai + effect: NoSchedule + # A second independent filter in addition to ingressClassName. + - target: + group: networking.k8s.io + version: v1 + kind: Ingress + name: firefly-iii + patch: |- + - op: add + path: /metadata/labels/firefly.hexor.cy~1private-ai + value: "true" diff --git a/k8s/apps/firefly-iii/network-policy.yaml b/k8s/apps/firefly-iii/network-policy.yaml new file mode 100644 index 0000000..868279a --- /dev/null +++ b/k8s/apps/firefly-iii/network-policy.yaml @@ -0,0 +1,59 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: firefly-ingress +spec: + podSelector: + matchLabels: + app.kubernetes.io/instance: firefly-iii + app.kubernetes.io/name: firefly-iii + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: traefik-ai + # The chart's CronJob uses the same selector labels as the application. + - podSelector: + matchLabels: + app.kubernetes.io/instance: firefly-iii + app.kubernetes.io/name: firefly-iii + ports: + - protocol: TCP + port: 8080 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: firefly-postgres-ingress +spec: + podSelector: + matchLabels: + cnpg.io/cluster: firefly-postgres + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/instance: firefly-iii + app.kubernetes.io/name: firefly-iii + - podSelector: + matchLabels: + cnpg.io/cluster: firefly-postgres + ports: + - protocol: TCP + port: 5432 + # CloudNativePG operator health/status traffic to the instance manager. + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: psql + podSelector: + matchLabels: + app.kubernetes.io/name: cloudnative-pg + ports: + - protocol: TCP + port: 8000 + diff --git a/k8s/apps/firefly-iii/postgres.yaml b/k8s/apps/firefly-iii/postgres.yaml new file mode 100644 index 0000000..433d3be --- /dev/null +++ b/k8s/apps/firefly-iii/postgres.yaml @@ -0,0 +1,49 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: firefly-postgres +spec: + description: PostgreSQL for Firefly III + instances: 1 + enableSuperuserAccess: false + primaryUpdateStrategy: unsupervised + bootstrap: + initdb: + database: firefly + owner: firefly + secret: + name: firefly-secrets + dataChecksums: true + storage: + size: 10Gi + storageClass: firefly-local + pvcTemplate: + accessModes: + - ReadWriteOnce + storageClassName: firefly-local + selector: + matchLabels: + app.kubernetes.io/name: firefly-postgres + resources: + requests: + storage: 10Gi + volumeMode: Filesystem + affinity: + enablePodAntiAffinity: false + nodeSelector: + kubernetes.io/hostname: ai.tail2fe2d.ts.net + tolerations: + - key: workload + operator: Equal + value: ai + effect: NoSchedule + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: "1" + memory: 1Gi + monitoring: + enablePodMonitor: true + diff --git a/k8s/apps/firefly-iii/storage.yaml b/k8s/apps/firefly-iii/storage.yaml new file mode 100644 index 0000000..7c36c7e --- /dev/null +++ b/k8s/apps/firefly-iii/storage.yaml @@ -0,0 +1,73 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: firefly-local +provisioner: kubernetes.io/no-provisioner +reclaimPolicy: Retain +volumeBindingMode: WaitForFirstConsumer +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: firefly-postgres + labels: + app.kubernetes.io/name: firefly-postgres +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: firefly-local + volumeMode: Filesystem + hostPath: + path: /k8s/firefly-iii/postgres + type: DirectoryOrCreate + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - ai.tail2fe2d.ts.net +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: firefly-uploads + labels: + app.kubernetes.io/name: firefly-uploads +spec: + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: firefly-local + volumeMode: Filesystem + hostPath: + path: /k8s/firefly-iii/uploads + type: DirectoryOrCreate + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - ai.tail2fe2d.ts.net +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: firefly-uploads +spec: + accessModes: + - ReadWriteOnce + storageClassName: firefly-local + volumeName: firefly-uploads + resources: + requests: + storage: 5Gi + diff --git a/k8s/apps/firefly-iii/traefik-ai.yaml b/k8s/apps/firefly-iii/traefik-ai.yaml new file mode 100644 index 0000000..441697b --- /dev/null +++ b/k8s/apps/firefly-iii/traefik-ai.yaml @@ -0,0 +1,160 @@ +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: traefik-ai +spec: + controller: traefik.io/ingress-controller +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: traefik-ai +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: traefik-ai +rules: + - apiGroups: + - "" + resources: + - services + - secrets + - endpoints + verbs: + - get + - list + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - get + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: traefik-ai +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: traefik-ai +subjects: + - kind: ServiceAccount + name: traefik-ai +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: firefly-traefik-ai +rules: + - apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: firefly-traefik-ai +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: firefly-traefik-ai +subjects: + - kind: ServiceAccount + name: traefik-ai + namespace: firefly-iii +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: traefik-ai + labels: + app.kubernetes.io/name: traefik-ai +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app.kubernetes.io/name: traefik-ai + template: + metadata: + labels: + app.kubernetes.io/name: traefik-ai + spec: + serviceAccountName: traefik-ai + nodeSelector: + kubernetes.io/hostname: ai.tail2fe2d.ts.net + tolerations: + - key: workload + operator: Equal + value: ai + effect: NoSchedule + containers: + - name: traefik + image: rancher/mirrored-library-traefik:3.6.13 + imagePullPolicy: IfNotPresent + args: + - --entrypoints.web.address=:8000 + - --providers.kubernetesingress=true + - --providers.kubernetesingress.namespaces=firefly-iii + - --providers.kubernetesingress.ingressclass=traefik-ai + - --providers.kubernetesingress.labelselector=firefly.hexor.cy/private-ai=true + - --providers.kubernetescrd=false + - --api.dashboard=false + - --log.level=INFO + ports: + - name: web + containerPort: 8000 + hostPort: 80 + protocol: TCP + readinessProbe: + tcpSocket: + port: web + periodSeconds: 10 + livenessProbe: + tcpSocket: + port: web + periodSeconds: 30 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 500m + memory: 256Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 65532 + securityContext: + seccompProfile: + type: RuntimeDefault + diff --git a/k8s/apps/firefly-iii/values.yaml b/k8s/apps/firefly-iii/values.yaml new file mode 100644 index 0000000..4c29d43 --- /dev/null +++ b/k8s/apps/firefly-iii/values.yaml @@ -0,0 +1,69 @@ +replicaCount: 1 + +image: + repository: fireflyiii/core + tag: version-6.6.1 + pullPolicy: IfNotPresent + +service: + type: ClusterIP + port: 80 + targetPort: 8080 + +ingress: + enabled: true + className: traefik-ai + hosts: + - host: ff.lan + paths: + - path: / + pathType: Prefix + +persistence: + upload: + enabled: true + existingClaim: firefly-uploads + +# Must remain true because chart 0.1.2 validates it. Kustomize removes the +# generated StatefulSet and redirects this Service to CloudNativePG. +postgres: + enabled: true + auth: + database: firefly + username: firefly + service: + port: 5432 + +cronjob: + enabled: true + schedule: "0 3 * * *" + timeZone: Europe/London + +firefly: + env: + APP_ENV: production + APP_DEBUG: "false" + APP_URL: http://ff.lan + SITE_OWNER: owner@ff.lan + TZ: Europe/London + DEFAULT_LANGUAGE: en_US + TRUSTED_PROXIES: 10.42.0.0/16 + COOKIE_SECURE: "false" + PGSQL_SSL_MODE: prefer + secrets: + existingSecret: firefly-secrets + nodeSelector: + kubernetes.io/hostname: ai.tail2fe2d.ts.net + tolerations: + - key: workload + operator: Equal + value: ai + effect: NoSchedule + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: "2" + memory: 1Gi +