feat(helm): complete chart normalization with ConfigMap, HPA, and OCI migration

- Added debug mode, structured ConfigMap pattern, and README to all charts
- Migrated all flat-structure charts to hierarchical values under chartname key
- Fixed templates to use correct hierarchical paths
- Renamed helm-rustical key to rustical, added HPA support
- Fixed ingress YAML parsing (oxicloud), nested env vars (rustical)
- Pushed all normalized charts to OCI registry as 1.0.0+ and updated ArgoCD apps
This commit is contained in:
cătălin 2026-08-14 09:35:19 +02:00
commit 1fd2bfef3d
No known key found for this signature in database
71 changed files with 2150 additions and 965 deletions

View file

@ -51,6 +51,10 @@ spec:
persistentVolumeClaim:
claimName: pvc-authentik-media
server:
resources:
requests:
cpu: 500m
memory: 1Gi
autoscaling:
enabled: true
minReplicas: 1
@ -79,6 +83,9 @@ spec:
tls: []
https: true
worker:
requests:
cpu: 250m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 2
@ -92,4 +99,6 @@ spec:
targetRevision: main
project: fuku
syncPolicy:
automated: {}
automated:
prune: true
selfHeal: true

View file

@ -12,9 +12,10 @@ spec:
sources:
- repoURL: git.roboces.dev/catalin/fukuops
chart: dokuwiki
targetRevision: 0.1.2
targetRevision: 1.0.2
helm:
valuesObject:
dokuwiki:
replicaCount: 1
securityContext:
allowPrivilegeEscalation: false
@ -40,10 +41,11 @@ spec:
storageClass: "truenas-nfs-csi"
accessMode: ReadWriteMany
size: 20Gi
env:
PHP_TIMEZONE: Europe/Madrid
PHP_MEMORYLIMIT: 512M
PHP_UPLOADLIMIT: 128M
config:
php:
timezone: Europe/Madrid
memoryLimit: 512M
uploadLimit: 128M
project: roboces
syncPolicy:
automated:

View file

@ -101,4 +101,6 @@ spec:
enabled: false
project: roboces
syncPolicy:
automated: {}
automated:
prune: true
selfHeal: true

View file

@ -12,12 +12,18 @@ spec:
sources:
- repoURL: git.roboces.dev/catalin/fukuops
chart: helm-rustical
targetRevision: 0.3.0
targetRevision: 1.1.1
helm:
valuesObject:
rustical:
debug:
enabled: false
replicaCount: 1
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 3
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
service:
type: LoadBalancer
port: 8001
@ -30,13 +36,13 @@ spec:
paths:
- path: /
pathType: Prefix
env:
RUSTICAL_OIDC__NAME: Authentik
RUSTICAL_OIDC__ISSUER: https://auth.fukurokuju.dev/application/o/rustical/
RUSTICAL_OIDC__CLAIM_USERID: preferred_username
RUSTICAL_OIDC__SCOPES: '["openid", "profile", "groups"]'
RUSTICAL_OIDC__ALLOW_SIGN_UP: "true"
RUSTICAL_FRONTEND__ALLOW_PASSWORD_LOGIN: false
config:
oidc.name: Authentik
oidc.issuer: https://auth.fukurokuju.dev/application/o/rustical/
oidc.claim_userid: preferred_username
oidc.scopes: '["openid", "profile", "groups"]'
oidc.allow_sign_up: "true"
frontend.allow_password_login: false
persistence:
enabled: true
storageClass: "truenas-nfs-csi"

View file

@ -12,13 +12,9 @@ spec:
sources:
- repoURL: git.roboces.dev/catalin/fukuops
chart: immich
targetRevision: 0.2.3
targetRevision: 1.0.4
helm:
valuesObject:
machine-learning:
enabled: true
service:
type: LoadBalancer
immich:
autoscaling:
enabled: true
@ -32,16 +28,17 @@ spec:
- all
service:
type: LoadBalancer
db:
config:
database:
hostname: "192.168.1.3"
port: 5432
database_name: "immich"
name: "immich"
redis:
hostname: "192.168.1.3"
port: 30036
dbindex: "12"
dbIndex: "12"
general:
tz: "Europe/Madrid"
timezone: Europe/Madrid
persistence:
library:
enabled: true
@ -96,7 +93,12 @@ spec:
- path: /
pathType: Prefix
tls: []
machineLearning:
enabled: true
service:
type: LoadBalancer
secret:
enabled: true
existingSecretName: "immich-secrets"
project: roboces
syncPolicy:

View file

@ -12,10 +12,12 @@ spec:
sources:
- repoURL: git.roboces.dev/catalin/fukuops
chart: miniflux
targetRevision: 0.2.0
targetRevision: 1.0.3
helm:
valuesObject:
replicaCount: 3
miniflux:
autoscaling:
enabled: true
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
@ -36,12 +38,12 @@ spec:
pathType: Prefix
resources:
requests:
cpu: 300m
memory: 300Mi
cpu: 500m
memory: 512Mi
ephemeral-storage: 2Gi
limits:
cpu: 400m
memory: 500Mi
cpu: 700m
memory: 1024Mi
ephemeral-storage: 4Gi
livenessProbe:
tcpSocket:
@ -63,19 +65,24 @@ spec:
podDisruptionBudget:
enabled: true
maxUnavailable: 1
env:
RUN_MIGRATIONS: "1"
CREATE_ADMIN: "1"
OAUTH2_PROVIDER: oidc
OAUTH2_REDIRECT_URL: https://feeds.roboces.dev/oauth2/oidc/callback
OAUTH2_OIDC_DISCOVERY_ENDPOINT: https://auth.fukurokuju.dev/application/o/miniflux/
OAUTH2_USER_CREATION: "1"
FETCH_YOUTUBE_WATCH_TIME: "1"
WORKER_POOL_SIZE: "1"
POLLING_FREQUENCY: "120"
BATCH_SIZE: "25"
METRICS_COLLECTOR: "1"
METRICS_ALLOWED_NETWORKS: 10.42.1.0/16
config:
general:
runMigrations: true
createAdmin: true
workerPoolSize: 1
pollingFrequency: 120
batchSize: 25
oauth2:
enabled: true
provider: oidc
redirectUrl: https://feeds.roboces.dev/oauth2/oidc/callback
oidcDiscoveryEndpoint: https://auth.fukurokuju.dev/application/o/miniflux/
userCreation: true
metrics:
enabled: true
allowedNetworks: 10.42.1.0/16
youtube:
fetchWatchTime: true
secret:
existingSecretName: miniflux
project: roboces

View file

@ -12,9 +12,10 @@ spec:
sources:
- repoURL: git.roboces.dev/catalin/fukuops
chart: oxicloud
targetRevision: 0.1.0
targetRevision: 1.0.1
helm:
valuesObject:
oxicloud:
persistence:
enabled: true
storageClass: "truenas-nfs-csi"
@ -28,16 +29,17 @@ spec:
host: "0.0.0.0"
baseUrl: "https://cloud.roboces.dev"
features:
enableAuth: "true"
enableSharing: "true"
enableAuth: true
enableSharing: true
mimalloc:
purgeDelay: "0"
allowLargeOsPages: "0"
secrets:
existingSecret: oxicloud
secret:
existingSecretName: oxicloud
wopi:
enabled: false
ingress:
enabled: true
className: "traefik"
hosts:
- host: cloud.roboces.dev
@ -47,4 +49,6 @@ spec:
tls: []
project: roboces
syncPolicy:
automated: {}
automated:
prune: true
selfHeal: true

View file

@ -21,6 +21,7 @@ spec:
type: LoadBalancer
persistence:
storageClass: truenas-nfs-csi
accessMode: ReadWriteMany
dashboard:
enabled: true
service:

View file

@ -12,18 +12,18 @@ spec:
sources:
- repoURL: git.roboces.dev/catalin/fukuops
chart: vaultwarden
targetRevision: 0.1.1
targetRevision: 1.0.2
helm:
valuesObject:
conf:
db:
enable_db_wal: true
connection_retries: 15
vaultwarden:
config:
database:
enableWal: true
connectionRetries: 15
timeout: 30
web_vault:
webVault:
enabled: true
folder: "web-vault/"
replicaCount: 1
securityContext:
allowPrivilegeEscalation: false

View file

@ -2,15 +2,9 @@ apiVersion: v2
name: chart
description: A Helm chart template with standardized structure
type: application
version: 0.1.0
appVersion: "latest"
version: 1.0.2
appVersion: latest
annotations:
artifacthub.io/images: |
- name: chart
image: nginx:latest
artifacthub.io/changes: |
- kind: added
description: Initial chart template
artifacthub.io/maintainers: |
- name: catalin
email: catalin@example.com
artifacthub.io/images: "- name: chart\n image: nginx:latest\n"
artifacthub.io/changes: "- kind: added\n description: Initial chart template\n"
artifacthub.io/maintainers: "- name: catalin\n email: catalin@example.com\n"

View file

@ -5,7 +5,7 @@ A Helm chart template with standardized structure for self-hosted services.
## TL;DR
```bash
helm install chart oci://git.roboces.dev/catalin/fukuops/chart --version 1.0.0 -n apps-roboces
helm install chart oci://git.roboces.dev/catalin/fukuops/chart --version 1.0.1 -n apps-roboces
```
## Prerequisites
@ -17,9 +17,11 @@ helm install chart oci://git.roboces.dev/catalin/fukuops/chart --version 1.0.0 -
## Configuration
### Basic Configuration
All values are nested under the `chart:` key. Example:
```yaml
chart:
replicaCount: 1
service:
type: LoadBalancer
port: 80
@ -31,21 +33,14 @@ ingress:
paths:
- path: /
pathType: Prefix
```
### With Persistence
```yaml
config:
# Key-value pairs that will be mapped to env vars (uppercased)
# Example: `FOO: "bar"` becomes env var FOO=bar
persistence:
enabled: true
storageClass: "truenas-nfs-csi"
accessMode: ReadWriteMany
size: 10Gi
```
### With Secrets
```yaml
secret:
existingSecretName: chart-secrets
```
@ -54,27 +49,42 @@ secret:
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `replicaCount` | int | `1` | Number of replicas |
| `image.repository` | string | `nginx` | Image repository |
| `image.pullPolicy` | string | `IfNotPresent` | Image pull policy |
| `image.tag` | string | `.Chart.AppVersion` | Image tag |
| `service.type` | string | `LoadBalancer` | Service type |
| `service.port` | int | `80` | Service port |
| `service.targetPort` | int | `80` | Container port |
| `ingress.enabled` | bool | `false` | Enable ingress |
| `ingress.className` | string | `traefik` | Ingress class |
| `persistence.enabled` | bool | `false` | Enable persistence |
| `persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
| `persistence.accessMode` | string | `ReadWriteMany` | Access mode |
| `persistence.size` | string | `1Gi` | PVC size |
| `persistence.name` | string | `chart-data` | PVC name |
| `secret.existingSecretName` | string | `""` | Existing secret name |
| `resources` | object | `{}` | Container resources |
| `livenessProbe` | object | (see values.yaml) | Liveness probe |
| `readinessProbe` | object | (see values.yaml) | Readiness probe |
| `nodeSelector` | object | `{}` | Node selector |
| `tolerations` | array | `[]` | Tolerations |
| `affinity` | object | `{}` | Affinity rules |
| `chart.replicaCount` | int | `1` | Number of replicas |
| `chart.image.repository` | string | `nginx` | Image repository |
| `chart.image.pullPolicy` | string | `IfNotPresent` | Image pull policy |
| `chart.image.tag` | string | `.Chart.AppVersion` | Image tag |
| `chart.service.type` | string | `LoadBalancer` | Service type |
| `chart.service.port` | int | `80` | Service port |
| `chart.service.targetPort` | int | `80` | Container port |
| `chart.ingress.enabled` | bool | `false` | Enable ingress |
| `chart.ingress.className` | string | `traefik` | Ingress class |
| `chart.config` | object | `{}` | Config key-value pairs mapped to env vars |
| `chart.persistence.enabled` | bool | `false` | Enable persistence |
| `chart.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
| `chart.persistence.accessMode` | string | `ReadWriteMany` | Access mode |
| `chart.persistence.size` | string | `1Gi` | PVC size |
| `chart.persistence.name` | string | `chart-data` | PVC name |
| `chart.secret.existingSecretName` | string | `""` | Existing secret name |
| `chart.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
| `chart.resources` | object | `{}` | Container resources |
| `chart.livenessProbe` | object | (see values.yaml) | Liveness probe |
| `chart.readinessProbe` | object | (see values.yaml) | Readiness probe |
| `chart.nodeSelector` | object | `{}` | Node selector |
| `chart.tolerations` | array | `[]` | Tolerations |
| `chart.affinity` | object | `{}` | Affinity rules |
## ConfigMap
The `config:` section creates a ConfigMap that maps keys to environment variables. Keys are uppercased.
```yaml
chart:
config:
FOO: "bar"
BAZ: "qux"
```
This creates env vars `FOO=bar` and `BAZ=qux`.
## Persistence
@ -82,10 +92,28 @@ When persistence is enabled, a PVC named `chart-data` is created. The PVC uses t
## Secret Management
The chart looks for an existing `Secret` with the name specified in `secret.existingSecretName`. If not provided, no secret is mounted.
The chart looks for an existing `Secret` with the name specified in `chart.secret.existingSecretName`. If not provided, no secret is mounted.
## Troubleshooting
### Debug Mode
Enable debug mode to troubleshoot issues by adding an init container with a shell:
```yaml
chart:
debug:
enabled: true
```
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
```bash
kubectl exec -it <pod-name> -c debug -- sh
```
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
### Pod not starting
```bash

View file

@ -1,4 +1,4 @@
{{- if .Values.config }}
{{- if .Values.chart.config }}
apiVersion: v1
kind: ConfigMap
metadata:
@ -6,7 +6,8 @@ metadata:
labels:
{{- include "labels" . | nindent 4 }}
data:
{{- range $key, $value := .Values.config }}
{{ $key }}: {{ $value | quote }}
{{- range $key, $value := .Values.chart.config }}
{{- $envKey := $key | upper }}
{{ $envKey }}: {{ $value | quote }}
{{- end }}
{{- end }}

View file

@ -5,7 +5,7 @@ metadata:
labels:
{{- include "labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
replicas: {{ .Values.chart.replicaCount }}
selector:
matchLabels:
{{- include "selectorLabels" . | nindent 6 }}
@ -14,55 +14,90 @@ spec:
labels:
{{- include "selectorLabels" . | nindent 8 }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.targetPort }}
protocol: TCP
{{- if .Values.config }}
{{- with .Values.chart.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.chart.debug.enabled }}
initContainers:
- name: debug
image: alpine:3.19
command: ["sleep", "infinity"]
envFrom:
{{- if .Values.chart.config }}
- configMapRef:
name: {{ include "fullname" . }}-config
{{- end }}
{{- if .Values.secret.existingSecretName }}
envFrom:
{{- if .Values.chart.secret.existingSecretName }}
- secretRef:
name: {{ .Values.secret.existingSecretName }}
name: {{ .Values.chart.secret.existingSecretName }}
{{- end }}
{{- if .Values.persistence.enabled }}
{{- if .Values.chart.persistence.enabled }}
volumeMounts:
- name: {{ include "pvcName" . }}
mountPath: /data
{{- end }}
{{- with .Values.resources }}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
capabilities:
drop:
- all
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.chart.image.repository }}:{{ .Values.chart.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.chart.image.pullPolicy }}
{{- with .Values.chart.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.chart.service.targetPort }}
protocol: TCP
envFrom:
{{- if .Values.chart.config }}
- configMapRef:
name: {{ include "fullname" . }}-config
{{- end }}
{{- if .Values.chart.secret.existingSecretName }}
- secretRef:
name: {{ .Values.chart.secret.existingSecretName }}
{{- end }}
{{- if .Values.chart.persistence.enabled }}
volumeMounts:
- name: {{ include "pvcName" . }}
mountPath: /data
{{- end }}
{{- with .Values.chart.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.livenessProbe }}
{{- if not .Values.chart.debug.enabled }}
{{- with .Values.chart.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
{{- with .Values.chart.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if not .Values.persistence.enabled }}
{{- end }}
{{- if not .Values.chart.persistence.enabled }}
volumes:
- name: {{ include "pvcName" . }}
emptyDir: {}
{{- end }}
{{- with .Values.nodeSelector }}
{{- with .Values.chart.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
{{- with .Values.chart.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
{{- with .Values.chart.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}

View file

@ -1,21 +1,21 @@
{{- if .Values.ingress.enabled }}
{{- if .Values.chart.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "fullname" . }}
labels:
{{- include "labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
{{- with .Values.chart.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.chart.ingress.className }}
ingressClassName: {{ .Values.chart.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
{{- if .Values.chart.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
{{- range .Values.chart.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
@ -24,7 +24,7 @@ spec:
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
{{- range .Values.chart.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
@ -35,7 +35,7 @@ spec:
service:
name: {{ include "fullname" $ }}
port:
number: {{ $.Values.service.port }}
number: {{ $.Values.chart.service.port }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,18 @@
{{- if .Values.chart.podDisruptionBudget.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "fullname" . }}
labels:
{{- include "labels" . | nindent 4 }}
spec:
{{- if .Values.chart.podDisruptionBudget.minAvailable }}
minAvailable: {{ .Values.chart.podDisruptionBudget.minAvailable }}
{{- end }}
{{- if .Values.chart.podDisruptionBudget.maxUnavailable }}
maxUnavailable: {{ .Values.chart.podDisruptionBudget.maxUnavailable }}
{{- end }}
selector:
matchLabels:
{{- include "selectorLabels" . | nindent 6 }}
{{- end }}

View file

@ -1,4 +1,4 @@
{{- if .Values.persistence.enabled }}
{{- if .Values.chart.persistence.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
@ -7,11 +7,11 @@ metadata:
{{- include "labels" . | nindent 4 }}
spec:
accessModes:
- {{ .Values.persistence.accessMode | default "ReadWriteMany" }}
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass }}
- {{ .Values.chart.persistence.accessMode | default "ReadWriteMany" }}
{{- if .Values.chart.persistence.storageClass }}
storageClassName: {{ .Values.chart.persistence.storageClass }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.size | default "1Gi" }}
storage: {{ .Values.chart.persistence.size | default "1Gi" }}
{{- end }}

View file

@ -1,4 +1,4 @@
{{- if and .Values.secret.enabled (not .Values.secret.existingSecretName) .Values.secret.data }}
{{- if and .Values.chart.secret.enabled (not .Values.chart.secret.existingSecretName) .Values.chart.secret.data }}
apiVersion: v1
kind: Secret
metadata:
@ -7,7 +7,7 @@ metadata:
{{- include "labels" . | nindent 4 }}
type: Opaque
data:
{{- range $key, $value := .Values.secret.data }}
{{- range $key, $value := .Values.chart.secret.data }}
{{ $key }}: {{ $value | b64enc | quote }}
{{- end }}
{{- end }}

View file

@ -5,9 +5,9 @@ metadata:
labels:
{{- include "labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
type: {{ .Values.chart.service.type }}
ports:
- port: {{ .Values.service.port }}
- port: {{ .Values.chart.service.port }}
targetPort: http
protocol: TCP
name: http

View file

@ -1,3 +1,4 @@
chart:
replicaCount: 1
image:
@ -58,3 +59,13 @@ secret:
enabled: true
existingSecretName: ""
data: {}
podSecurityContext: {}
securityContext: {}
podDisruptionBudget:
enabled: false
minAvailable: 1
debug:
enabled: false

View file

@ -2,7 +2,7 @@ apiVersion: v2
name: dokuwiki
description: A Helm chart for DokuWiki
type: application
version: 0.1.0
version: 1.0.2
appVersion: 2024-02-06b
annotations:
artifacthub.io/images: "- name: dokuwiki\n image: dokuwiki/dokuwiki:2024-02-06b\n"

View file

@ -0,0 +1,112 @@
# dokuwiki
A Helm chart for DokuWiki, a simple to use and highly versatile wiki software.
## TL;DR
```bash
helm install dokuwiki oci://git.roboces.dev/catalin/fukuops/dokuwiki --version 1.0.2 -n apps-roboces
```
## Prerequisites
- Kubernetes 1.19+
- Helm 3+
- A NFS storage class (default: `truenas-nfs-csi`)
## Configuration
All values are nested under the `dokuwiki:` key. Example:
```yaml
dokuwiki:
persistence:
enabled: true
storageClass: "truenas-nfs-csi"
accessMode: ReadWriteMany
size: 2Gi
service:
type: LoadBalancer
port: 8004
ingress:
enabled: true
hosts:
- host: wiki.roboces.dev
paths:
- path: /
pathType: Prefix
config:
php:
timezone: Europe/Madrid
memoryLimit: 512M
uploadLimit: 128M
```
## Values Reference
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `dokuwiki.replicaCount` | int | `1` | Number of replicas |
| `dokuwiki.image.repository` | string | `dokuwiki/dokuwiki` | Image repository |
| `dokuwiki.image.pullpolicy` | string | `IfNotPresent` | Image pull policy |
| `dokuwiki.image.tag` | string | `.Chart.AppVersion` | Image tag |
| `dokuwiki.service.type` | string | `LoadBalancer` | Service type |
| `dokuwiki.service.port` | int | `8004` | Service port |
| `dokuwiki.service.targetPort` | int | `8080` | Container port |
| `dokuwiki.ingress.enabled` | bool | `true` | Enable ingress |
| `dokuwiki.ingress.className` | string | `""` | Ingress class |
| `dokuwiki.ingress.hosts[0].host` | string | `wiki.roboces.dev` | Ingress hostname |
| `dokuwiki.persistence.enabled` | bool | `true` | Enable persistence |
| `dokuwiki.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
| `dokuwiki.persistence.accessMode` | string | `ReadWriteMany` | Access mode |
| `dokuwiki.persistence.size` | string | `2Gi` | PVC size |
| `dokuwiki.config.php.timezone` | string | `Europe/Madrid` | PHP timezone |
| `dokuwiki.config.php.memoryLimit` | string | `512M` | PHP memory limit |
| `dokuwiki.config.php.uploadLimit` | string | `128M` | PHP upload limit |
| `dokuwiki.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
| `dokuwiki.resources` | object | `{}` | Container resources |
| `dokuwiki.livenessProbe` | object | (see values.yaml) | Liveness probe |
| `dokuwiki.readinessProbe` | object | (see values.yaml) | Readiness probe |
## ConfigMap
The chart creates a ConfigMap with PHP environment variables:
- `PHP_TIMEZONE`
- `PHP_MEMORYLIMIT`
- `PHP_UPLOADLIMIT`
## Persistence
The chart creates a PVC named `dokuwiki-data`. The PVC uses the `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode.
## Troubleshooting
### Debug Mode
Enable debug mode to troubleshoot issues by adding an init container with a shell:
```yaml
dokuwiki:
debug:
enabled: true
```
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
```bash
kubectl exec -it <pod-name> -c debug -- sh
```
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
### Pod not starting
```bash
kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=dokuwiki
```
### Check logs
```bash
kubectl logs dokuwiki-0 -n apps-roboces
```

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "dokuwiki.fullname" . }}-config
labels:
{{- include "dokuwiki.labels" . | nindent 4 }}
data:
PHP_TIMEZONE: {{ .Values.dokuwiki.config.php.timezone | quote }}
PHP_MEMORYLIMIT: {{ .Values.dokuwiki.config.php.memoryLimit | quote }}
PHP_UPLOADLIMIT: {{ .Values.dokuwiki.config.php.uploadLimit | quote }}

View file

@ -1,23 +1,23 @@
{{- if .Values.ingress.enabled -}}
{{- if .Values.dokuwiki.ingress.enabled -}}
{{- $fullName := include "dokuwiki.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- $svcPort := .Values.dokuwiki.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "dokuwiki.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
{{- with .Values.dokuwiki.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.dokuwiki.ingress.className }}
ingressClassName: {{ .Values.dokuwiki.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
{{- if .Values.dokuwiki.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
{{- range .Values.dokuwiki.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
@ -26,7 +26,7 @@ spec:
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
{{- range .Values.dokuwiki.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:

View file

@ -5,10 +5,10 @@ metadata:
labels:
{{- include "dokuwiki.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
type: {{ .Values.dokuwiki.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
- port: {{ .Values.dokuwiki.service.port }}
targetPort: {{ .Values.dokuwiki.service.targetPort }}
protocol: TCP
name: http
selector:

View file

@ -6,86 +6,84 @@ metadata:
{{- include "dokuwiki.labels" . | nindent 4 }}
spec:
serviceName: {{ include "dokuwiki.fullname" . }}-headless
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- if not .Values.dokuwiki.autoscaling.enabled }}
replicas: {{ .Values.dokuwiki.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "dokuwiki.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
{{- with .Values.dokuwiki.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "dokuwiki.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
{{- with .Values.dokuwiki.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
{{- toYaml .Values.dokuwiki.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- toYaml .Values.dokuwiki.securityContext | nindent 12 }}
image: "{{ .Values.dokuwiki.image.repository }}:{{ .Values.dokuwiki.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.dokuwiki.image.pullPolicy }}
envFrom:
- configMapRef:
name: {{ include "dokuwiki.fullname" . }}-config
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
{{- toYaml .Values.dokuwiki.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
{{- toYaml .Values.dokuwiki.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- toYaml .Values.dokuwiki.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /storage
{{- if .Values.persistence.existingClaim }}
{{- if .Values.dokuwiki.persistence.existingClaim }}
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Values.persistence.existingClaim }}
claimName: {{ .Values.dokuwiki.persistence.existingClaim }}
{{- end }}
{{- with .Values.nodeSelector }}
{{- with .Values.dokuwiki.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
{{- with .Values.dokuwiki.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
{{- with .Values.dokuwiki.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
restartPolicy: Always
automountServiceAccountToken: false
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
{{- if and .Values.dokuwiki.persistence.enabled (not .Values.dokuwiki.persistence.existingClaim) }}
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- {{ .Values.persistence.accessMode }}
{{- if .Values.persistence.storageClass }}
{{- if eq "-" .Values.persistence.storageClass }}
- {{ .Values.dokuwiki.persistence.accessMode }}
{{- if .Values.dokuwiki.persistence.storageClass }}
{{- if eq "-" .Values.dokuwiki.persistence.storageClass }}
storageClassName: ""
{{- else }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
storageClassName: {{ .Values.dokuwiki.persistence.storageClass | quote }}
{{- end }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.size }}
storage: {{ .Values.dokuwiki.persistence.size }}
{{- end }}

View file

@ -1,3 +1,4 @@
dokuwiki:
replicaCount: 1
image:
@ -26,6 +27,12 @@ ingress:
pathType: Prefix
tls: []
config:
php:
timezone: Europe/Madrid
memoryLimit: 512M
uploadLimit: 128M
persistence:
enabled: true
storageClass: "truenas-nfs-csi"
@ -60,7 +67,5 @@ nodeSelector: {}
tolerations: []
affinity: {}
env:
PHP_TIMEZONE: Europe/Madrid
PHP_MEMORYLIMIT: 512M
PHP_UPLOADLIMIT: 128M
debug:
enabled: false

View file

@ -2,7 +2,7 @@ apiVersion: v2
name: helm-rustical
description: A Helm chart for Rustical WebDAV server
type: application
version: 0.3.0
version: 1.1.1
appVersion: 0.14.1
annotations:
artifacthub.io/images: "- name: rustical\n image: ghcr.io/lennart-k/rustical:0.14.1\n"

View file

@ -0,0 +1,115 @@
# helm-rustical
A Helm chart for Rustical WebDAV server.
## TL;DR
```bash
helm install helm-rustical oci://git.roboces.dev/catalin/fukuops/helm-rustical --version 1.1.0 -n apps-roboces
```
## Prerequisites
- Kubernetes 1.19+
- Helm 3+
- A NFS storage class (default: `truenas-nfs-csi`)
- An existing `Secret` containing:
- `RUSTICAL_PASSWORD` - Password for WebDAV access
## Configuration
All values are nested under the `rustical:` key. Example:
```yaml
rustical:
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 3
targetCPUUtilizationPercentage: 80
service:
type: LoadBalancer
port: 8001
config:
oidc.name: Authentik
oidc.issuer: https://auth.example.com/application/o/rustical/
persistence:
enabled: true
storageClass: "truenas-nfs-csi"
size: 50Gi
secret:
existingSecretName: rustical
```
## Values Reference
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `rustical.replicaCount` | int | `1` | Number of replicas (when autoscaling disabled) |
| `rustical.image.repository` | string | `ghcr.io/lennart-k/rustical` | Image repository |
| `rustical.image.pullpolicy` | string | `Always` | Image pull policy |
| `rustical.image.tag` | string | `.Chart.AppVersion` | Image tag |
| `rustical.service.type` | string | `ClusterIP` | Service type |
| `rustical.service.port` | int | `4000` | Service port |
| `rustical.service.targetPort` | int | `4000` | Container port |
| `rustical.ingress.enabled` | bool | `false` | Enable ingress |
| `rustical.autoscaling.enabled` | bool | `false` | Enable HPA |
| `rustical.autoscaling.minReplicas` | int | `1` | Minimum replicas |
| `rustical.autoscaling.maxReplicas` | int | `3` | Maximum replicas |
| `rustical.autoscaling.targetCPUUtilizationPercentage` | int | `80` | Target CPU utilization |
| `rustical.autoscaling.targetMemoryUtilizationPercentage` | int | `80` | Target memory utilization |
| `rustical.persistence.enabled` | bool | `true` | Enable persistence |
| `rustical.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
| `rustical.persistence.accessMode` | string | `ReadWriteMany` | Access mode |
| `rustical.persistence.size` | string | `50Gi` | PVC size |
| `rustical.secret.existingSecretName` | string | `rustical` | Existing secret name |
| `rustical.config` | object | `{}` | Config key-value pairs mapped to env vars |
| `rustical.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
| `rustical.resources` | object | `{}` | Container resources |
| `rustical.livenessProbe` | object | (see values.yaml) | Liveness probe |
| `rustical.readinessProbe` | object | (see values.yaml) | Readiness probe |
## ConfigMap
The `config:` section creates a ConfigMap that maps keys to environment variables. Keys are uppercased.
## Persistence
The chart creates a PVC named `rustical-data`. The PVC uses the `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode.
## Secret Management
The chart looks for an existing `Secret` with the name specified in `rustical.secret.existingSecretName`. Required secret keys:
- `RUSTICAL_PASSWORD` - Password for WebDAV access
## Troubleshooting
### Debug Mode
Enable debug mode to troubleshoot issues by adding an init container with a shell:
```yaml
rustical:
debug:
enabled: true
```
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
```bash
kubectl exec -it <pod-name> -c debug -- sh
```
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
### Pod not starting
```bash
kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=helm-rustical
```
### Check logs
```bash
kubectl logs helm-rustical-0 -n apps-roboces
```

View file

@ -0,0 +1,13 @@
{{- if .Values.rustical.config }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "rustical.fullname" . }}-config
labels:
{{- include "rustical.labels" . | nindent 4 }}
data:
{{- range $key, $value := .Values.rustical.config }}
{{- $envKey := printf "RUSTICAL_%s" ($key | upper | replace "." "__" | replace "-" "_") }}
{{ $envKey }}: {{ $value | quote }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,36 @@
{{- if .Values.rustical.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "rustical.fullname" . }}
labels:
{{- include "rustical.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: StatefulSet
name: {{ include "rustical.fullname" . }}
{{- if .Values.rustical.autoscaling.minReplicas }}
minReplicas: {{ .Values.rustical.autoscaling.minReplicas }}
{{- end }}
{{- if .Values.rustical.autoscaling.maxReplicas }}
maxReplicas: {{ .Values.rustical.autoscaling.maxReplicas }}
{{- end }}
metrics:
{{- if .Values.rustical.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.rustical.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.rustical.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.rustical.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View file

@ -1,23 +1,23 @@
{{- if .Values.ingress.enabled -}}
{{- if .Values.rustical.ingress.enabled -}}
{{- $fullName := include "rustical.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- $svcPort := .Values.rustical.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "rustical.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
{{- with .Values.rustical.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.rustical.ingress.className }}
ingressClassName: {{ .Values.rustical.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
{{- if .Values.rustical.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
{{- range .Values.rustical.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
@ -26,7 +26,7 @@ spec:
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
{{- range .Values.rustical.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:

View file

@ -5,10 +5,10 @@ metadata:
labels:
{{- include "rustical.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
type: {{ .Values.rustical.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
- port: {{ .Values.rustical.service.port }}
targetPort: {{ .Values.rustical.service.targetPort }}
protocol: TCP
name: http
selector:

View file

@ -6,40 +6,35 @@ metadata:
{{- include "rustical.labels" . | nindent 4 }}
spec:
serviceName: {{ include "rustical.fullname" . }}-headless
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- if not .Values.rustical.autoscaling.enabled }}
replicas: {{ .Values.rustical.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "rustical.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
{{- with .Values.rustical.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "rustical.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
{{- with .Values.rustical.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
{{- if .Values.debug.enabled }}
{{- toYaml .Values.rustical.podSecurityContext | nindent 8 }}
{{- if .Values.rustical.debug.enabled }}
initContainers:
- name: debug
image: alpine:3.19
command: ["sleep", "infinity"]
envFrom:
- secretRef:
name: {{ .Values.secret.existingSecretName }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
name: {{ .Values.rustical.secret.existingSecretName }}
volumeMounts:
- name: data
mountPath: /var/lib/rustical
@ -53,67 +48,64 @@ spec:
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullpolicy }}
{{- toYaml .Values.rustical.securityContext | nindent 12 }}
image: "{{ .Values.rustical.image.repository }}:{{ .Values.rustical.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.rustical.image.pullpolicy }}
envFrom:
- configMapRef:
name: {{ include "rustical.fullname" . }}-config
- secretRef:
name: {{ .Values.secret.existingSecretName | default (include "rustical.fullname" .) }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
name: {{ .Values.rustical.secret.existingSecretName | default (include "rustical.fullname" .) }}
ports:
- name: http
containerPort: 4000
protocol: TCP
{{- if not .Values.debug.enabled }}
{{- if not .Values.rustical.debug.enabled }}
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
{{- toYaml .Values.rustical.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
{{- toYaml .Values.rustical.readinessProbe | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- toYaml .Values.rustical.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /var/lib/rustical
{{- if .Values.persistence.existingClaim }}
{{- if .Values.rustical.persistence.existingClaim }}
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Values.persistence.existingClaim }}
claimName: {{ .Values.rustical.persistence.existingClaim }}
{{- end }}
{{- with .Values.nodeSelector }}
{{- with .Values.rustical.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
{{- with .Values.rustical.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
{{- with .Values.rustical.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
restartPolicy: Always
automountServiceAccountToken: false
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
{{- if and .Values.rustical.persistence.enabled (not .Values.rustical.persistence.existingClaim) }}
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- {{ .Values.persistence.accessMode }}
{{- if .Values.persistence.storageClass }}
{{- if eq "-" .Values.persistence.storageClass }}
- {{ .Values.rustical.persistence.accessMode }}
{{- if .Values.rustical.persistence.storageClass }}
{{- if eq "-" .Values.rustical.persistence.storageClass }}
storageClassName: ""
{{- else }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
storageClassName: {{ .Values.rustical.persistence.storageClass | quote }}
{{- end }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.size }}
storage: {{ .Values.rustical.persistence.size }}
{{- end }}

View file

@ -1,4 +1,4 @@
# Configuration is managed in k8s/argo-apps/rustical.yaml
rustical:
replicaCount: 1
image:
@ -28,6 +28,10 @@ persistence:
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 3
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
resources: {}
@ -53,7 +57,7 @@ nodeSelector: {}
tolerations: []
affinity: {}
env: {}
config: {}
secret:
enabled: true

View file

@ -2,7 +2,7 @@ apiVersion: v2
name: immich
description: A Helm chart for Immich photo and video backup solution
type: application
version: 0.2.3
version: 1.0.1
appVersion: v3.1.0
keywords:
- immich

192
k8s/charts/immich/README.md Normal file
View file

@ -0,0 +1,192 @@
# immich
A Helm chart for Immich, a self-hosted photo and video backup solution.
## TL;DR
```bash
helm install immich oci://git.roboces.dev/catalin/fukuops/immich --version 1.0.0 -n apps-roboces
```
## Prerequisites
- Kubernetes 1.19+
- Helm 3+
- A NFS storage class (default: `truenas-nfs-csi`)
- External PostgreSQL database
- External Redis instance
## Configuration
All values are nested under the `immich:` key. Example:
```yaml
immich:
replicaCount: 1
service:
type: ClusterIP
port: 2283
config:
database:
hostname: "192.168.1.3"
port: 55432
username: ""
password: ""
name: ""
redis:
hostname: "192.168.1.3"
port: 6379
general:
timezone: Europe/Madrid
persistence:
library:
enabled: true
storageClass: "truenas-nfs-csi"
accessMode: ReadWriteMany
size: 10Gi
machineLearning:
enabled: false
secret:
enabled: false
existingSecretName: ""
```
## Values Reference
### Main Application
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `immich.replicaCount` | int | `1` | Number of replicas |
| `immich.image.repository` | string | `ghcr.io/immich-app/immich-server` | Image repository |
| `immich.image.pullPolicy` | string | `Always` | Image pull policy |
| `immich.image.tag` | string | `.Chart.AppVersion` | Image tag |
| `immich.service.type` | string | `ClusterIP` | Service type |
| `immich.service.port` | int | `2283` | Service port |
| `immich.service.targetPort` | int | `2283` | Container port |
| `immich.ingress.enabled` | bool | `false` | Enable ingress |
| `immich.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
| `immich.resources` | object | `{}` | Container resources |
| `immich.livenessProbe` | object | (see values.yaml) | Liveness probe |
| `immich.readinessProbe` | object | (see values.yaml) | Readiness probe |
### Database Configuration
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `immich.config.database.hostname` | string | `""` | External database hostname |
| `immich.config.database.port` | int | `55432` | Database port |
| `immich.config.database.username` | string | `""` | Database username |
| `immich.config.database.password` | string | `""` | Database password |
| `immich.config.database.name` | string | `""` | Database name |
### Redis Configuration
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `immich.config.redis.hostname` | string | `""` | External Redis hostname |
| `immich.config.redis.port` | int | `6379` | Redis port |
| `immich.config.redis.dbIndex` | string | `""` | Redis database index |
### General Configuration
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `immich.config.general.timezone` | string | `Europe/Madrid` | Timezone |
### Persistence
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `immich.persistence.library.enabled` | bool | `true` | Enable library persistence |
| `immich.persistence.library.storageClass` | string | `truenas-nfs-csi` | Storage class |
| `immich.persistence.library.size` | string | `10Gi` | PVC size |
| `immich.persistence.encodedVideo.enabled` | bool | `true` | Enable encoded video persistence |
| `immich.persistence.thumbs.enabled` | bool | `true` | Enable thumbnails persistence |
| `immich.persistence.upload.enabled` | bool | `true` | Enable uploads persistence |
| `immich.persistence.profile.enabled` | bool | `true` | Enable profiles persistence |
| `immich.persistence.backups.enabled` | bool | `true` | Enable backups persistence |
| `immich.persistence.modelCache.enabled` | bool | `true` | Enable model cache persistence |
### Machine Learning
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `immich.machineLearning.enabled` | bool | `false` | Enable machine learning module |
| `immich.machineLearning.image.repository` | string | `ghcr.io/immich-app/immich-machine-learning` | ML image repository |
| `immich.machineLearning.replicaCount` | int | `1` | Number of ML replicas |
| `immich.machineLearning.service.port` | int | `3003` | ML service port |
| `immich.machineLearning.persistence.modelCache.enabled` | bool | `true` | Enable model cache persistence |
### Secret Management
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `immich.secret.enabled` | bool | `false` | Enable secret management |
| `immich.secret.existingSecretName` | string | `""` | Existing secret name |
## ConfigMap
The chart creates a ConfigMap with database and Redis environment variables:
- `TZ`
- `IMMICH_PORT`
- `IMMICH_MACHINE_LEARNING_URL`
- `DB_HOSTNAME`, `DB_PORT`, `DB_USERNAME`, `DB_DATABASE_NAME`
- `REDIS_HOSTNAME`, `REDIS_PORT`, `REDIS_DB`
## Persistence
The chart creates multiple PVCs:
- `immich-library` - Original photos and videos
- `immich-encoded-video` - Transcoded videos
- `immich-thumbs` - Generated thumbnails
- `immich-upload` - Upload queue
- `immich-profile` - User profiles
- `immich-backups` - Backup data
- `immich-model-cache` - ML model cache
The library PVC uses `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode.
## Troubleshooting
### Debug Mode
Enable debug mode to troubleshoot issues by adding an init container with a shell:
```yaml
immich:
debug:
enabled: true
```
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
```bash
kubectl exec -it <pod-name> -c debug -- sh
```
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
### Pod not starting
```bash
kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=immich
```
### Check logs
```bash
kubectl logs immich-server-0 -n apps-roboces
```
### Check ML logs (if enabled)
```bash
kubectl logs immich-machine-learning-0 -n apps-roboces
```
### Verify configmap
```bash
kubectl get configmap immich-config -n apps-roboces -o yaml
```

View file

@ -1,22 +1,31 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "immich.fullname" . }}
name: {{ include "immich.fullname" . }}-config
labels:
{{- include "immich.labels" . | nindent 4 }}
data:
TZ: {{ .Values.immich.general.tz | quote }}
TZ: {{ .Values.immich.config.general.timezone | quote }}
IMMICH_PORT: {{ .Values.immich.service.port | quote }}
IMMICH_MACHINE_LEARNING_URL: {{ printf "http://%s-machine-learning:3003" (include "immich.fullname" .) | quote }}
{{- range $key, $value := .Values.immich.db }}
{{- if and (ne $key "password") $value }}
{{ $keyName := printf "DB_%s" (upper $key) }}
{{ $keyName }}: {{ $value | quote }}
{{- if .Values.immich.config.database.hostname }}
DB_HOSTNAME: {{ .Values.immich.config.database.hostname | quote }}
{{- end }}
{{- if .Values.immich.config.database.port }}
DB_PORT: {{ .Values.immich.config.database.port | quote }}
{{- end }}
{{- range $key, $value := .Values.immich.redis }}
{{- if and (ne $key "password") (ne $key "username") $value }}
{{ $keyName := printf "REDIS_%s" (upper $key) }}
{{ $keyName }}: {{ $value | quote }}
{{- if .Values.immich.config.database.username }}
DB_USERNAME: {{ .Values.immich.config.database.username | quote }}
{{- end }}
{{- if .Values.immich.config.database.name }}
DB_DATABASE_NAME: {{ .Values.immich.config.database.name | quote }}
{{- end }}
{{- if .Values.immich.config.redis.hostname }}
REDIS_HOSTNAME: {{ .Values.immich.config.redis.hostname | quote }}
{{- end }}
{{- if .Values.immich.config.redis.port }}
REDIS_PORT: {{ .Values.immich.config.redis.port | quote }}
{{- end }}
{{- if .Values.immich.config.redis.dbIndex }}
REDIS_DB: {{ .Values.immich.config.redis.dbIndex | quote }}
{{- end }}

View file

@ -62,6 +62,50 @@ spec:
mountPath: /data/profile
- name: backups
mountPath: /data/backups
{{- if .Values.immich.debug.enabled }}
- name: debug
image: alpine:3.19
command: ["sleep", "infinity"]
envFrom:
{{- if .Values.immich.secret.existingSecretName }}
- secretRef:
name: {{ .Values.immich.secret.existingSecretName }}
{{- else if .Values.immich.secret.enabled }}
- secretRef:
name: {{ include "immich.fullname" . }}
{{- end }}
- configMapRef:
name: {{ include "immich.fullname" . }}-config
volumeMounts:
- name: library
mountPath: /data/library
{{- if .Values.immich.persistence.encodedVideo.enabled }}
- name: encoded-video
mountPath: /data/encoded-video
{{- end }}
{{- if .Values.immich.persistence.thumbs.enabled }}
- name: thumbs
mountPath: /data/thumbs
{{- end }}
{{- if .Values.immich.persistence.upload.enabled }}
- name: upload
mountPath: /data/upload
{{- end }}
{{- if .Values.immich.persistence.profile.enabled }}
- name: profile
mountPath: /data/profile
{{- end }}
{{- if .Values.immich.persistence.backups.enabled }}
- name: backups
mountPath: /data/backups
{{- end }}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
capabilities:
drop:
- all
{{- end }}
containers:
- name: {{ .Chart.Name }}-server
securityContext:
@ -69,32 +113,30 @@ spec:
image: "{{ .Values.immich.image.repository }}:{{ .Values.immich.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.immich.image.pullPolicy }}
envFrom:
{{- if .Values.secret.existingSecretName }}
{{- if .Values.immich.secret.existingSecretName }}
- secretRef:
name: {{ .Values.secret.existingSecretName }}
{{- else if .Values.secret.enabled }}
name: {{ .Values.immich.secret.existingSecretName }}
{{- else if .Values.immich.secret.enabled }}
- secretRef:
name: {{ include "immich.fullname" . }}
{{- end }}
- configMapRef:
name: {{ include "immich.fullname" . }}
name: {{ include "immich.fullname" . }}-config
env:
- name: IMMICH_PORT
value: {{ .Values.immich.service.port | quote }}
- name: IMMICH_MACHINE_LEARNING_URL
value: {{ printf "http://%s-machine-learning:3003" (include "immich.fullname" .) | quote }}
{{- range $key, $value := .Values.immich.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- if not .Values.immich.debug.enabled }}
livenessProbe:
{{- toYaml .Values.immich.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.immich.readinessProbe | nindent 12 }}
{{- end }}
ports:
- name: http
containerPort: 2283
protocol: TCP
livenessProbe:
{{- toYaml .Values.immich.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.immich.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.immich.resources | nindent 12 }}
volumeMounts:

View file

@ -106,7 +106,7 @@ spec:
requests:
storage: {{ .Values.immich.persistence.backups.size }}
{{- end }}
{{- if and .Values.machineLearning.enabled .Values.machineLearning.persistence.modelCache.enabled }}
{{- if and .Values.immich.machineLearning.enabled .Values.immich.machineLearning.persistence.modelCache.enabled }}
---
apiVersion: v1
kind: PersistentVolumeClaim
@ -116,11 +116,11 @@ metadata:
{{- include "immich.labels" . | nindent 4 }}
spec:
accessModes:
- {{ .Values.machineLearning.persistence.modelCache.accessMode }}
{{- if .Values.machineLearning.persistence.modelCache.storageClass }}
storageClassName: {{ .Values.machineLearning.persistence.modelCache.storageClass | quote }}
- {{ .Values.immich.machineLearning.persistence.modelCache.accessMode }}
{{- if .Values.immich.machineLearning.persistence.modelCache.storageClass }}
storageClassName: {{ .Values.immich.machineLearning.persistence.modelCache.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.machineLearning.persistence.modelCache.size }}
storage: {{ .Values.immich.machineLearning.persistence.modelCache.size }}
{{- end }}

View file

@ -1,4 +1,4 @@
{{- if and .Values.secret.enabled (not .Values.secret.existingSecretName) -}}
{{- if and .Values.immich.secret.enabled (not .Values.immich.secret.existingSecretName) -}}
apiVersion: v1
kind: Secret
metadata:
@ -7,19 +7,19 @@ metadata:
{{- include "immich.labels" . | nindent 4 }}
type: Opaque
stringData:
{{- if .Values.immich.db.password }}
DB_PASSWORD: {{ .Values.immich.db.password | quote }}
{{- if .Values.immich.config.database.password }}
DB_PASSWORD: {{ .Values.immich.config.database.password | quote }}
{{- end }}
{{- if .Values.immich.db.username }}
DB_USERNAME: {{ .Values.immich.db.username | quote }}
{{- if .Values.immich.config.database.username }}
DB_USERNAME: {{ .Values.immich.config.database.username | quote }}
{{- end }}
{{- if .Values.immich.redis.password }}
REDIS_PASSWORD: {{ .Values.immich.redis.password | quote }}
{{- if .Values.immich.config.redis.password }}
REDIS_PASSWORD: {{ .Values.immich.config.redis.password | quote }}
{{- end }}
{{- if .Values.immich.redis.username }}
REDIS_USERNAME: {{ .Values.immich.redis.username | quote }}
{{- if .Values.immich.config.redis.username }}
REDIS_USERNAME: {{ .Values.immich.config.redis.username | quote }}
{{- end }}
{{- range $key, $value := .Values.secret.data }}
{{- range $key, $value := .Values.immich.secret.data }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}

View file

@ -15,7 +15,7 @@ spec:
{{- include "immich.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: server
---
{{- if .Values.machineLearning.enabled }}
{{- if .Values.immich.machineLearning.enabled }}
apiVersion: v1
kind: Service
metadata:
@ -24,9 +24,9 @@ metadata:
{{- include "immich.labels" . | nindent 4 }}
app.kubernetes.io/component: machine-learning
spec:
type: {{ .Values.machineLearning.service.type }}
type: {{ .Values.immich.machineLearning.service.type }}
ports:
- port: {{ .Values.machineLearning.service.port }}
- port: {{ .Values.immich.machineLearning.service.port }}
targetPort: http
protocol: TCP
name: http

View file

@ -1,4 +1,4 @@
{{- if .Values.machineLearning.enabled -}}
{{- if .Values.immich.machineLearning.enabled -}}
apiVersion: apps/v1
kind: StatefulSet
metadata:
@ -8,8 +8,8 @@ metadata:
app.kubernetes.io/component: machine-learning
spec:
serviceName: {{ include "immich.fullname" . }}-machine-learning-headless
{{- if not .Values.machineLearning.autoscaling.enabled }}
replicas: {{ .Values.machineLearning.replicaCount }}
{{- if not .Values.immich.machineLearning.autoscaling.enabled }}
replicas: {{ .Values.immich.machineLearning.replicaCount }}
{{- end }}
selector:
matchLabels:
@ -17,7 +17,7 @@ spec:
app.kubernetes.io/component: machine-learning
template:
metadata:
{{- with .Values.machineLearning.podAnnotations }}
{{- with .Values.immich.machineLearning.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
@ -25,41 +25,41 @@ spec:
{{- include "immich.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: machine-learning
spec:
{{- with .Values.machineLearning.imagePullSecrets }}
{{- with .Values.immich.machineLearning.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
{{- toYaml .Values.machineLearning.podSecurityContext | nindent 8 }}
{{- toYaml .Values.immich.machineLearning.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}-machine-learning
securityContext:
{{- toYaml .Values.machineLearning.securityContext | nindent 12 }}
image: "{{ .Values.machineLearning.image.repository }}:{{ .Values.machineLearning.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.machineLearning.image.pullPolicy }}
{{- toYaml .Values.immich.machineLearning.securityContext | nindent 12 }}
image: "{{ .Values.immich.machineLearning.image.repository }}:{{ .Values.immich.machineLearning.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.immich.machineLearning.image.pullPolicy }}
ports:
- name: http
containerPort: 3003
protocol: TCP
env:
- name: IMMICH_PORT
value: {{ .Values.machineLearning.service.port | quote }}
value: {{ .Values.immich.machineLearning.service.port | quote }}
- name: IMMICH_MACHINE_LEARNING_PORT
value: "3003"
resources:
{{- toYaml .Values.machineLearning.resources | nindent 12 }}
{{- toYaml .Values.immich.machineLearning.resources | nindent 12 }}
volumeMounts:
- name: model-cache
mountPath: /cache
{{- with .Values.machineLearning.nodeSelector }}
{{- with .Values.immich.machineLearning.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.machineLearning.affinity }}
{{- with .Values.immich.machineLearning.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.machineLearning.tolerations }}
{{- with .Values.immich.machineLearning.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View file

@ -19,22 +19,21 @@ immich:
ingress:
enabled: false
db:
hostname: "192.168.1.3"
config:
database:
hostname: ""
port: 55432
username: ""
password: ""
database_name: ""
name: ""
redis:
hostname: "192.168.1.3"
port: 300036
hostname: ""
port: 6379
username: ""
password: ""
dbindex: ""
dbIndex: ""
general:
tz: "Europe/Madrid"
timezone: Europe/Madrid
persistence:
library:
@ -104,8 +103,6 @@ immich:
tolerations: []
affinity: {}
env: {}
machineLearning:
enabled: false
@ -167,3 +164,6 @@ secret:
enabled: false
existingSecretName: ""
data: {}
debug:
enabled: false

View file

@ -2,9 +2,7 @@ apiVersion: v2
name: miniflux
description: A Helm chart for Miniflux RSS reader
type: application
version: 0.1.0
version: 1.0.2
appVersion: 2.3.3
annotations:
artifacthub.io/images: |
- name: miniflux
image: miniflux/miniflux:2.3.3
artifacthub.io/images: "- name: miniflux\n image: miniflux/miniflux:2.3.3\n"

View file

@ -0,0 +1,153 @@
# miniflux
A Helm chart for Miniflux, a minimalist and open source RSS reader.
## TL;DR
```bash
helm install miniflux oci://git.roboces.dev/catalin/fukuops/miniflux --version 1.0.1 -n apps-roboces
```
## Prerequisites
- Kubernetes 1.19+
- Helm 3+
- An existing `Secret` containing:
- `DATABASE_URL` - PostgreSQL connection string
- `ADMIN_USERNAME` - Admin username (optional if oauth2 is configured)
- `ADMIN_PASSWORD` - Admin password (optional if oauth2 is configured)
## Configuration
All values are nested under the `miniflux:` key. Example:
```yaml
miniflux:
service:
type: LoadBalancer
config:
database:
url: "postgres://user:pass@host:5432/miniflux"
general:
createAdmin: true
runMigrations: true
workerPoolSize: 1
pollingFrequency: 60
oauth2:
enabled: true
provider: oidc
redirectUrl: https://feeds.example.com/oauth2/oidc/callback
oidcDiscoveryEndpoint: https://auth.example.com/application/o/miniflux/
userCreation: true
metrics:
enabled: true
allowedNetworks: 10.0.0.0/8
secret:
existingSecretName: miniflux
```
## Values Reference
### Main Settings
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `miniflux.replicaCount` | int | `1` | Number of replicas |
| `miniflux.image.repository` | string | `miniflux/miniflux` | Image repository |
| `miniflux.image.pullpolicy` | string | `always` | Image pull policy |
| `miniflux.image.tag` | string | `.Chart.AppVersion` | Image tag |
| `miniflux.service.type` | string | `ClusterIP` | Service type |
| `miniflux.service.port` | int | `8888` | Service port |
| `miniflux.service.targetPort` | int | `8080` | Container port |
| `miniflux.ingress.enabled` | bool | `false` | Enable ingress |
| `miniflux.resources` | object | `{}` | Container resources |
| `miniflux.livenessProbe` | object | `{}` | Liveness probe |
| `miniflux.readinessProbe` | object | `{}` | Readiness probe |
| `miniflux.secret.existingSecretName` | string | `""` | Existing secret name |
| `miniflux.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
| `miniflux.nodeSelector` | object | `{}` | Node selector |
| `miniflux.tolerations` | array | `[]` | Tolerations |
| `miniflux.affinity` | object | `{}` | Affinity rules |
| `miniflux.autoscaling.enabled` | bool | `false` | Enable autoscaling |
| `miniflux.autoscaling.minReplicas` | int | `1` | Minimum replicas |
| `miniflux.autoscaling.maxReplicas` | int | `3` | Maximum replicas |
| `miniflux.autoscaling.targetCPUUtilizationPercentage` | int | `80` | Target CPU utilization for HPA |
| `miniflux.autoscaling.targetMemoryUtilizationPercentage` | int | `80` | Target memory utilization for HPA |
### Database Configuration
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `miniflux.config.database.url` | string | `""` | PostgreSQL connection URL |
### General Configuration
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `miniflux.config.general.createAdmin` | bool | `false` | Create admin user on startup |
| `miniflux.config.general.runMigrations` | bool | `false` | Run database migrations |
| `miniflux.config.general.workerPoolSize` | int | `1` | Number of worker threads |
| `miniflux.config.general.pollingFrequency` | int | `60` | Feed refresh interval (minutes) |
| `miniflux.config.general.batchSize` | int | `10` | Number of feeds to refresh per batch |
### OAuth2 Configuration
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `miniflux.config.oauth2.enabled` | bool | `false` | Enable OAuth2 authentication |
| `miniflux.config.oauth2.provider` | string | `oidc` | OAuth2 provider |
| `miniflux.config.oauth2.redirectUrl` | string | `""` | OAuth redirect URL |
| `miniflux.config.oauth2.oidcDiscoveryEndpoint` | string | `""` | OIDC discovery endpoint URL |
| `miniflux.config.oauth2.userCreation` | bool | `false` | Allow OAuth users to create accounts |
### Metrics Configuration
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `miniflux.config.metrics.enabled` | bool | `false` | Enable metrics endpoint |
| `miniflux.config.metrics.allowedNetworks` | string | `""` | Networks allowed to access metrics (CIDR) |
### YouTube Configuration
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `miniflux.config.youtube.fetchWatchTime` | bool | `false` | Fetch YouTube watch time data |
## Secret Management
The chart looks for an existing `Secret` with the name specified in `miniflux.secret.existingSecretName`. Required secret keys:
- `DATABASE_URL` - PostgreSQL connection string
- `ADMIN_USERNAME` - Admin username (if `config.general.createAdmin=true`)
- `ADMIN_PASSWORD` - Admin password (if `config.general.createAdmin=true`)
## Troubleshooting
### Debug Mode
Enable debug mode to troubleshoot issues by adding an init container with a shell:
```yaml
miniflux:
debug:
enabled: true
```
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
```bash
kubectl exec -it <pod-name> -c debug -- sh
```
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
### Check logs
```bash
kubectl logs miniflux-0 -n apps-roboces
```
### Verify configmap
```bash
kubectl get configmap miniflux-config -n apps-roboces -o yaml
```

View file

@ -0,0 +1,40 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "miniflux.fullname" . }}-config
labels:
{{- include "miniflux.labels" . | nindent 4 }}
data:
{{- if .Values.miniflux.config.database.url }}
DATABASE_URL: {{ .Values.miniflux.config.database.url | quote }}
{{- end }}
{{- if .Values.miniflux.config.general.runMigrations }}
RUN_MIGRATIONS: {{ .Values.miniflux.config.general.runMigrations | quote }}
{{- end }}
{{- if .Values.miniflux.config.general.createAdmin }}
CREATE_ADMIN: {{ .Values.miniflux.config.general.createAdmin | quote }}
{{- end }}
WORKER_POOL_SIZE: {{ .Values.miniflux.config.general.workerPoolSize | quote }}
POLLING_FREQUENCY: {{ .Values.miniflux.config.general.pollingFrequency | quote }}
BATCH_SIZE: {{ .Values.miniflux.config.general.batchSize | quote }}
{{- if .Values.miniflux.config.oauth2.enabled }}
OAUTH2_PROVIDER: {{ .Values.miniflux.config.oauth2.provider | quote }}
{{- if .Values.miniflux.config.oauth2.redirectUrl }}
OAUTH2_REDIRECT_URL: {{ .Values.miniflux.config.oauth2.redirectUrl | quote }}
{{- end }}
{{- if .Values.miniflux.config.oauth2.oidcDiscoveryEndpoint }}
OAUTH2_OIDC_DISCOVERY_ENDPOINT: {{ .Values.miniflux.config.oauth2.oidcDiscoveryEndpoint | quote }}
{{- end }}
{{- if .Values.miniflux.config.oauth2.userCreation }}
OAUTH2_USER_CREATION: {{ .Values.miniflux.config.oauth2.userCreation | quote }}
{{- end }}
{{- end }}
{{- if .Values.miniflux.config.metrics.enabled }}
METRICS_COLLECTOR: {{ .Values.miniflux.config.metrics.enabled | quote }}
{{- if .Values.miniflux.config.metrics.allowedNetworks }}
METRICS_ALLOWED_NETWORKS: {{ .Values.miniflux.config.metrics.allowedNetworks | quote }}
{{- end }}
{{- end }}
{{- if .Values.miniflux.config.youtube.fetchWatchTime }}
FETCH_YOUTUBE_WATCH_TIME: {{ .Values.miniflux.config.youtube.fetchWatchTime | quote }}
{{- end }}

View file

@ -7,8 +7,8 @@ metadata:
annotations:
kube-score/ignore: pod-networkpolicy,deployment-has-host-podantiaffinity
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- if not .Values.miniflux.autoscaling.enabled }}
replicas: {{ .Values.miniflux.replicaCount }}
{{- end }}
selector:
matchLabels:
@ -20,52 +20,49 @@ spec:
type: RollingUpdate
template:
metadata:
{{- with .Values.podAnnotations }}
{{- with .Values.miniflux.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "miniflux.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
{{- with .Values.miniflux.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
{{- toYaml .Values.miniflux.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- toYaml .Values.miniflux.securityContext | nindent 12 }}
image: "{{ .Values.miniflux.image.repository }}:{{ .Values.miniflux.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.miniflux.image.pullPolicy }}
envFrom:
- configMapRef:
name: {{ include "miniflux.fullname" . }}-config
- secretRef:
name: {{ .Values.secret.existingSecretName | default (include "miniflux.fullname" .) }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
name: {{ .Values.miniflux.secret.existingSecretName | default (include "miniflux.fullname" .) }}
ports:
- name: http
containerPort: 8080
protocol: TCP
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
{{- toYaml .Values.miniflux.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
{{- toYaml .Values.miniflux.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
{{- toYaml .Values.miniflux.resources | nindent 12 }}
{{- with .Values.miniflux.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
{{- with .Values.miniflux.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
{{- with .Values.miniflux.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View file

@ -0,0 +1,33 @@
{{- if .Values.miniflux.autoscaling.enabled }}
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "miniflux.fullname" . }}
labels:
{{- include "miniflux.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "miniflux.fullname" . }}
minReplicas: {{ .Values.miniflux.autoscaling.minReplicas }}
maxReplicas: {{ .Values.miniflux.autoscaling.maxReplicas }}
metrics:
{{- if .Values.miniflux.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.miniflux.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.miniflux.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.miniflux.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View file

@ -1,23 +1,23 @@
{{- if .Values.ingress.enabled -}}
{{- if .Values.miniflux.ingress.enabled -}}
{{- $fullName := include "miniflux.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- $svcPort := .Values.miniflux.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "miniflux.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
{{- with .Values.miniflux.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.miniflux.ingress.className }}
ingressClassName: {{ .Values.miniflux.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
{{- if .Values.miniflux.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
{{- range .Values.miniflux.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
@ -26,7 +26,7 @@ spec:
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
{{- range .Values.miniflux.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:

View file

@ -1,4 +1,4 @@
{{- if .Values.podDisruptionBudget.enabled -}}
{{- if .Values.miniflux.podDisruptionBudget.enabled -}}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
@ -6,11 +6,11 @@ metadata:
labels:
{{- include "miniflux.labels" . | nindent 4 }}
spec:
{{- if .Values.podDisruptionBudget.minAvailable }}
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
{{- if .Values.miniflux.podDisruptionBudget.minAvailable }}
minAvailable: {{ .Values.miniflux.podDisruptionBudget.minAvailable }}
{{- end }}
{{- if .Values.podDisruptionBudget.maxUnavailable }}
maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
{{- if .Values.miniflux.podDisruptionBudget.maxUnavailable }}
maxUnavailable: {{ .Values.miniflux.podDisruptionBudget.maxUnavailable }}
{{- end }}
selector:
matchLabels:

View file

@ -1,4 +1,4 @@
{{- if and .Values.secret.enabled (not .Values.secret.existingSecretName) -}}
{{- if and .Values.miniflux.secret.enabled (not .Values.miniflux.secret.existingSecretName) -}}
apiVersion: v1
kind: Secret
metadata:
@ -7,7 +7,7 @@ metadata:
{{- include "miniflux.labels" . | nindent 4 }}
type: Opaque
stringData:
{{- range $key, $value := .Values.secret.data }}
{{- range $key, $value := .Values.miniflux.secret.data }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}

View file

@ -5,10 +5,10 @@ metadata:
labels:
{{- include "miniflux.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
type: {{ .Values.miniflux.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
- port: {{ .Values.miniflux.service.port }}
targetPort: {{ .Values.miniflux.service.targetPort }}
protocol: TCP
name: http
selector:

View file

@ -1,4 +1,4 @@
# Configuration is managed in k8s/argo-apps/miniflux.yaml
miniflux:
replicaCount: 1
image:
@ -19,6 +19,27 @@ service:
ingress:
enabled: false
config:
database:
url: ""
general:
createAdmin: false
runMigrations: false
workerPoolSize: 1
pollingFrequency: 60
batchSize: 10
oauth2:
enabled: false
provider: oidc
redirectUrl: ""
oidcDiscoveryEndpoint: ""
userCreation: false
metrics:
enabled: false
allowedNetworks: ""
youtube:
fetchWatchTime: false
resources: {}
livenessProbe: {}
@ -26,6 +47,10 @@ readinessProbe: {}
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 3
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
@ -34,9 +59,10 @@ affinity: {}
podDisruptionBudget:
enabled: false
env: {}
secret:
enabled: false
existingSecretName: ""
data: {}
debug:
enabled: false

View file

@ -5,7 +5,7 @@ description: 'Ultra-fast, secure & lightweight self-hosted cloud storage — you
'
type: application
version: 1.0.0
version: 1.0.1
appVersion: 0.8.6
annotations:
artifacthub.io/images: "- name: oxicloud\n image: diocrafts/oxicloud:0.8.6\n"

View file

@ -73,6 +73,7 @@ oxicloud:
| `oxicloud.persistence.size` | string | `50Gi` | PVC size |
| `oxicloud.secret.existingSecretName` | string | `""` | Existing secret name |
| `oxicloud.wopi.enabled` | bool | `false` | Enable WOPI/Collabora integration |
| `oxicloud.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
| `oxicloud.ingress.enabled` | bool | `false` | Enable ingress |
| `oxicloud.ingress.className` | string | `""` | Ingress class |
| `oxicloud.resources` | object | `{}` | Container resources |
@ -115,6 +116,24 @@ Required secret keys depend on configuration:
## Troubleshooting
### Debug Mode
Enable debug mode to troubleshoot issues by adding an init container with a shell:
```yaml
oxicloud:
debug:
enabled: true
```
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
```bash
kubectl exec -it <pod-name> -c debug -- sh
```
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
### Pod not starting
Check if the PVC is bound:

View file

@ -1,4 +1,3 @@
---
{{- if .Values.oxicloud.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
@ -51,7 +50,6 @@ spec:
port:
number: {{ .Values.oxicloud.wopi.collabora.service.port }}
{{- end }}
- path: /
pathType: Prefix
backend:

View file

@ -85,3 +85,6 @@ oxicloud:
enabled: true
existingSecretName: "oxicloud-secret"
data: {}
debug:
enabled: false

View file

@ -2,9 +2,7 @@ apiVersion: v2
name: vaultwarden
description: A Helm chart for Vaultwarden password manager
type: application
version: 0.1.0
version: 1.0.2
appVersion: 1.37.1
annotations:
artifacthub.io/images: |
- name: vaultwarden
image: vaultwarden/server:1.37.1-alpine
artifacthub.io/images: "- name: vaultwarden\n image: vaultwarden/server:1.37.1-alpine\n"

View file

@ -0,0 +1,132 @@
# vaultwarden
A Helm chart for Vaultwarden (formerly Bitwarden RS), a lightweight self-hosted password manager.
## TL;DR
```bash
helm install vaultwarden oci://git.roboces.dev/catalin/fukuops/vaultwarden --version 1.0.2 -n apps-roboces
```
## Prerequisites
- Kubernetes 1.19+
- Helm 3+
- A NFS storage class (default: `truenas-nfs-csi`)
- An existing `Secret` containing:
- `DATABASE_URL` (if using external database)
- `ADMIN_TOKEN` (optional - for admin panel)
- `SMTP_HOST` and related SMTP settings (optional - for email)
## Configuration
All values are nested under the `vaultwarden:` key. Example:
```yaml
vaultwarden:
persistence:
enabled: true
storageClass: "truenas-nfs-csi"
accessMode: ReadWriteMany
size: 5Gi
service:
type: ClusterIP
port: 80
secret:
existingSecretName: vaultwarden-kubernetes-secrets
config:
database:
url: ""
enableWal: true
connectionRetries: 15
webVault:
enabled: true
```
## Values Reference
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `vaultwarden.replicaCount` | int | `1` | Number of replicas |
| `vaultwarden.image.repository` | string | `vaultwarden/server` | Image repository |
| `vaultwarden.image.pullpolicy` | string | `Always` | Image pull policy |
| `vaultwarden.image.tag` | string | `.Chart.AppVersion` | Image tag |
| `vaultwarden.service.type` | string | `ClusterIP` | Service type |
| `vaultwarden.service.port` | int | `80` | Service port |
| `vaultwarden.service.targetPort` | int | `80` | Container port |
| `vaultwarden.ingress.enabled` | bool | `false` | Enable ingress |
| `vaultwarden.config.database.url` | string | `""` | Database connection URL |
| `vaultwarden.config.database.enableWal` | bool | `true` | Enable WAL mode |
| `vaultwarden.config.database.connectionRetries` | int | `15` | Connection retries |
| `vaultwarden.config.database.timeout` | int | `30` | Connection timeout |
| `vaultwarden.config.database.idleTimeout` | int | `600` | Idle timeout |
| `vaultwarden.config.webVault.enabled` | bool | `true` | Enable web vault |
| `vaultwarden.persistence.enabled` | bool | `true` | Enable persistence |
| `vaultwarden.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
| `vaultwarden.persistence.accessMode` | string | `ReadWriteMany` | Access mode |
| `vaultwarden.persistence.size` | string | `5Gi` | PVC size |
| `vaultwarden.secret.existingSecretName` | string | `vaultwarden-kubernetes-secrets` | Existing secret name |
| `vaultwarden.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
| `vaultwarden.resources` | object | `{}` | Container resources |
| `vaultwarden.livenessProbe` | object | (see values.yaml) | Liveness probe |
| `vaultwarden.readinessProbe` | object | (see values.yaml) | Readiness probe |
## ConfigMap
The chart creates a ConfigMap with database and web vault environment variables:
- `DATABASE_URL`
- `DB_ENABLE_WAL`
- `DB_CONNECTION_RETRIES`
- `DB_TIMEOUT`
- `DB_IDLE_TIMEOUT`
- `WEB_VAULT_ENABLED`
- `WEB_VAULT_FOLDER`
## Persistence
The chart creates a PVC named `vaultwarden-data`. The PVC uses the `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode.
## Secret Management
The chart looks for an existing `Secret` with the name specified in `vaultwarden.secret.existingSecretName`. Required/optional secret keys depend on configuration:
- `DATABASE_URL` - External database connection (if not using built-in SQLite)
- `ADMIN_TOKEN` - Admin panel access token
- `SMTP_HOST`, `SMTP_PORT`, `SMTP_FROM`, `SMTP_USER`, `SMTP_PASS` - Email configuration
## Troubleshooting
### Debug Mode
Enable debug mode to troubleshoot issues by adding an init container with a shell:
```yaml
vaultwarden:
debug:
enabled: true
```
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
```bash
kubectl exec -it <pod-name> -c debug -- sh
```
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
### Pod not starting
```bash
kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=vaultwarden
```
### Check logs
```bash
kubectl logs vaultwarden-0 -n apps-roboces
```
### Verify configmap
```bash
kubectl get configmap vaultwarden-config -n apps-roboces -o yaml
```

View file

@ -0,0 +1,37 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "vaultwarden.fullname" . }}-config
labels:
{{- include "vaultwarden.labels" . | nindent 4 }}
data:
{{- if .Values.vaultwarden.config.database.url }}
DATABASE_URL: {{ .Values.vaultwarden.config.database.url | quote }}
{{- end }}
{{- if .Values.vaultwarden.config.database.enableWal }}
DB_ENABLE_WAL: {{ .Values.vaultwarden.config.database.enableWal | quote }}
{{- end }}
{{- if .Values.vaultwarden.config.database.connectionRetries }}
DB_CONNECTION_RETRIES: {{ .Values.vaultwarden.config.database.connectionRetries | quote }}
{{- end }}
{{- if .Values.vaultwarden.config.database.timeout }}
DB_TIMEOUT: {{ .Values.vaultwarden.config.database.timeout | quote }}
{{- end }}
{{- if .Values.vaultwarden.config.database.idleTimeout }}
DB_IDLE_TIMEOUT: {{ .Values.vaultwarden.config.database.idleTimeout | quote }}
{{- end }}
{{- if .Values.vaultwarden.config.database.minConns }}
DB_MIN_CONNS: {{ .Values.vaultwarden.config.database.minConns | quote }}
{{- end }}
{{- if .Values.vaultwarden.config.database.maxConns }}
DB_MAX_CONNS: {{ .Values.vaultwarden.config.database.maxConns | quote }}
{{- end }}
{{- if .Values.vaultwarden.config.database.connInit }}
DB_CONN_INIT: {{ .Values.vaultwarden.config.database.connInit | quote }}
{{- end }}
{{- if .Values.vaultwarden.config.webVault.enabled }}
WEB_VAULT_ENABLED: {{ .Values.vaultwarden.config.webVault.enabled | quote }}
{{- end }}
{{- if .Values.vaultwarden.config.webVault.folder }}
WEB_VAULT_FOLDER: {{ .Values.vaultwarden.config.webVault.folder | quote }}
{{- end }}

View file

@ -1,23 +1,23 @@
{{- if .Values.ingress.enabled -}}
{{- if .Values.vaultwarden.ingress.enabled -}}
{{- $fullName := include "vaultwarden.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- $svcPort := .Values.vaultwarden.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "vaultwarden.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
{{- with .Values.vaultwarden.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- if .Values.vaultwarden.ingress.className }}
ingressClassName: {{ .Values.vaultwarden.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
{{- if .Values.vaultwarden.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
{{- range .Values.vaultwarden.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
@ -26,7 +26,7 @@ spec:
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
{{- range .Values.vaultwarden.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:

View file

@ -5,10 +5,10 @@ metadata:
labels:
{{- include "vaultwarden.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
type: {{ .Values.vaultwarden.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
- port: {{ .Values.vaultwarden.service.port }}
targetPort: {{ .Values.vaultwarden.service.targetPort }}
protocol: TCP
name: http
selector:

View file

@ -6,103 +6,86 @@ metadata:
{{- include "vaultwarden.labels" . | nindent 4 }}
spec:
serviceName: {{ include "vaultwarden.fullname" . }}-headless
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- if not .Values.vaultwarden.autoscaling.enabled }}
replicas: {{ .Values.vaultwarden.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "vaultwarden.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
{{- with .Values.vaultwarden.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "vaultwarden.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
{{- with .Values.vaultwarden.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
{{- toYaml .Values.vaultwarden.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default (printf "%s-alpine" .Chart.AppVersion) }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- toYaml .Values.vaultwarden.securityContext | nindent 12 }}
image: "{{ .Values.vaultwarden.image.repository }}:{{ .Values.vaultwarden.image.tag | default (printf "%s-alpine" .Chart.AppVersion) }}"
imagePullPolicy: {{ .Values.vaultwarden.image.pullPolicy }}
envFrom:
- configMapRef:
name: {{ include "vaultwarden.fullname" . }}-config
- secretRef:
name: {{ .Values.secret.existingSecretName | default (include "vaultwarden.fullname" .) }}
env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- if .Values.conf.db.database_url }}
- name: DATABASE_URL
value: {{ .Values.conf.db.database_url | quote }}
{{- end }}
{{- range $key, $value := .Values.conf.db }}
{{- if and (ne $key "database_url") $value }}
- name: {{ upper (print "DB_" $key) }}
value: {{ $value | quote }}
{{- end }}
{{- end }}
{{- range $key, $value := .Values.conf.web_vault }}
- name: {{ upper (print "WEB_VAULT_" $key) }}
value: {{ $value | quote }}
{{- end }}
name: {{ .Values.vaultwarden.secret.existingSecretName | default (include "vaultwarden.fullname" .) }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
{{- toYaml .Values.vaultwarden.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
{{- toYaml .Values.vaultwarden.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- toYaml .Values.vaultwarden.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /data
{{- if .Values.persistence.existingClaim }}
{{- if .Values.vaultwarden.persistence.existingClaim }}
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Values.persistence.existingClaim }}
claimName: {{ .Values.vaultwarden.persistence.existingClaim }}
{{- end }}
{{- with .Values.nodeSelector }}
{{- with .Values.vaultwarden.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
{{- with .Values.vaultwarden.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
{{- with .Values.vaultwarden.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
restartPolicy: Always
automountServiceAccountToken: false
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
{{- if and .Values.vaultwarden.persistence.enabled (not .Values.vaultwarden.persistence.existingClaim) }}
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- {{ .Values.persistence.accessMode }}
{{- if .Values.persistence.storageClass }}
{{- if eq "-" .Values.persistence.storageClass }}
- {{ .Values.vaultwarden.persistence.accessMode }}
{{- if .Values.vaultwarden.persistence.storageClass }}
{{- if eq "-" .Values.vaultwarden.persistence.storageClass }}
storageClassName: ""
{{- else }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
storageClassName: {{ .Values.vaultwarden.persistence.storageClass | quote }}
{{- end }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.size }}
storage: {{ .Values.vaultwarden.persistence.size }}
{{- end }}

View file

@ -1,4 +1,4 @@
# Configuration is managed in k8s/argo-apps/vaultwarden.yaml
vaultwarden:
replicaCount: 1
image:
@ -19,17 +19,17 @@ service:
ingress:
enabled: false
conf:
db:
database_url: ""
enable_db_wal: true
connection_retries: 15
config:
database:
url: ""
enableWal: true
connectionRetries: 15
timeout: 30
idle_timeout: 600
min_conns: 2
max_conns: 10
conn_init: ""
web_vault:
idleTimeout: 600
minConns: 2
maxConns: 10
connInit: ""
webVault:
enabled: true
folder: "web-vault/"
@ -67,9 +67,10 @@ nodeSelector: {}
tolerations: []
affinity: {}
env: {}
secret:
enabled: true
existingSecretName: "vaultwarden-kubernetes-secrets"
data: {}
debug:
enabled: false

View file

@ -19,22 +19,10 @@ spec:
- https://git.roboces.dev/catalin/fukuops.git
- https://charts.goauthentik.io/
- https://kubereboot.github.io/charts
- https://sqljames.github.io/factorio-server-charts/
- https://portainer.github.io/k8s/
- https://charts.bitnami.com/bitnami
- https://charts.crystalnet.org
- https://portainer.github.io/k8s/
- https://docs.renovatebot.com/helm-charts
- https://meilisearch.github.io/meilisearch-kubernetes
- https://kubetail-org.github.io/helm-charts/
- https://groundhog2k.github.io/helm-charts/
- registry-1.docker.io/cloudpirates
- https://vmware-tanzu.github.io/helm-charts/
- https://helm.runix.net
- https://rcourtman.github.io/Pulse
- ghcr.io/antoniolago/charts
- https://helm.elastic.co
- https://repo.helmforge.dev
- https://harish2k01.github.io/helm-charts/
- git.roboces.dev/catalin/fukuops
- git.roboces.dev/catalin

View file

@ -25,8 +25,3 @@ spec:
- https://git.roboces.dev/catalin/fukuops.git
- https://democratic-csi.github.io/charts/
- https://bitnami-labs.github.io/sealed-secrets
- https://grafana.github.io/helm-charts
- https://kubernetes-sigs.github.io/descheduler/
- https://github.com/rancher/system-upgrade-controller.git
- https://charts.bitnami.com/bitnami
- https://argoproj.github.io/argo-helm

View file

@ -14,9 +14,6 @@ spec:
- group: ""
kind: PersistentVolume
sourceRepos:
- https://git.roboces.dev/catalin/fukuops.git
- code.forgejo.org/forgejo-helm
- git.roboces.dev/catalin/fukuops
- git.roboces.dev/catalin
- https://git.roboces.dev/catalin/huesoporro.git
- https://gitlab.com/api/v4/projects/64552889/packages/helm/release

174
scripts/source-repos.sh Executable file
View file

@ -0,0 +1,174 @@
#!/usr/bin/env bash
set -euo pipefail
PROJECTS_DIR="k8s/services/argo"
APPS_DIR="k8s/argo-apps"
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
List and manage source repos in ArgoCD projects.
OPTIONS:
-l, --list List all source repos (default)
-u, --unused List unused source repos
-d, --delete-unused Delete unused source repos from projects
-h, --help Show this help message
Examples:
$(basename "$0") # List all source repos
$(basename "$0") -u # List unused source repos
$(basename "$0") -d # Delete unused source repos
EOF
}
get_all_project_repos() {
awk '/^[[:space:]]*sourceRepos:/,/^[[:space:]]*[a-z]+:/' "$PROJECTS_DIR"/project-*.yaml | \
grep -E '^[[:space:]]+-[[:space:]]+' | \
sed 's/^[[:space:]]*- //' | \
sort -u
}
get_all_app_repos() {
grep -E '(^\s+-\s+repoURL:|^\s+repoURL:)' "$APPS_DIR"/*.yaml 2>/dev/null | \
sed 's/.*repoURL: *//' | \
sort -u
}
get_project_name_from_repo() {
local repo="$1"
for project_file in "$PROJECTS_DIR"/project-*.yaml; do
local project_name
project_name=$(basename "$project_file" .yaml | sed 's/^project-//')
if awk '/^[[:space:]]*sourceRepos:/,/^[[:space:]]*[a-z]+:/' "$project_file" | \
grep -qE "^[[:space:]]+- ${repo//\./\\.}$"; then
echo "$project_name"
return
fi
done
}
list_all() {
echo "=== All Source Repos in ArgoCD Projects ==="
echo ""
while IFS= read -r repo; do
local project
project=$(get_project_name_from_repo "$repo")
echo "[$project] $repo"
done < <(get_all_project_repos)
}
list_unused() {
local all_repos
local all_app_repos
all_repos=$(get_all_project_repos)
all_app_repos=$(get_all_app_repos)
echo "=== Unused Source Repos ==="
echo ""
local found_unused=false
while IFS= read -r repo; do
if ! echo "$all_app_repos" | grep -qF -- "$repo"; then
found_unused=true
local project
project=$(get_project_name_from_repo "$repo")
echo "[$project] $repo"
fi
done < <(echo "$all_repos")
if [ "$found_unused" = false ]; then
echo "No unused source repos found."
fi
}
delete_unused() {
local all_repos
local all_app_repos
all_repos=$(get_all_project_repos)
all_app_repos=$(get_all_app_repos)
local found_unused=false
local to_delete=()
while IFS= read -r repo; do
if ! echo "$all_app_repos" | grep -qF -- "$repo"; then
found_unused=true
to_delete+=("$repo")
fi
done < <(echo "$all_repos")
if [ "$found_unused" = false ]; then
echo "No unused source repos found."
return
fi
echo "The following source repos will be deleted:"
echo ""
for repo in "${to_delete[@]}"; do
local project
project=$(get_project_name_from_repo "$repo")
echo "[$project] $repo"
done
echo ""
read -p "Are you sure? [y/N] " -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
return
fi
for repo in "${to_delete[@]}"; do
for project_file in "$PROJECTS_DIR"/project-*.yaml; do
if grep -q "sourceRepos:" "$project_file"; then
local escaped_repo
escaped_repo="${repo//\./\\.}"
if grep -qE "^[[:space:]]+- $escaped_repo$" "$project_file"; then
sed -i "s|^[[:space:]]*- $escaped_repo$||" "$project_file"
sed -i '/^[[:space:]]*-$/d' "$project_file"
sed -i '/^[[:space:]]*$/d' "$project_file"
echo "Deleted '$repo' from $(basename "$project_file")"
fi
fi
done
done
}
COMMAND="list_all"
while [[ $# -gt 0 ]]; do
case "$1" in
-l|--list)
COMMAND="list_all"
;;
-u|--unused)
COMMAND="list_unused"
;;
-d|--delete-unused)
COMMAND="delete_unused"
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
shift
done
case "$COMMAND" in
list_all)
list_all
;;
list_unused)
list_unused
;;
delete_unused)
delete_unused
;;
esac

View file

@ -4,6 +4,7 @@ terraform {
bucket = "fuku-terraform"
key = "adguard/terraform"
region = "us-east-1"
use_lockfile = "true"
}
required_providers {

View file

@ -4,6 +4,7 @@ terraform {
bucket = "fuku-terraform"
key = "authentik/terraform"
region = "us-east-1"
use_lockfile = "true"
}
required_providers {
authentik = {
@ -101,21 +102,6 @@ module "portainer" {
sub_mode = "hashed_user_id"
}
module "paperless" {
source = "../modules/authentik-oidc"
app_name = "Paperless"
app_slug = "paperless"
client_id = var.paperless_client_id
client_secret = var.paperless_client_secret
app_access_group_id = ""
redirect_uris = [
{ matching_mode = "strict", url = "https://paperless.roboces.dev/accounts/oidc/authentik/login/callback/" }
]
app_icon = "https://paperless.roboces.dev/favicon.ico"
app_description = "Document manager"
app_publisher = "Paperless"
app_url = "https://paperless.roboces.dev"
}
module "sonarr" {
source = "../modules/authentik-proxy"
@ -193,28 +179,6 @@ module "jellyfin" {
}
module "ganymede" {
source = "../modules/authentik-oidc"
app_name = "Ganymede"
app_slug = "ganymede"
redirect_uris = [{ matching_mode = "strict", url = "https://vods.roboces.dev/api/v1/auth/oauth/callback" }]
client_id = var.ganymede_client_id
client_secret = var.ganymede_client_secret
app_url = "https://vods.roboces.dev"
app_icon = "https://vods.roboces.dev/favicon.ico"
app_access_group_id = authentik_group.vods.id
}
module "jellyseerr" {
source = "../modules/authentik-app"
app_name = "Solicitudes Jelly"
app_slug = "jellyseer"
app_url = "https://requests.roboces.dev"
app_icon = "https://requests.roboces.dev/os_icon.svg"
app_description = "Solicita series, animes y pelis para ser añadidas automáticamente a Jellyfin"
app_access_group_id = authentik_group.arrs.id
}
module "cloud" {
source = "../modules/authentik-oidc"
app_name = "Cloud"

View file

@ -12,5 +12,5 @@ TF_VAR_rustical_client_id=
TF_VAR_rustical_client_secret=
TF_VAR_ganymede_client_id=
TF_VAR_ganymede_client_secret=
TF_VAR_oxicloud_client_id=aef61f77326b813cf8d8ba71d1ac994b5642685ca37e4710ab0079e91d87702d55fd9775d473b05aff45603bf08e78dba26850af3a815f3c3ac171d163368aa0
TF_VAR_oxicloud_client_secret=a4038df17c9fd06f86372aeaaae8f3fd1374d8978983af7b398d948ef15d1efe522a1faa2fc7652bc410c516d96cd2e4211dad4e05ba6297bdd8d9090460d5fc
TF_VAR_oxicloud_client_id=
TF_VAR_oxicloud_client_secret=