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:
parent
d0f40c0967
commit
1fd2bfef3d
71 changed files with 2150 additions and 965 deletions
|
|
@ -51,6 +51,10 @@ spec:
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: pvc-authentik-media
|
claimName: pvc-authentik-media
|
||||||
server:
|
server:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 1Gi
|
||||||
autoscaling:
|
autoscaling:
|
||||||
enabled: true
|
enabled: true
|
||||||
minReplicas: 1
|
minReplicas: 1
|
||||||
|
|
@ -79,6 +83,9 @@ spec:
|
||||||
tls: []
|
tls: []
|
||||||
https: true
|
https: true
|
||||||
worker:
|
worker:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 512Mi
|
||||||
autoscaling:
|
autoscaling:
|
||||||
enabled: true
|
enabled: true
|
||||||
minReplicas: 2
|
minReplicas: 2
|
||||||
|
|
@ -92,4 +99,6 @@ spec:
|
||||||
targetRevision: main
|
targetRevision: main
|
||||||
project: fuku
|
project: fuku
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
automated: {}
|
automated:
|
||||||
|
prune: true
|
||||||
|
selfHeal: true
|
||||||
|
|
|
||||||
|
|
@ -12,38 +12,40 @@ spec:
|
||||||
sources:
|
sources:
|
||||||
- repoURL: git.roboces.dev/catalin/fukuops
|
- repoURL: git.roboces.dev/catalin/fukuops
|
||||||
chart: dokuwiki
|
chart: dokuwiki
|
||||||
targetRevision: 0.1.2
|
targetRevision: 1.0.2
|
||||||
helm:
|
helm:
|
||||||
valuesObject:
|
valuesObject:
|
||||||
replicaCount: 1
|
dokuwiki:
|
||||||
securityContext:
|
replicaCount: 1
|
||||||
allowPrivilegeEscalation: false
|
securityContext:
|
||||||
readOnlyRootFilesystem: false
|
allowPrivilegeEscalation: false
|
||||||
runAsUser: 33
|
readOnlyRootFilesystem: false
|
||||||
runAsGroup: 33
|
runAsUser: 33
|
||||||
capabilities:
|
runAsGroup: 33
|
||||||
drop:
|
capabilities:
|
||||||
- all
|
drop:
|
||||||
service:
|
- all
|
||||||
type: LoadBalancer
|
service:
|
||||||
port: 8004
|
type: LoadBalancer
|
||||||
ingress:
|
port: 8004
|
||||||
enabled: true
|
ingress:
|
||||||
className: "traefik"
|
enabled: true
|
||||||
hosts:
|
className: "traefik"
|
||||||
- host: wiki.roboces.dev
|
hosts:
|
||||||
paths:
|
- host: wiki.roboces.dev
|
||||||
- path: /
|
paths:
|
||||||
pathType: Prefix
|
- path: /
|
||||||
persistence:
|
pathType: Prefix
|
||||||
enabled: true
|
persistence:
|
||||||
storageClass: "truenas-nfs-csi"
|
enabled: true
|
||||||
accessMode: ReadWriteMany
|
storageClass: "truenas-nfs-csi"
|
||||||
size: 20Gi
|
accessMode: ReadWriteMany
|
||||||
env:
|
size: 20Gi
|
||||||
PHP_TIMEZONE: Europe/Madrid
|
config:
|
||||||
PHP_MEMORYLIMIT: 512M
|
php:
|
||||||
PHP_UPLOADLIMIT: 128M
|
timezone: Europe/Madrid
|
||||||
|
memoryLimit: 512M
|
||||||
|
uploadLimit: 128M
|
||||||
project: roboces
|
project: roboces
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
automated:
|
automated:
|
||||||
|
|
|
||||||
|
|
@ -101,4 +101,6 @@ spec:
|
||||||
enabled: false
|
enabled: false
|
||||||
project: roboces
|
project: roboces
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
automated: {}
|
automated:
|
||||||
|
prune: true
|
||||||
|
selfHeal: true
|
||||||
|
|
|
||||||
|
|
@ -12,45 +12,51 @@ spec:
|
||||||
sources:
|
sources:
|
||||||
- repoURL: git.roboces.dev/catalin/fukuops
|
- repoURL: git.roboces.dev/catalin/fukuops
|
||||||
chart: helm-rustical
|
chart: helm-rustical
|
||||||
targetRevision: 0.3.0
|
targetRevision: 1.1.1
|
||||||
helm:
|
helm:
|
||||||
valuesObject:
|
valuesObject:
|
||||||
debug:
|
rustical:
|
||||||
enabled: false
|
debug:
|
||||||
replicaCount: 1
|
enabled: false
|
||||||
service:
|
autoscaling:
|
||||||
type: LoadBalancer
|
enabled: true
|
||||||
port: 8001
|
minReplicas: 1
|
||||||
targetPort: 4000
|
maxReplicas: 3
|
||||||
ingress:
|
targetCPUUtilizationPercentage: 80
|
||||||
enabled: true
|
targetMemoryUtilizationPercentage: 80
|
||||||
className: "traefik"
|
service:
|
||||||
hosts:
|
type: LoadBalancer
|
||||||
- host: cal.roboces.dev
|
port: 8001
|
||||||
paths:
|
targetPort: 4000
|
||||||
- path: /
|
ingress:
|
||||||
pathType: Prefix
|
enabled: true
|
||||||
env:
|
className: "traefik"
|
||||||
RUSTICAL_OIDC__NAME: Authentik
|
hosts:
|
||||||
RUSTICAL_OIDC__ISSUER: https://auth.fukurokuju.dev/application/o/rustical/
|
- host: cal.roboces.dev
|
||||||
RUSTICAL_OIDC__CLAIM_USERID: preferred_username
|
paths:
|
||||||
RUSTICAL_OIDC__SCOPES: '["openid", "profile", "groups"]'
|
- path: /
|
||||||
RUSTICAL_OIDC__ALLOW_SIGN_UP: "true"
|
pathType: Prefix
|
||||||
RUSTICAL_FRONTEND__ALLOW_PASSWORD_LOGIN: false
|
config:
|
||||||
persistence:
|
oidc.name: Authentik
|
||||||
enabled: true
|
oidc.issuer: https://auth.fukurokuju.dev/application/o/rustical/
|
||||||
storageClass: "truenas-nfs-csi"
|
oidc.claim_userid: preferred_username
|
||||||
accessMode: ReadWriteMany
|
oidc.scopes: '["openid", "profile", "groups"]'
|
||||||
size: 50Gi
|
oidc.allow_sign_up: "true"
|
||||||
secret:
|
frontend.allow_password_login: false
|
||||||
existingSecretName: rustical
|
persistence:
|
||||||
resources:
|
enabled: true
|
||||||
requests:
|
storageClass: "truenas-nfs-csi"
|
||||||
cpu: 250m
|
accessMode: ReadWriteMany
|
||||||
memory: 256Mi
|
size: 50Gi
|
||||||
limits:
|
secret:
|
||||||
cpu: 500m
|
existingSecretName: rustical
|
||||||
memory: 512Mi
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 250m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
project: roboces
|
project: roboces
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
automated:
|
automated:
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,9 @@ spec:
|
||||||
sources:
|
sources:
|
||||||
- repoURL: git.roboces.dev/catalin/fukuops
|
- repoURL: git.roboces.dev/catalin/fukuops
|
||||||
chart: immich
|
chart: immich
|
||||||
targetRevision: 0.2.3
|
targetRevision: 1.0.4
|
||||||
helm:
|
helm:
|
||||||
valuesObject:
|
valuesObject:
|
||||||
machine-learning:
|
|
||||||
enabled: true
|
|
||||||
service:
|
|
||||||
type: LoadBalancer
|
|
||||||
immich:
|
immich:
|
||||||
autoscaling:
|
autoscaling:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
@ -32,16 +28,17 @@ spec:
|
||||||
- all
|
- all
|
||||||
service:
|
service:
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
db:
|
config:
|
||||||
hostname: "192.168.1.3"
|
database:
|
||||||
port: 5432
|
hostname: "192.168.1.3"
|
||||||
database_name: "immich"
|
port: 5432
|
||||||
redis:
|
name: "immich"
|
||||||
hostname: "192.168.1.3"
|
redis:
|
||||||
port: 30036
|
hostname: "192.168.1.3"
|
||||||
dbindex: "12"
|
port: 30036
|
||||||
general:
|
dbIndex: "12"
|
||||||
tz: "Europe/Madrid"
|
general:
|
||||||
|
timezone: Europe/Madrid
|
||||||
persistence:
|
persistence:
|
||||||
library:
|
library:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
@ -96,8 +93,13 @@ spec:
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
tls: []
|
tls: []
|
||||||
secret:
|
machineLearning:
|
||||||
existingSecretName: "immich-secrets"
|
enabled: true
|
||||||
|
service:
|
||||||
|
type: LoadBalancer
|
||||||
|
secret:
|
||||||
|
enabled: true
|
||||||
|
existingSecretName: "immich-secrets"
|
||||||
project: roboces
|
project: roboces
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
automated:
|
automated:
|
||||||
|
|
|
||||||
|
|
@ -12,72 +12,79 @@ spec:
|
||||||
sources:
|
sources:
|
||||||
- repoURL: git.roboces.dev/catalin/fukuops
|
- repoURL: git.roboces.dev/catalin/fukuops
|
||||||
chart: miniflux
|
chart: miniflux
|
||||||
targetRevision: 0.2.0
|
targetRevision: 1.0.3
|
||||||
helm:
|
helm:
|
||||||
valuesObject:
|
valuesObject:
|
||||||
replicaCount: 3
|
miniflux:
|
||||||
securityContext:
|
autoscaling:
|
||||||
allowPrivilegeEscalation: false
|
enabled: true
|
||||||
readOnlyRootFilesystem: true
|
securityContext:
|
||||||
runAsUser: 10000
|
allowPrivilegeEscalation: false
|
||||||
runAsGroup: 10000
|
readOnlyRootFilesystem: true
|
||||||
capabilities:
|
runAsUser: 10000
|
||||||
drop:
|
runAsGroup: 10000
|
||||||
- all
|
capabilities:
|
||||||
service:
|
drop:
|
||||||
type: LoadBalancer
|
- all
|
||||||
ingress:
|
service:
|
||||||
enabled: true
|
type: LoadBalancer
|
||||||
className: "traefik"
|
ingress:
|
||||||
hosts:
|
enabled: true
|
||||||
- host: feeds.roboces.dev
|
className: "traefik"
|
||||||
paths:
|
hosts:
|
||||||
- path: /
|
- host: feeds.roboces.dev
|
||||||
pathType: Prefix
|
paths:
|
||||||
resources:
|
- path: /
|
||||||
requests:
|
pathType: Prefix
|
||||||
cpu: 300m
|
resources:
|
||||||
memory: 300Mi
|
requests:
|
||||||
ephemeral-storage: 2Gi
|
cpu: 500m
|
||||||
limits:
|
memory: 512Mi
|
||||||
cpu: 400m
|
ephemeral-storage: 2Gi
|
||||||
memory: 500Mi
|
limits:
|
||||||
ephemeral-storage: 4Gi
|
cpu: 700m
|
||||||
livenessProbe:
|
memory: 1024Mi
|
||||||
tcpSocket:
|
ephemeral-storage: 4Gi
|
||||||
port: 8080
|
livenessProbe:
|
||||||
initialDelaySeconds: 5
|
tcpSocket:
|
||||||
timeoutSeconds: 15
|
port: 8080
|
||||||
successThreshold: 1
|
initialDelaySeconds: 5
|
||||||
failureThreshold: 3
|
timeoutSeconds: 15
|
||||||
periodSeconds: 10
|
successThreshold: 1
|
||||||
readinessProbe:
|
failureThreshold: 3
|
||||||
httpGet:
|
periodSeconds: 10
|
||||||
path: /
|
readinessProbe:
|
||||||
port: 8080
|
httpGet:
|
||||||
initialDelaySeconds: 15
|
path: /
|
||||||
timeoutSeconds: 2
|
port: 8080
|
||||||
successThreshold: 1
|
initialDelaySeconds: 15
|
||||||
failureThreshold: 3
|
timeoutSeconds: 2
|
||||||
periodSeconds: 10
|
successThreshold: 1
|
||||||
podDisruptionBudget:
|
failureThreshold: 3
|
||||||
enabled: true
|
periodSeconds: 10
|
||||||
maxUnavailable: 1
|
podDisruptionBudget:
|
||||||
env:
|
enabled: true
|
||||||
RUN_MIGRATIONS: "1"
|
maxUnavailable: 1
|
||||||
CREATE_ADMIN: "1"
|
config:
|
||||||
OAUTH2_PROVIDER: oidc
|
general:
|
||||||
OAUTH2_REDIRECT_URL: https://feeds.roboces.dev/oauth2/oidc/callback
|
runMigrations: true
|
||||||
OAUTH2_OIDC_DISCOVERY_ENDPOINT: https://auth.fukurokuju.dev/application/o/miniflux/
|
createAdmin: true
|
||||||
OAUTH2_USER_CREATION: "1"
|
workerPoolSize: 1
|
||||||
FETCH_YOUTUBE_WATCH_TIME: "1"
|
pollingFrequency: 120
|
||||||
WORKER_POOL_SIZE: "1"
|
batchSize: 25
|
||||||
POLLING_FREQUENCY: "120"
|
oauth2:
|
||||||
BATCH_SIZE: "25"
|
enabled: true
|
||||||
METRICS_COLLECTOR: "1"
|
provider: oidc
|
||||||
METRICS_ALLOWED_NETWORKS: 10.42.1.0/16
|
redirectUrl: https://feeds.roboces.dev/oauth2/oidc/callback
|
||||||
secret:
|
oidcDiscoveryEndpoint: https://auth.fukurokuju.dev/application/o/miniflux/
|
||||||
existingSecretName: miniflux
|
userCreation: true
|
||||||
|
metrics:
|
||||||
|
enabled: true
|
||||||
|
allowedNetworks: 10.42.1.0/16
|
||||||
|
youtube:
|
||||||
|
fetchWatchTime: true
|
||||||
|
secret:
|
||||||
|
existingSecretName: miniflux
|
||||||
project: roboces
|
project: roboces
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
automated:
|
automated:
|
||||||
|
|
|
||||||
|
|
@ -12,39 +12,43 @@ spec:
|
||||||
sources:
|
sources:
|
||||||
- repoURL: git.roboces.dev/catalin/fukuops
|
- repoURL: git.roboces.dev/catalin/fukuops
|
||||||
chart: oxicloud
|
chart: oxicloud
|
||||||
targetRevision: 0.1.0
|
targetRevision: 1.0.1
|
||||||
helm:
|
helm:
|
||||||
valuesObject:
|
valuesObject:
|
||||||
persistence:
|
oxicloud:
|
||||||
enabled: true
|
persistence:
|
||||||
storageClass: "truenas-nfs-csi"
|
enabled: true
|
||||||
accessMode: ReadWriteMany
|
storageClass: "truenas-nfs-csi"
|
||||||
size: 50Gi
|
accessMode: ReadWriteMany
|
||||||
service:
|
size: 50Gi
|
||||||
type: LoadBalancer
|
service:
|
||||||
config:
|
type: LoadBalancer
|
||||||
server:
|
config:
|
||||||
port: 8086
|
server:
|
||||||
host: "0.0.0.0"
|
port: 8086
|
||||||
baseUrl: "https://cloud.roboces.dev"
|
host: "0.0.0.0"
|
||||||
features:
|
baseUrl: "https://cloud.roboces.dev"
|
||||||
enableAuth: "true"
|
features:
|
||||||
enableSharing: "true"
|
enableAuth: true
|
||||||
mimalloc:
|
enableSharing: true
|
||||||
purgeDelay: "0"
|
mimalloc:
|
||||||
allowLargeOsPages: "0"
|
purgeDelay: "0"
|
||||||
secrets:
|
allowLargeOsPages: "0"
|
||||||
existingSecret: oxicloud
|
secret:
|
||||||
wopi:
|
existingSecretName: oxicloud
|
||||||
enabled: false
|
wopi:
|
||||||
ingress:
|
enabled: false
|
||||||
className: "traefik"
|
ingress:
|
||||||
hosts:
|
enabled: true
|
||||||
- host: cloud.roboces.dev
|
className: "traefik"
|
||||||
paths:
|
hosts:
|
||||||
- path: /
|
- host: cloud.roboces.dev
|
||||||
pathType: ImplementationSpecific
|
paths:
|
||||||
tls: []
|
- path: /
|
||||||
|
pathType: ImplementationSpecific
|
||||||
|
tls: []
|
||||||
project: roboces
|
project: roboces
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
automated: {}
|
automated:
|
||||||
|
prune: true
|
||||||
|
selfHeal: true
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ spec:
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
persistence:
|
persistence:
|
||||||
storageClass: truenas-nfs-csi
|
storageClass: truenas-nfs-csi
|
||||||
|
accessMode: ReadWriteMany
|
||||||
dashboard:
|
dashboard:
|
||||||
enabled: true
|
enabled: true
|
||||||
service:
|
service:
|
||||||
|
|
|
||||||
|
|
@ -12,51 +12,51 @@ spec:
|
||||||
sources:
|
sources:
|
||||||
- repoURL: git.roboces.dev/catalin/fukuops
|
- repoURL: git.roboces.dev/catalin/fukuops
|
||||||
chart: vaultwarden
|
chart: vaultwarden
|
||||||
targetRevision: 0.1.1
|
targetRevision: 1.0.2
|
||||||
helm:
|
helm:
|
||||||
valuesObject:
|
valuesObject:
|
||||||
conf:
|
vaultwarden:
|
||||||
db:
|
config:
|
||||||
enable_db_wal: true
|
database:
|
||||||
connection_retries: 15
|
enableWal: true
|
||||||
timeout: 30
|
connectionRetries: 15
|
||||||
web_vault:
|
timeout: 30
|
||||||
|
webVault:
|
||||||
|
enabled: true
|
||||||
|
folder: "web-vault/"
|
||||||
|
replicaCount: 1
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
readOnlyRootFilesystem: false
|
||||||
|
runAsUser: 10000
|
||||||
|
runAsGroup: 10000
|
||||||
|
capabilities:
|
||||||
|
drop:
|
||||||
|
- all
|
||||||
|
service:
|
||||||
|
type: LoadBalancer
|
||||||
|
ingress:
|
||||||
enabled: true
|
enabled: true
|
||||||
folder: "web-vault/"
|
className: "traefik"
|
||||||
|
hosts:
|
||||||
replicaCount: 1
|
- host: vault.roboces.dev
|
||||||
securityContext:
|
paths:
|
||||||
allowPrivilegeEscalation: false
|
- path: /
|
||||||
readOnlyRootFilesystem: false
|
pathType: Prefix
|
||||||
runAsUser: 10000
|
resources:
|
||||||
runAsGroup: 10000
|
requests:
|
||||||
capabilities:
|
cpu: 250m
|
||||||
drop:
|
memory: 256Mi
|
||||||
- all
|
limits:
|
||||||
service:
|
cpu: 500m
|
||||||
type: LoadBalancer
|
memory: 512Mi
|
||||||
ingress:
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
className: "traefik"
|
storageClass: "truenas-nfs-csi"
|
||||||
hosts:
|
accessMode: ReadWriteMany
|
||||||
- host: vault.roboces.dev
|
size: 5Gi
|
||||||
paths:
|
secret:
|
||||||
- path: /
|
existingSecretName: vaultwarden-secrets
|
||||||
pathType: Prefix
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 250m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 512Mi
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
storageClass: "truenas-nfs-csi"
|
|
||||||
accessMode: ReadWriteMany
|
|
||||||
size: 5Gi
|
|
||||||
secret:
|
|
||||||
existingSecretName: vaultwarden-secrets
|
|
||||||
project: roboces
|
project: roboces
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
automated:
|
automated:
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,9 @@ apiVersion: v2
|
||||||
name: chart
|
name: chart
|
||||||
description: A Helm chart template with standardized structure
|
description: A Helm chart template with standardized structure
|
||||||
type: application
|
type: application
|
||||||
version: 0.1.0
|
version: 1.0.2
|
||||||
appVersion: "latest"
|
appVersion: latest
|
||||||
annotations:
|
annotations:
|
||||||
artifacthub.io/images: |
|
artifacthub.io/images: "- name: chart\n image: nginx:latest\n"
|
||||||
- name: chart
|
artifacthub.io/changes: "- kind: added\n description: Initial chart template\n"
|
||||||
image: nginx:latest
|
artifacthub.io/maintainers: "- name: catalin\n email: catalin@example.com\n"
|
||||||
artifacthub.io/changes: |
|
|
||||||
- kind: added
|
|
||||||
description: Initial chart template
|
|
||||||
artifacthub.io/maintainers: |
|
|
||||||
- name: catalin
|
|
||||||
email: catalin@example.com
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ A Helm chart template with standardized structure for self-hosted services.
|
||||||
## TL;DR
|
## TL;DR
|
||||||
|
|
||||||
```bash
|
```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
|
## Prerequisites
|
||||||
|
|
@ -17,64 +17,74 @@ helm install chart oci://git.roboces.dev/catalin/fukuops/chart --version 1.0.0 -
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Basic Configuration
|
All values are nested under the `chart:` key. Example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
service:
|
chart:
|
||||||
type: LoadBalancer
|
replicaCount: 1
|
||||||
port: 80
|
service:
|
||||||
ingress:
|
type: LoadBalancer
|
||||||
enabled: true
|
port: 80
|
||||||
className: "traefik"
|
ingress:
|
||||||
hosts:
|
enabled: true
|
||||||
- host: chart.example.com
|
className: "traefik"
|
||||||
paths:
|
hosts:
|
||||||
- path: /
|
- host: chart.example.com
|
||||||
pathType: Prefix
|
paths:
|
||||||
```
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
### With Persistence
|
config:
|
||||||
|
# Key-value pairs that will be mapped to env vars (uppercased)
|
||||||
```yaml
|
# Example: `FOO: "bar"` becomes env var FOO=bar
|
||||||
persistence:
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
storageClass: "truenas-nfs-csi"
|
storageClass: "truenas-nfs-csi"
|
||||||
accessMode: ReadWriteMany
|
accessMode: ReadWriteMany
|
||||||
size: 10Gi
|
size: 10Gi
|
||||||
```
|
secret:
|
||||||
|
existingSecretName: chart-secrets
|
||||||
### With Secrets
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
secret:
|
|
||||||
existingSecretName: chart-secrets
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Values Reference
|
## Values Reference
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
| Key | Type | Default | Description |
|
||||||
|-----|------|---------|-------------|
|
|-----|------|---------|-------------|
|
||||||
| `replicaCount` | int | `1` | Number of replicas |
|
| `chart.replicaCount` | int | `1` | Number of replicas |
|
||||||
| `image.repository` | string | `nginx` | Image repository |
|
| `chart.image.repository` | string | `nginx` | Image repository |
|
||||||
| `image.pullPolicy` | string | `IfNotPresent` | Image pull policy |
|
| `chart.image.pullPolicy` | string | `IfNotPresent` | Image pull policy |
|
||||||
| `image.tag` | string | `.Chart.AppVersion` | Image tag |
|
| `chart.image.tag` | string | `.Chart.AppVersion` | Image tag |
|
||||||
| `service.type` | string | `LoadBalancer` | Service type |
|
| `chart.service.type` | string | `LoadBalancer` | Service type |
|
||||||
| `service.port` | int | `80` | Service port |
|
| `chart.service.port` | int | `80` | Service port |
|
||||||
| `service.targetPort` | int | `80` | Container port |
|
| `chart.service.targetPort` | int | `80` | Container port |
|
||||||
| `ingress.enabled` | bool | `false` | Enable ingress |
|
| `chart.ingress.enabled` | bool | `false` | Enable ingress |
|
||||||
| `ingress.className` | string | `traefik` | Ingress class |
|
| `chart.ingress.className` | string | `traefik` | Ingress class |
|
||||||
| `persistence.enabled` | bool | `false` | Enable persistence |
|
| `chart.config` | object | `{}` | Config key-value pairs mapped to env vars |
|
||||||
| `persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
|
| `chart.persistence.enabled` | bool | `false` | Enable persistence |
|
||||||
| `persistence.accessMode` | string | `ReadWriteMany` | Access mode |
|
| `chart.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
|
||||||
| `persistence.size` | string | `1Gi` | PVC size |
|
| `chart.persistence.accessMode` | string | `ReadWriteMany` | Access mode |
|
||||||
| `persistence.name` | string | `chart-data` | PVC name |
|
| `chart.persistence.size` | string | `1Gi` | PVC size |
|
||||||
| `secret.existingSecretName` | string | `""` | Existing secret name |
|
| `chart.persistence.name` | string | `chart-data` | PVC name |
|
||||||
| `resources` | object | `{}` | Container resources |
|
| `chart.secret.existingSecretName` | string | `""` | Existing secret name |
|
||||||
| `livenessProbe` | object | (see values.yaml) | Liveness probe |
|
| `chart.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
|
||||||
| `readinessProbe` | object | (see values.yaml) | Readiness probe |
|
| `chart.resources` | object | `{}` | Container resources |
|
||||||
| `nodeSelector` | object | `{}` | Node selector |
|
| `chart.livenessProbe` | object | (see values.yaml) | Liveness probe |
|
||||||
| `tolerations` | array | `[]` | Tolerations |
|
| `chart.readinessProbe` | object | (see values.yaml) | Readiness probe |
|
||||||
| `affinity` | object | `{}` | Affinity rules |
|
| `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
|
## Persistence
|
||||||
|
|
||||||
|
|
@ -82,10 +92,28 @@ When persistence is enabled, a PVC named `chart-data` is created. The PVC uses t
|
||||||
|
|
||||||
## Secret Management
|
## 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
|
## 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
|
### Pod not starting
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{{- if .Values.config }}
|
{{- if .Values.chart.config }}
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -6,7 +6,8 @@ metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "labels" . | nindent 4 }}
|
{{- include "labels" . | nindent 4 }}
|
||||||
data:
|
data:
|
||||||
{{- range $key, $value := .Values.config }}
|
{{- range $key, $value := .Values.chart.config }}
|
||||||
{{ $key }}: {{ $value | quote }}
|
{{- $envKey := $key | upper }}
|
||||||
|
{{ $envKey }}: {{ $value | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "labels" . | nindent 4 }}
|
{{- include "labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
replicas: {{ .Values.replicaCount }}
|
replicas: {{ .Values.chart.replicaCount }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
{{- include "selectorLabels" . | nindent 6 }}
|
{{- include "selectorLabels" . | nindent 6 }}
|
||||||
|
|
@ -14,55 +14,90 @@ spec:
|
||||||
labels:
|
labels:
|
||||||
{{- include "selectorLabels" . | nindent 8 }}
|
{{- include "selectorLabels" . | nindent 8 }}
|
||||||
spec:
|
spec:
|
||||||
containers:
|
{{- with .Values.chart.podSecurityContext }}
|
||||||
- name: {{ .Chart.Name }}
|
securityContext:
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
{{- toYaml . | nindent 8 }}
|
||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
{{- end }}
|
||||||
ports:
|
{{- if .Values.chart.debug.enabled }}
|
||||||
- name: http
|
initContainers:
|
||||||
containerPort: {{ .Values.service.targetPort }}
|
- name: debug
|
||||||
protocol: TCP
|
image: alpine:3.19
|
||||||
{{- if .Values.config }}
|
command: ["sleep", "infinity"]
|
||||||
envFrom:
|
envFrom:
|
||||||
|
{{- if .Values.chart.config }}
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: {{ include "fullname" . }}-config
|
name: {{ include "fullname" . }}-config
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.secret.existingSecretName }}
|
{{- if .Values.chart.secret.existingSecretName }}
|
||||||
envFrom:
|
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: {{ .Values.secret.existingSecretName }}
|
name: {{ .Values.chart.secret.existingSecretName }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.persistence.enabled }}
|
{{- if .Values.chart.persistence.enabled }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: {{ include "pvcName" . }}
|
- name: {{ include "pvcName" . }}
|
||||||
mountPath: /data
|
mountPath: /data
|
||||||
{{- end }}
|
{{- 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:
|
resources:
|
||||||
{{- toYaml . | nindent 12 }}
|
{{- toYaml . | nindent 12 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.livenessProbe }}
|
{{- if not .Values.chart.debug.enabled }}
|
||||||
|
{{- with .Values.chart.livenessProbe }}
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
{{- toYaml . | nindent 12 }}
|
{{- toYaml . | nindent 12 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.readinessProbe }}
|
{{- with .Values.chart.readinessProbe }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
{{- toYaml . | nindent 12 }}
|
{{- toYaml . | nindent 12 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if not .Values.persistence.enabled }}
|
{{- end }}
|
||||||
|
{{- if not .Values.chart.persistence.enabled }}
|
||||||
volumes:
|
volumes:
|
||||||
- name: {{ include "pvcName" . }}
|
- name: {{ include "pvcName" . }}
|
||||||
emptyDir: {}
|
emptyDir: {}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.chart.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.tolerations }}
|
{{- with .Values.chart.tolerations }}
|
||||||
tolerations:
|
tolerations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.chart.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
{{- if .Values.ingress.enabled }}
|
{{- if .Values.chart.ingress.enabled }}
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "fullname" . }}
|
name: {{ include "fullname" . }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "labels" . | nindent 4 }}
|
{{- include "labels" . | nindent 4 }}
|
||||||
{{- with .Values.ingress.annotations }}
|
{{- with .Values.chart.ingress.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
{{- if .Values.ingress.className }}
|
{{- if .Values.chart.ingress.className }}
|
||||||
ingressClassName: {{ .Values.ingress.className }}
|
ingressClassName: {{ .Values.chart.ingress.className }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.ingress.tls }}
|
{{- if .Values.chart.ingress.tls }}
|
||||||
tls:
|
tls:
|
||||||
{{- range .Values.ingress.tls }}
|
{{- range .Values.chart.ingress.tls }}
|
||||||
- hosts:
|
- hosts:
|
||||||
{{- range .hosts }}
|
{{- range .hosts }}
|
||||||
- {{ . | quote }}
|
- {{ . | quote }}
|
||||||
|
|
@ -24,7 +24,7 @@ spec:
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
{{- range .Values.ingress.hosts }}
|
{{- range .Values.chart.ingress.hosts }}
|
||||||
- host: {{ .host | quote }}
|
- host: {{ .host | quote }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
|
|
@ -35,7 +35,7 @@ spec:
|
||||||
service:
|
service:
|
||||||
name: {{ include "fullname" $ }}
|
name: {{ include "fullname" $ }}
|
||||||
port:
|
port:
|
||||||
number: {{ $.Values.service.port }}
|
number: {{ $.Values.chart.service.port }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
18
k8s/charts/_template/templates/pdb.yaml
Normal file
18
k8s/charts/_template/templates/pdb.yaml
Normal 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 }}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{{- if .Values.persistence.enabled }}
|
{{- if .Values.chart.persistence.enabled }}
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: PersistentVolumeClaim
|
kind: PersistentVolumeClaim
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -7,11 +7,11 @@ metadata:
|
||||||
{{- include "labels" . | nindent 4 }}
|
{{- include "labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
accessModes:
|
accessModes:
|
||||||
- {{ .Values.persistence.accessMode | default "ReadWriteMany" }}
|
- {{ .Values.chart.persistence.accessMode | default "ReadWriteMany" }}
|
||||||
{{- if .Values.persistence.storageClass }}
|
{{- if .Values.chart.persistence.storageClass }}
|
||||||
storageClassName: {{ .Values.persistence.storageClass }}
|
storageClassName: {{ .Values.chart.persistence.storageClass }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.persistence.size | default "1Gi" }}
|
storage: {{ .Values.chart.persistence.size | default "1Gi" }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -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
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -7,7 +7,7 @@ metadata:
|
||||||
{{- include "labels" . | nindent 4 }}
|
{{- include "labels" . | nindent 4 }}
|
||||||
type: Opaque
|
type: Opaque
|
||||||
data:
|
data:
|
||||||
{{- range $key, $value := .Values.secret.data }}
|
{{- range $key, $value := .Values.chart.secret.data }}
|
||||||
{{ $key }}: {{ $value | b64enc | quote }}
|
{{ $key }}: {{ $value | b64enc | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "labels" . | nindent 4 }}
|
{{- include "labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.service.type }}
|
type: {{ .Values.chart.service.type }}
|
||||||
ports:
|
ports:
|
||||||
- port: {{ .Values.service.port }}
|
- port: {{ .Values.chart.service.port }}
|
||||||
targetPort: http
|
targetPort: http
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: http
|
name: http
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,71 @@
|
||||||
replicaCount: 1
|
chart:
|
||||||
|
replicaCount: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: nginx
|
repository: nginx
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
tag: ""
|
tag: ""
|
||||||
|
|
||||||
service:
|
service:
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
port: 80
|
|
||||||
targetPort: 80
|
|
||||||
|
|
||||||
ingress:
|
|
||||||
enabled: false
|
|
||||||
className: "traefik"
|
|
||||||
annotations: {}
|
|
||||||
hosts:
|
|
||||||
- host: chart.example.com
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
tls: []
|
|
||||||
|
|
||||||
config: {}
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
enabled: false
|
|
||||||
storageClass: "truenas-nfs-csi"
|
|
||||||
accessMode: ReadWriteMany
|
|
||||||
size: 1Gi
|
|
||||||
name: "chart-data"
|
|
||||||
|
|
||||||
resources: {}
|
|
||||||
|
|
||||||
livenessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 80
|
port: 80
|
||||||
initialDelaySeconds: 30
|
targetPort: 80
|
||||||
timeoutSeconds: 15
|
|
||||||
successThreshold: 1
|
|
||||||
failureThreshold: 3
|
|
||||||
periodSeconds: 10
|
|
||||||
|
|
||||||
readinessProbe:
|
ingress:
|
||||||
tcpSocket:
|
enabled: false
|
||||||
port: 80
|
className: "traefik"
|
||||||
initialDelaySeconds: 15
|
annotations: {}
|
||||||
timeoutSeconds: 2
|
hosts:
|
||||||
successThreshold: 1
|
- host: chart.example.com
|
||||||
failureThreshold: 3
|
paths:
|
||||||
periodSeconds: 10
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls: []
|
||||||
|
|
||||||
nodeSelector: {}
|
config: {}
|
||||||
tolerations: []
|
|
||||||
affinity: {}
|
|
||||||
|
|
||||||
secret:
|
persistence:
|
||||||
enabled: true
|
enabled: false
|
||||||
existingSecretName: ""
|
storageClass: "truenas-nfs-csi"
|
||||||
data: {}
|
accessMode: ReadWriteMany
|
||||||
|
size: 1Gi
|
||||||
|
name: "chart-data"
|
||||||
|
|
||||||
|
resources: {}
|
||||||
|
|
||||||
|
livenessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: 80
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 15
|
||||||
|
successThreshold: 1
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
|
readinessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: 80
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
timeoutSeconds: 2
|
||||||
|
successThreshold: 1
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
tolerations: []
|
||||||
|
affinity: {}
|
||||||
|
|
||||||
|
secret:
|
||||||
|
enabled: true
|
||||||
|
existingSecretName: ""
|
||||||
|
data: {}
|
||||||
|
|
||||||
|
podSecurityContext: {}
|
||||||
|
securityContext: {}
|
||||||
|
|
||||||
|
podDisruptionBudget:
|
||||||
|
enabled: false
|
||||||
|
minAvailable: 1
|
||||||
|
|
||||||
|
debug:
|
||||||
|
enabled: false
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ apiVersion: v2
|
||||||
name: dokuwiki
|
name: dokuwiki
|
||||||
description: A Helm chart for DokuWiki
|
description: A Helm chart for DokuWiki
|
||||||
type: application
|
type: application
|
||||||
version: 0.1.0
|
version: 1.0.2
|
||||||
appVersion: 2024-02-06b
|
appVersion: 2024-02-06b
|
||||||
annotations:
|
annotations:
|
||||||
artifacthub.io/images: "- name: dokuwiki\n image: dokuwiki/dokuwiki:2024-02-06b\n"
|
artifacthub.io/images: "- name: dokuwiki\n image: dokuwiki/dokuwiki:2024-02-06b\n"
|
||||||
|
|
|
||||||
112
k8s/charts/dokuwiki/README.md
Normal file
112
k8s/charts/dokuwiki/README.md
Normal 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
|
||||||
|
```
|
||||||
10
k8s/charts/dokuwiki/templates/configmap.yaml
Normal file
10
k8s/charts/dokuwiki/templates/configmap.yaml
Normal 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 }}
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
{{- if .Values.ingress.enabled -}}
|
{{- if .Values.dokuwiki.ingress.enabled -}}
|
||||||
{{- $fullName := include "dokuwiki.fullname" . -}}
|
{{- $fullName := include "dokuwiki.fullname" . -}}
|
||||||
{{- $svcPort := .Values.service.port -}}
|
{{- $svcPort := .Values.dokuwiki.service.port -}}
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "dokuwiki.labels" . | nindent 4 }}
|
{{- include "dokuwiki.labels" . | nindent 4 }}
|
||||||
{{- with .Values.ingress.annotations }}
|
{{- with .Values.dokuwiki.ingress.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
{{- if .Values.ingress.className }}
|
{{- if .Values.dokuwiki.ingress.className }}
|
||||||
ingressClassName: {{ .Values.ingress.className }}
|
ingressClassName: {{ .Values.dokuwiki.ingress.className }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.ingress.tls }}
|
{{- if .Values.dokuwiki.ingress.tls }}
|
||||||
tls:
|
tls:
|
||||||
{{- range .Values.ingress.tls }}
|
{{- range .Values.dokuwiki.ingress.tls }}
|
||||||
- hosts:
|
- hosts:
|
||||||
{{- range .hosts }}
|
{{- range .hosts }}
|
||||||
- {{ . | quote }}
|
- {{ . | quote }}
|
||||||
|
|
@ -26,7 +26,7 @@ spec:
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
{{- range .Values.ingress.hosts }}
|
{{- range .Values.dokuwiki.ingress.hosts }}
|
||||||
- host: {{ .host | quote }}
|
- host: {{ .host | quote }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "dokuwiki.labels" . | nindent 4 }}
|
{{- include "dokuwiki.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.service.type }}
|
type: {{ .Values.dokuwiki.service.type }}
|
||||||
ports:
|
ports:
|
||||||
- port: {{ .Values.service.port }}
|
- port: {{ .Values.dokuwiki.service.port }}
|
||||||
targetPort: {{ .Values.service.targetPort }}
|
targetPort: {{ .Values.dokuwiki.service.targetPort }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: http
|
name: http
|
||||||
selector:
|
selector:
|
||||||
|
|
|
||||||
|
|
@ -6,86 +6,84 @@ metadata:
|
||||||
{{- include "dokuwiki.labels" . | nindent 4 }}
|
{{- include "dokuwiki.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
serviceName: {{ include "dokuwiki.fullname" . }}-headless
|
serviceName: {{ include "dokuwiki.fullname" . }}-headless
|
||||||
{{- if not .Values.autoscaling.enabled }}
|
{{- if not .Values.dokuwiki.autoscaling.enabled }}
|
||||||
replicas: {{ .Values.replicaCount }}
|
replicas: {{ .Values.dokuwiki.replicaCount }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
{{- include "dokuwiki.selectorLabels" . | nindent 6 }}
|
{{- include "dokuwiki.selectorLabels" . | nindent 6 }}
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
{{- with .Values.podAnnotations }}
|
{{- with .Values.dokuwiki.podAnnotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "dokuwiki.selectorLabels" . | nindent 8 }}
|
{{- include "dokuwiki.selectorLabels" . | nindent 8 }}
|
||||||
spec:
|
spec:
|
||||||
{{- with .Values.imagePullSecrets }}
|
{{- with .Values.dokuwiki.imagePullSecrets }}
|
||||||
imagePullSecrets:
|
imagePullSecrets:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
{{- toYaml .Values.dokuwiki.podSecurityContext | nindent 8 }}
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Chart.Name }}
|
- name: {{ .Chart.Name }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
{{- toYaml .Values.dokuwiki.securityContext | nindent 12 }}
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
image: "{{ .Values.dokuwiki.image.repository }}:{{ .Values.dokuwiki.image.tag | default .Chart.AppVersion }}"
|
||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
imagePullPolicy: {{ .Values.dokuwiki.image.pullPolicy }}
|
||||||
env:
|
envFrom:
|
||||||
{{- range $key, $value := .Values.env }}
|
- configMapRef:
|
||||||
- name: {{ $key }}
|
name: {{ include "dokuwiki.fullname" . }}-config
|
||||||
value: {{ $value | quote }}
|
|
||||||
{{- end }}
|
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 8080
|
containerPort: 8080
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
{{- toYaml .Values.dokuwiki.livenessProbe | nindent 12 }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
{{- toYaml .Values.dokuwiki.readinessProbe | nindent 12 }}
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
{{- toYaml .Values.dokuwiki.resources | nindent 12 }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /storage
|
mountPath: /storage
|
||||||
{{- if .Values.persistence.existingClaim }}
|
{{- if .Values.dokuwiki.persistence.existingClaim }}
|
||||||
volumes:
|
volumes:
|
||||||
- name: data
|
- name: data
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: {{ .Values.persistence.existingClaim }}
|
claimName: {{ .Values.dokuwiki.persistence.existingClaim }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.dokuwiki.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.dokuwiki.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.tolerations }}
|
{{- with .Values.dokuwiki.tolerations }}
|
||||||
tolerations:
|
tolerations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
restartPolicy: Always
|
restartPolicy: Always
|
||||||
automountServiceAccountToken: false
|
automountServiceAccountToken: false
|
||||||
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
|
{{- if and .Values.dokuwiki.persistence.enabled (not .Values.dokuwiki.persistence.existingClaim) }}
|
||||||
volumeClaimTemplates:
|
volumeClaimTemplates:
|
||||||
- metadata:
|
- metadata:
|
||||||
name: data
|
name: data
|
||||||
spec:
|
spec:
|
||||||
accessModes:
|
accessModes:
|
||||||
- {{ .Values.persistence.accessMode }}
|
- {{ .Values.dokuwiki.persistence.accessMode }}
|
||||||
{{- if .Values.persistence.storageClass }}
|
{{- if .Values.dokuwiki.persistence.storageClass }}
|
||||||
{{- if eq "-" .Values.persistence.storageClass }}
|
{{- if eq "-" .Values.dokuwiki.persistence.storageClass }}
|
||||||
storageClassName: ""
|
storageClassName: ""
|
||||||
{{- else }}
|
{{- else }}
|
||||||
storageClassName: {{ .Values.persistence.storageClass | quote }}
|
storageClassName: {{ .Values.dokuwiki.persistence.storageClass | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.persistence.size }}
|
storage: {{ .Values.dokuwiki.persistence.size }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,71 @@
|
||||||
replicaCount: 1
|
dokuwiki:
|
||||||
|
replicaCount: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: dokuwiki/dokuwiki
|
repository: dokuwiki/dokuwiki
|
||||||
pullpolicy: IfNotPresent
|
pullpolicy: IfNotPresent
|
||||||
tag: ""
|
tag: ""
|
||||||
|
|
||||||
imagePullSecrets: []
|
imagePullSecrets: []
|
||||||
podAnnotations: {}
|
podAnnotations: {}
|
||||||
podSecurityContext: {}
|
podSecurityContext: {}
|
||||||
securityContext: {}
|
securityContext: {}
|
||||||
|
|
||||||
service:
|
service:
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
port: 8004
|
port: 8004
|
||||||
targetPort: 8080
|
targetPort: 8080
|
||||||
|
|
||||||
ingress:
|
ingress:
|
||||||
enabled: true
|
enabled: true
|
||||||
className: ""
|
className: ""
|
||||||
annotations: {}
|
annotations: {}
|
||||||
hosts:
|
hosts:
|
||||||
- host: wiki.roboces.dev
|
- host: wiki.roboces.dev
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
tls: []
|
tls: []
|
||||||
|
|
||||||
persistence:
|
config:
|
||||||
enabled: true
|
php:
|
||||||
storageClass: "truenas-nfs-csi"
|
timezone: Europe/Madrid
|
||||||
accessMode: ReadWriteMany
|
memoryLimit: 512M
|
||||||
size: 2Gi
|
uploadLimit: 128M
|
||||||
existingClaim: ""
|
|
||||||
|
|
||||||
autoscaling:
|
persistence:
|
||||||
enabled: false
|
enabled: true
|
||||||
|
storageClass: "truenas-nfs-csi"
|
||||||
|
accessMode: ReadWriteMany
|
||||||
|
size: 2Gi
|
||||||
|
existingClaim: ""
|
||||||
|
|
||||||
resources: {}
|
autoscaling:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
livenessProbe:
|
resources: {}
|
||||||
tcpSocket:
|
|
||||||
port: 8080
|
|
||||||
initialDelaySeconds: 30
|
|
||||||
timeoutSeconds: 15
|
|
||||||
successThreshold: 1
|
|
||||||
failureThreshold: 3
|
|
||||||
periodSeconds: 10
|
|
||||||
|
|
||||||
readinessProbe:
|
livenessProbe:
|
||||||
tcpSocket:
|
tcpSocket:
|
||||||
port: 8080
|
port: 8080
|
||||||
initialDelaySeconds: 15
|
initialDelaySeconds: 30
|
||||||
timeoutSeconds: 2
|
timeoutSeconds: 15
|
||||||
successThreshold: 1
|
successThreshold: 1
|
||||||
failureThreshold: 3
|
failureThreshold: 3
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
|
|
||||||
nodeSelector: {}
|
readinessProbe:
|
||||||
tolerations: []
|
tcpSocket:
|
||||||
affinity: {}
|
port: 8080
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
timeoutSeconds: 2
|
||||||
|
successThreshold: 1
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
env:
|
nodeSelector: {}
|
||||||
PHP_TIMEZONE: Europe/Madrid
|
tolerations: []
|
||||||
PHP_MEMORYLIMIT: 512M
|
affinity: {}
|
||||||
PHP_UPLOADLIMIT: 128M
|
|
||||||
|
debug:
|
||||||
|
enabled: false
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ apiVersion: v2
|
||||||
name: helm-rustical
|
name: helm-rustical
|
||||||
description: A Helm chart for Rustical WebDAV server
|
description: A Helm chart for Rustical WebDAV server
|
||||||
type: application
|
type: application
|
||||||
version: 0.3.0
|
version: 1.1.1
|
||||||
appVersion: 0.14.1
|
appVersion: 0.14.1
|
||||||
annotations:
|
annotations:
|
||||||
artifacthub.io/images: "- name: rustical\n image: ghcr.io/lennart-k/rustical:0.14.1\n"
|
artifacthub.io/images: "- name: rustical\n image: ghcr.io/lennart-k/rustical:0.14.1\n"
|
||||||
|
|
|
||||||
115
k8s/charts/helm-rustical/README.md
Normal file
115
k8s/charts/helm-rustical/README.md
Normal 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
|
||||||
|
```
|
||||||
13
k8s/charts/helm-rustical/templates/configmap.yaml
Normal file
13
k8s/charts/helm-rustical/templates/configmap.yaml
Normal 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 }}
|
||||||
36
k8s/charts/helm-rustical/templates/hpa.yaml
Normal file
36
k8s/charts/helm-rustical/templates/hpa.yaml
Normal 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 }}
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
{{- if .Values.ingress.enabled -}}
|
{{- if .Values.rustical.ingress.enabled -}}
|
||||||
{{- $fullName := include "rustical.fullname" . -}}
|
{{- $fullName := include "rustical.fullname" . -}}
|
||||||
{{- $svcPort := .Values.service.port -}}
|
{{- $svcPort := .Values.rustical.service.port -}}
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "rustical.labels" . | nindent 4 }}
|
{{- include "rustical.labels" . | nindent 4 }}
|
||||||
{{- with .Values.ingress.annotations }}
|
{{- with .Values.rustical.ingress.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
{{- if .Values.ingress.className }}
|
{{- if .Values.rustical.ingress.className }}
|
||||||
ingressClassName: {{ .Values.ingress.className }}
|
ingressClassName: {{ .Values.rustical.ingress.className }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.ingress.tls }}
|
{{- if .Values.rustical.ingress.tls }}
|
||||||
tls:
|
tls:
|
||||||
{{- range .Values.ingress.tls }}
|
{{- range .Values.rustical.ingress.tls }}
|
||||||
- hosts:
|
- hosts:
|
||||||
{{- range .hosts }}
|
{{- range .hosts }}
|
||||||
- {{ . | quote }}
|
- {{ . | quote }}
|
||||||
|
|
@ -26,7 +26,7 @@ spec:
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
{{- range .Values.ingress.hosts }}
|
{{- range .Values.rustical.ingress.hosts }}
|
||||||
- host: {{ .host | quote }}
|
- host: {{ .host | quote }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "rustical.labels" . | nindent 4 }}
|
{{- include "rustical.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.service.type }}
|
type: {{ .Values.rustical.service.type }}
|
||||||
ports:
|
ports:
|
||||||
- port: {{ .Values.service.port }}
|
- port: {{ .Values.rustical.service.port }}
|
||||||
targetPort: {{ .Values.service.targetPort }}
|
targetPort: {{ .Values.rustical.service.targetPort }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: http
|
name: http
|
||||||
selector:
|
selector:
|
||||||
|
|
|
||||||
|
|
@ -6,40 +6,35 @@ metadata:
|
||||||
{{- include "rustical.labels" . | nindent 4 }}
|
{{- include "rustical.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
serviceName: {{ include "rustical.fullname" . }}-headless
|
serviceName: {{ include "rustical.fullname" . }}-headless
|
||||||
{{- if not .Values.autoscaling.enabled }}
|
{{- if not .Values.rustical.autoscaling.enabled }}
|
||||||
replicas: {{ .Values.replicaCount }}
|
replicas: {{ .Values.rustical.replicaCount }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
{{- include "rustical.selectorLabels" . | nindent 6 }}
|
{{- include "rustical.selectorLabels" . | nindent 6 }}
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
{{- with .Values.podAnnotations }}
|
{{- with .Values.rustical.podAnnotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "rustical.selectorLabels" . | nindent 8 }}
|
{{- include "rustical.selectorLabels" . | nindent 8 }}
|
||||||
spec:
|
spec:
|
||||||
{{- with .Values.imagePullSecrets }}
|
{{- with .Values.rustical.imagePullSecrets }}
|
||||||
imagePullSecrets:
|
imagePullSecrets:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
{{- toYaml .Values.rustical.podSecurityContext | nindent 8 }}
|
||||||
{{- if .Values.debug.enabled }}
|
{{- if .Values.rustical.debug.enabled }}
|
||||||
initContainers:
|
initContainers:
|
||||||
- name: debug
|
- name: debug
|
||||||
image: alpine:3.19
|
image: alpine:3.19
|
||||||
command: ["sleep", "infinity"]
|
command: ["sleep", "infinity"]
|
||||||
envFrom:
|
envFrom:
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: {{ .Values.secret.existingSecretName }}
|
name: {{ .Values.rustical.secret.existingSecretName }}
|
||||||
env:
|
|
||||||
{{- range $key, $value := .Values.env }}
|
|
||||||
- name: {{ $key }}
|
|
||||||
value: {{ $value | quote }}
|
|
||||||
{{- end }}
|
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /var/lib/rustical
|
mountPath: /var/lib/rustical
|
||||||
|
|
@ -53,67 +48,64 @@ spec:
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Chart.Name }}
|
- name: {{ .Chart.Name }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
{{- toYaml .Values.rustical.securityContext | nindent 12 }}
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
image: "{{ .Values.rustical.image.repository }}:{{ .Values.rustical.image.tag | default .Chart.AppVersion }}"
|
||||||
imagePullPolicy: {{ .Values.image.pullpolicy }}
|
imagePullPolicy: {{ .Values.rustical.image.pullpolicy }}
|
||||||
envFrom:
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: {{ include "rustical.fullname" . }}-config
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: {{ .Values.secret.existingSecretName | default (include "rustical.fullname" .) }}
|
name: {{ .Values.rustical.secret.existingSecretName | default (include "rustical.fullname" .) }}
|
||||||
env:
|
|
||||||
{{- range $key, $value := .Values.env }}
|
|
||||||
- name: {{ $key }}
|
|
||||||
value: {{ $value | quote }}
|
|
||||||
{{- end }}
|
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 4000
|
containerPort: 4000
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
{{- if not .Values.debug.enabled }}
|
{{- if not .Values.rustical.debug.enabled }}
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
{{- toYaml .Values.rustical.livenessProbe | nindent 12 }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
{{- toYaml .Values.rustical.readinessProbe | nindent 12 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
{{- toYaml .Values.rustical.resources | nindent 12 }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /var/lib/rustical
|
mountPath: /var/lib/rustical
|
||||||
{{- if .Values.persistence.existingClaim }}
|
{{- if .Values.rustical.persistence.existingClaim }}
|
||||||
volumes:
|
volumes:
|
||||||
- name: data
|
- name: data
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: {{ .Values.persistence.existingClaim }}
|
claimName: {{ .Values.rustical.persistence.existingClaim }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.rustical.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.rustical.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.tolerations }}
|
{{- with .Values.rustical.tolerations }}
|
||||||
tolerations:
|
tolerations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
restartPolicy: Always
|
restartPolicy: Always
|
||||||
automountServiceAccountToken: false
|
automountServiceAccountToken: false
|
||||||
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
|
{{- if and .Values.rustical.persistence.enabled (not .Values.rustical.persistence.existingClaim) }}
|
||||||
volumeClaimTemplates:
|
volumeClaimTemplates:
|
||||||
- metadata:
|
- metadata:
|
||||||
name: data
|
name: data
|
||||||
spec:
|
spec:
|
||||||
accessModes:
|
accessModes:
|
||||||
- {{ .Values.persistence.accessMode }}
|
- {{ .Values.rustical.persistence.accessMode }}
|
||||||
{{- if .Values.persistence.storageClass }}
|
{{- if .Values.rustical.persistence.storageClass }}
|
||||||
{{- if eq "-" .Values.persistence.storageClass }}
|
{{- if eq "-" .Values.rustical.persistence.storageClass }}
|
||||||
storageClassName: ""
|
storageClassName: ""
|
||||||
{{- else }}
|
{{- else }}
|
||||||
storageClassName: {{ .Values.persistence.storageClass | quote }}
|
storageClassName: {{ .Values.rustical.persistence.storageClass | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.persistence.size }}
|
storage: {{ .Values.rustical.persistence.size }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,67 @@
|
||||||
# Configuration is managed in k8s/argo-apps/rustical.yaml
|
rustical:
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: ghcr.io/lennart-k/rustical
|
repository: ghcr.io/lennart-k/rustical
|
||||||
pullpolicy: Always
|
pullpolicy: Always
|
||||||
tag: ""
|
tag: ""
|
||||||
|
|
||||||
imagePullSecrets: []
|
imagePullSecrets: []
|
||||||
podAnnotations: {}
|
podAnnotations: {}
|
||||||
podSecurityContext: {}
|
podSecurityContext: {}
|
||||||
securityContext: {}
|
securityContext: {}
|
||||||
|
|
||||||
service:
|
service:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 4000
|
|
||||||
targetPort: 4000
|
|
||||||
|
|
||||||
ingress:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
storageClass: "truenas-nfs-csi"
|
|
||||||
accessMode: ReadWriteMany
|
|
||||||
size: 50Gi
|
|
||||||
existingClaim: ""
|
|
||||||
|
|
||||||
autoscaling:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
resources: {}
|
|
||||||
|
|
||||||
livenessProbe:
|
|
||||||
tcpSocket:
|
|
||||||
port: 4000
|
port: 4000
|
||||||
initialDelaySeconds: 30
|
targetPort: 4000
|
||||||
timeoutSeconds: 15
|
|
||||||
successThreshold: 1
|
|
||||||
failureThreshold: 3
|
|
||||||
periodSeconds: 10
|
|
||||||
|
|
||||||
readinessProbe:
|
ingress:
|
||||||
tcpSocket:
|
enabled: false
|
||||||
port: 4000
|
|
||||||
initialDelaySeconds: 15
|
|
||||||
timeoutSeconds: 2
|
|
||||||
successThreshold: 1
|
|
||||||
failureThreshold: 3
|
|
||||||
periodSeconds: 10
|
|
||||||
|
|
||||||
nodeSelector: {}
|
persistence:
|
||||||
tolerations: []
|
enabled: true
|
||||||
affinity: {}
|
storageClass: "truenas-nfs-csi"
|
||||||
|
accessMode: ReadWriteMany
|
||||||
|
size: 50Gi
|
||||||
|
existingClaim: ""
|
||||||
|
|
||||||
env: {}
|
autoscaling:
|
||||||
|
enabled: false
|
||||||
|
minReplicas: 1
|
||||||
|
maxReplicas: 3
|
||||||
|
targetCPUUtilizationPercentage: 80
|
||||||
|
targetMemoryUtilizationPercentage: 80
|
||||||
|
|
||||||
secret:
|
resources: {}
|
||||||
enabled: true
|
|
||||||
existingSecretName: "rustical"
|
|
||||||
|
|
||||||
debug:
|
livenessProbe:
|
||||||
enabled: false
|
tcpSocket:
|
||||||
|
port: 4000
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 15
|
||||||
|
successThreshold: 1
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
|
readinessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: 4000
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
timeoutSeconds: 2
|
||||||
|
successThreshold: 1
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
tolerations: []
|
||||||
|
affinity: {}
|
||||||
|
|
||||||
|
config: {}
|
||||||
|
|
||||||
|
secret:
|
||||||
|
enabled: true
|
||||||
|
existingSecretName: "rustical"
|
||||||
|
|
||||||
|
debug:
|
||||||
|
enabled: false
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ apiVersion: v2
|
||||||
name: immich
|
name: immich
|
||||||
description: A Helm chart for Immich photo and video backup solution
|
description: A Helm chart for Immich photo and video backup solution
|
||||||
type: application
|
type: application
|
||||||
version: 0.2.3
|
version: 1.0.1
|
||||||
appVersion: v3.1.0
|
appVersion: v3.1.0
|
||||||
keywords:
|
keywords:
|
||||||
- immich
|
- immich
|
||||||
|
|
|
||||||
192
k8s/charts/immich/README.md
Normal file
192
k8s/charts/immich/README.md
Normal 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
|
||||||
|
```
|
||||||
|
|
@ -1,22 +1,31 @@
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ include "immich.fullname" . }}
|
name: {{ include "immich.fullname" . }}-config
|
||||||
labels:
|
labels:
|
||||||
{{- include "immich.labels" . | nindent 4 }}
|
{{- include "immich.labels" . | nindent 4 }}
|
||||||
data:
|
data:
|
||||||
TZ: {{ .Values.immich.general.tz | quote }}
|
TZ: {{ .Values.immich.config.general.timezone | quote }}
|
||||||
IMMICH_PORT: {{ .Values.immich.service.port | quote }}
|
IMMICH_PORT: {{ .Values.immich.service.port | quote }}
|
||||||
IMMICH_MACHINE_LEARNING_URL: {{ printf "http://%s-machine-learning:3003" (include "immich.fullname" .) | quote }}
|
IMMICH_MACHINE_LEARNING_URL: {{ printf "http://%s-machine-learning:3003" (include "immich.fullname" .) | quote }}
|
||||||
{{- range $key, $value := .Values.immich.db }}
|
{{- if .Values.immich.config.database.hostname }}
|
||||||
{{- if and (ne $key "password") $value }}
|
DB_HOSTNAME: {{ .Values.immich.config.database.hostname | quote }}
|
||||||
{{ $keyName := printf "DB_%s" (upper $key) }}
|
|
||||||
{{ $keyName }}: {{ $value | quote }}
|
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.immich.config.database.port }}
|
||||||
|
DB_PORT: {{ .Values.immich.config.database.port | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- range $key, $value := .Values.immich.redis }}
|
{{- if .Values.immich.config.database.username }}
|
||||||
{{- if and (ne $key "password") (ne $key "username") $value }}
|
DB_USERNAME: {{ .Values.immich.config.database.username | quote }}
|
||||||
{{ $keyName := printf "REDIS_%s" (upper $key) }}
|
|
||||||
{{ $keyName }}: {{ $value | quote }}
|
|
||||||
{{- end }}
|
{{- 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 }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,50 @@ spec:
|
||||||
mountPath: /data/profile
|
mountPath: /data/profile
|
||||||
- name: backups
|
- name: backups
|
||||||
mountPath: /data/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:
|
containers:
|
||||||
- name: {{ .Chart.Name }}-server
|
- name: {{ .Chart.Name }}-server
|
||||||
securityContext:
|
securityContext:
|
||||||
|
|
@ -69,32 +113,30 @@ spec:
|
||||||
image: "{{ .Values.immich.image.repository }}:{{ .Values.immich.image.tag | default .Chart.AppVersion }}"
|
image: "{{ .Values.immich.image.repository }}:{{ .Values.immich.image.tag | default .Chart.AppVersion }}"
|
||||||
imagePullPolicy: {{ .Values.immich.image.pullPolicy }}
|
imagePullPolicy: {{ .Values.immich.image.pullPolicy }}
|
||||||
envFrom:
|
envFrom:
|
||||||
{{- if .Values.secret.existingSecretName }}
|
{{- if .Values.immich.secret.existingSecretName }}
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: {{ .Values.secret.existingSecretName }}
|
name: {{ .Values.immich.secret.existingSecretName }}
|
||||||
{{- else if .Values.secret.enabled }}
|
{{- else if .Values.immich.secret.enabled }}
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: {{ include "immich.fullname" . }}
|
name: {{ include "immich.fullname" . }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: {{ include "immich.fullname" . }}
|
name: {{ include "immich.fullname" . }}-config
|
||||||
env:
|
env:
|
||||||
- name: IMMICH_PORT
|
- name: IMMICH_PORT
|
||||||
value: {{ .Values.immich.service.port | quote }}
|
value: {{ .Values.immich.service.port | quote }}
|
||||||
- name: IMMICH_MACHINE_LEARNING_URL
|
- name: IMMICH_MACHINE_LEARNING_URL
|
||||||
value: {{ printf "http://%s-machine-learning:3003" (include "immich.fullname" .) | quote }}
|
value: {{ printf "http://%s-machine-learning:3003" (include "immich.fullname" .) | quote }}
|
||||||
{{- range $key, $value := .Values.immich.env }}
|
{{- if not .Values.immich.debug.enabled }}
|
||||||
- name: {{ $key }}
|
|
||||||
value: {{ $value | quote }}
|
|
||||||
{{- end }}
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 2283
|
|
||||||
protocol: TCP
|
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
{{- toYaml .Values.immich.livenessProbe | nindent 12 }}
|
{{- toYaml .Values.immich.livenessProbe | nindent 12 }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
{{- toYaml .Values.immich.readinessProbe | nindent 12 }}
|
{{- toYaml .Values.immich.readinessProbe | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 2283
|
||||||
|
protocol: TCP
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.immich.resources | nindent 12 }}
|
{{- toYaml .Values.immich.resources | nindent 12 }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ spec:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.immich.persistence.backups.size }}
|
storage: {{ .Values.immich.persistence.backups.size }}
|
||||||
{{- end }}
|
{{- 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
|
apiVersion: v1
|
||||||
kind: PersistentVolumeClaim
|
kind: PersistentVolumeClaim
|
||||||
|
|
@ -116,11 +116,11 @@ metadata:
|
||||||
{{- include "immich.labels" . | nindent 4 }}
|
{{- include "immich.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
accessModes:
|
accessModes:
|
||||||
- {{ .Values.machineLearning.persistence.modelCache.accessMode }}
|
- {{ .Values.immich.machineLearning.persistence.modelCache.accessMode }}
|
||||||
{{- if .Values.machineLearning.persistence.modelCache.storageClass }}
|
{{- if .Values.immich.machineLearning.persistence.modelCache.storageClass }}
|
||||||
storageClassName: {{ .Values.machineLearning.persistence.modelCache.storageClass | quote }}
|
storageClassName: {{ .Values.immich.machineLearning.persistence.modelCache.storageClass | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.machineLearning.persistence.modelCache.size }}
|
storage: {{ .Values.immich.machineLearning.persistence.modelCache.size }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -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
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -7,19 +7,19 @@ metadata:
|
||||||
{{- include "immich.labels" . | nindent 4 }}
|
{{- include "immich.labels" . | nindent 4 }}
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
{{- if .Values.immich.db.password }}
|
{{- if .Values.immich.config.database.password }}
|
||||||
DB_PASSWORD: {{ .Values.immich.db.password | quote }}
|
DB_PASSWORD: {{ .Values.immich.config.database.password | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.immich.db.username }}
|
{{- if .Values.immich.config.database.username }}
|
||||||
DB_USERNAME: {{ .Values.immich.db.username | quote }}
|
DB_USERNAME: {{ .Values.immich.config.database.username | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.immich.redis.password }}
|
{{- if .Values.immich.config.redis.password }}
|
||||||
REDIS_PASSWORD: {{ .Values.immich.redis.password | quote }}
|
REDIS_PASSWORD: {{ .Values.immich.config.redis.password | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.immich.redis.username }}
|
{{- if .Values.immich.config.redis.username }}
|
||||||
REDIS_USERNAME: {{ .Values.immich.redis.username | quote }}
|
REDIS_USERNAME: {{ .Values.immich.config.redis.username | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- range $key, $value := .Values.secret.data }}
|
{{- range $key, $value := .Values.immich.secret.data }}
|
||||||
{{ $key }}: {{ $value | quote }}
|
{{ $key }}: {{ $value | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ spec:
|
||||||
{{- include "immich.selectorLabels" . | nindent 4 }}
|
{{- include "immich.selectorLabels" . | nindent 4 }}
|
||||||
app.kubernetes.io/component: server
|
app.kubernetes.io/component: server
|
||||||
---
|
---
|
||||||
{{- if .Values.machineLearning.enabled }}
|
{{- if .Values.immich.machineLearning.enabled }}
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -24,9 +24,9 @@ metadata:
|
||||||
{{- include "immich.labels" . | nindent 4 }}
|
{{- include "immich.labels" . | nindent 4 }}
|
||||||
app.kubernetes.io/component: machine-learning
|
app.kubernetes.io/component: machine-learning
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.machineLearning.service.type }}
|
type: {{ .Values.immich.machineLearning.service.type }}
|
||||||
ports:
|
ports:
|
||||||
- port: {{ .Values.machineLearning.service.port }}
|
- port: {{ .Values.immich.machineLearning.service.port }}
|
||||||
targetPort: http
|
targetPort: http
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: http
|
name: http
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{{- if .Values.machineLearning.enabled -}}
|
{{- if .Values.immich.machineLearning.enabled -}}
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: StatefulSet
|
kind: StatefulSet
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -8,8 +8,8 @@ metadata:
|
||||||
app.kubernetes.io/component: machine-learning
|
app.kubernetes.io/component: machine-learning
|
||||||
spec:
|
spec:
|
||||||
serviceName: {{ include "immich.fullname" . }}-machine-learning-headless
|
serviceName: {{ include "immich.fullname" . }}-machine-learning-headless
|
||||||
{{- if not .Values.machineLearning.autoscaling.enabled }}
|
{{- if not .Values.immich.machineLearning.autoscaling.enabled }}
|
||||||
replicas: {{ .Values.machineLearning.replicaCount }}
|
replicas: {{ .Values.immich.machineLearning.replicaCount }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
|
|
@ -17,7 +17,7 @@ spec:
|
||||||
app.kubernetes.io/component: machine-learning
|
app.kubernetes.io/component: machine-learning
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
{{- with .Values.machineLearning.podAnnotations }}
|
{{- with .Values.immich.machineLearning.podAnnotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
@ -25,41 +25,41 @@ spec:
|
||||||
{{- include "immich.selectorLabels" . | nindent 8 }}
|
{{- include "immich.selectorLabels" . | nindent 8 }}
|
||||||
app.kubernetes.io/component: machine-learning
|
app.kubernetes.io/component: machine-learning
|
||||||
spec:
|
spec:
|
||||||
{{- with .Values.machineLearning.imagePullSecrets }}
|
{{- with .Values.immich.machineLearning.imagePullSecrets }}
|
||||||
imagePullSecrets:
|
imagePullSecrets:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.machineLearning.podSecurityContext | nindent 8 }}
|
{{- toYaml .Values.immich.machineLearning.podSecurityContext | nindent 8 }}
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Chart.Name }}-machine-learning
|
- name: {{ .Chart.Name }}-machine-learning
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.machineLearning.securityContext | nindent 12 }}
|
{{- toYaml .Values.immich.machineLearning.securityContext | nindent 12 }}
|
||||||
image: "{{ .Values.machineLearning.image.repository }}:{{ .Values.machineLearning.image.tag | default .Chart.AppVersion }}"
|
image: "{{ .Values.immich.machineLearning.image.repository }}:{{ .Values.immich.machineLearning.image.tag | default .Chart.AppVersion }}"
|
||||||
imagePullPolicy: {{ .Values.machineLearning.image.pullPolicy }}
|
imagePullPolicy: {{ .Values.immich.machineLearning.image.pullPolicy }}
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 3003
|
containerPort: 3003
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
env:
|
env:
|
||||||
- name: IMMICH_PORT
|
- name: IMMICH_PORT
|
||||||
value: {{ .Values.machineLearning.service.port | quote }}
|
value: {{ .Values.immich.machineLearning.service.port | quote }}
|
||||||
- name: IMMICH_MACHINE_LEARNING_PORT
|
- name: IMMICH_MACHINE_LEARNING_PORT
|
||||||
value: "3003"
|
value: "3003"
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.machineLearning.resources | nindent 12 }}
|
{{- toYaml .Values.immich.machineLearning.resources | nindent 12 }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: model-cache
|
- name: model-cache
|
||||||
mountPath: /cache
|
mountPath: /cache
|
||||||
{{- with .Values.machineLearning.nodeSelector }}
|
{{- with .Values.immich.machineLearning.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.machineLearning.affinity }}
|
{{- with .Values.immich.machineLearning.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.machineLearning.tolerations }}
|
{{- with .Values.immich.machineLearning.tolerations }}
|
||||||
tolerations:
|
tolerations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -19,22 +19,21 @@ immich:
|
||||||
ingress:
|
ingress:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
db:
|
config:
|
||||||
hostname: "192.168.1.3"
|
database:
|
||||||
port: 55432
|
hostname: ""
|
||||||
username: ""
|
port: 55432
|
||||||
password: ""
|
username: ""
|
||||||
database_name: ""
|
password: ""
|
||||||
|
name: ""
|
||||||
redis:
|
redis:
|
||||||
hostname: "192.168.1.3"
|
hostname: ""
|
||||||
port: 300036
|
port: 6379
|
||||||
username: ""
|
username: ""
|
||||||
password: ""
|
password: ""
|
||||||
dbindex: ""
|
dbIndex: ""
|
||||||
|
general:
|
||||||
general:
|
timezone: Europe/Madrid
|
||||||
tz: "Europe/Madrid"
|
|
||||||
|
|
||||||
persistence:
|
persistence:
|
||||||
library:
|
library:
|
||||||
|
|
@ -104,66 +103,67 @@ immich:
|
||||||
tolerations: []
|
tolerations: []
|
||||||
affinity: {}
|
affinity: {}
|
||||||
|
|
||||||
env: {}
|
machineLearning:
|
||||||
|
|
||||||
machineLearning:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
image:
|
|
||||||
repository: ghcr.io/immich-app/immich-machine-learning
|
|
||||||
pullPolicy: Always
|
|
||||||
tag: ""
|
|
||||||
|
|
||||||
imagePullSecrets: []
|
|
||||||
podAnnotations: {}
|
|
||||||
podSecurityContext: {}
|
|
||||||
securityContext: {}
|
|
||||||
|
|
||||||
service:
|
|
||||||
type: ClusterIP
|
|
||||||
port: 3003
|
|
||||||
|
|
||||||
replicaCount: 1
|
|
||||||
|
|
||||||
autoscaling:
|
|
||||||
enabled: false
|
enabled: false
|
||||||
minReplicas: 1
|
|
||||||
maxReplicas: 3
|
|
||||||
targetCPUUtilizationPercentage: 80
|
|
||||||
targetMemoryUtilizationPercentage: 80
|
|
||||||
|
|
||||||
resources: {}
|
image:
|
||||||
|
repository: ghcr.io/immich-app/immich-machine-learning
|
||||||
|
pullPolicy: Always
|
||||||
|
tag: ""
|
||||||
|
|
||||||
livenessProbe:
|
imagePullSecrets: []
|
||||||
tcpSocket:
|
podAnnotations: {}
|
||||||
|
podSecurityContext: {}
|
||||||
|
securityContext: {}
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
port: 3003
|
port: 3003
|
||||||
initialDelaySeconds: 30
|
|
||||||
timeoutSeconds: 15
|
|
||||||
successThreshold: 1
|
|
||||||
failureThreshold: 3
|
|
||||||
periodSeconds: 10
|
|
||||||
|
|
||||||
readinessProbe:
|
replicaCount: 1
|
||||||
tcpSocket:
|
|
||||||
port: 3003
|
|
||||||
initialDelaySeconds: 15
|
|
||||||
timeoutSeconds: 2
|
|
||||||
successThreshold: 1
|
|
||||||
failureThreshold: 3
|
|
||||||
periodSeconds: 10
|
|
||||||
|
|
||||||
nodeSelector: {}
|
autoscaling:
|
||||||
tolerations: []
|
enabled: false
|
||||||
affinity: {}
|
minReplicas: 1
|
||||||
|
maxReplicas: 3
|
||||||
|
targetCPUUtilizationPercentage: 80
|
||||||
|
targetMemoryUtilizationPercentage: 80
|
||||||
|
|
||||||
persistence:
|
resources: {}
|
||||||
modelCache:
|
|
||||||
enabled: true
|
|
||||||
storageClass: ""
|
|
||||||
accessMode: ReadWriteMany
|
|
||||||
size: 10Gi
|
|
||||||
|
|
||||||
secret:
|
livenessProbe:
|
||||||
enabled: false
|
tcpSocket:
|
||||||
existingSecretName: ""
|
port: 3003
|
||||||
data: {}
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 15
|
||||||
|
successThreshold: 1
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
|
readinessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: 3003
|
||||||
|
initialDelaySeconds: 15
|
||||||
|
timeoutSeconds: 2
|
||||||
|
successThreshold: 1
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
|
nodeSelector: {}
|
||||||
|
tolerations: []
|
||||||
|
affinity: {}
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
modelCache:
|
||||||
|
enabled: true
|
||||||
|
storageClass: ""
|
||||||
|
accessMode: ReadWriteMany
|
||||||
|
size: 10Gi
|
||||||
|
|
||||||
|
secret:
|
||||||
|
enabled: false
|
||||||
|
existingSecretName: ""
|
||||||
|
data: {}
|
||||||
|
|
||||||
|
debug:
|
||||||
|
enabled: false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@ apiVersion: v2
|
||||||
name: miniflux
|
name: miniflux
|
||||||
description: A Helm chart for Miniflux RSS reader
|
description: A Helm chart for Miniflux RSS reader
|
||||||
type: application
|
type: application
|
||||||
version: 0.1.0
|
version: 1.0.2
|
||||||
appVersion: 2.3.3
|
appVersion: 2.3.3
|
||||||
annotations:
|
annotations:
|
||||||
artifacthub.io/images: |
|
artifacthub.io/images: "- name: miniflux\n image: miniflux/miniflux:2.3.3\n"
|
||||||
- name: miniflux
|
|
||||||
image: miniflux/miniflux:2.3.3
|
|
||||||
|
|
|
||||||
153
k8s/charts/miniflux/README.md
Normal file
153
k8s/charts/miniflux/README.md
Normal 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
|
||||||
|
```
|
||||||
40
k8s/charts/miniflux/templates/configmap.yaml
Normal file
40
k8s/charts/miniflux/templates/configmap.yaml
Normal 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 }}
|
||||||
|
|
@ -7,8 +7,8 @@ metadata:
|
||||||
annotations:
|
annotations:
|
||||||
kube-score/ignore: pod-networkpolicy,deployment-has-host-podantiaffinity
|
kube-score/ignore: pod-networkpolicy,deployment-has-host-podantiaffinity
|
||||||
spec:
|
spec:
|
||||||
{{- if not .Values.autoscaling.enabled }}
|
{{- if not .Values.miniflux.autoscaling.enabled }}
|
||||||
replicas: {{ .Values.replicaCount }}
|
replicas: {{ .Values.miniflux.replicaCount }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
|
|
@ -20,52 +20,49 @@ spec:
|
||||||
type: RollingUpdate
|
type: RollingUpdate
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
{{- with .Values.podAnnotations }}
|
{{- with .Values.miniflux.podAnnotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "miniflux.selectorLabels" . | nindent 8 }}
|
{{- include "miniflux.selectorLabels" . | nindent 8 }}
|
||||||
spec:
|
spec:
|
||||||
{{- with .Values.imagePullSecrets }}
|
{{- with .Values.miniflux.imagePullSecrets }}
|
||||||
imagePullSecrets:
|
imagePullSecrets:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
{{- toYaml .Values.miniflux.podSecurityContext | nindent 8 }}
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Chart.Name }}
|
- name: {{ .Chart.Name }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
{{- toYaml .Values.miniflux.securityContext | nindent 12 }}
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
image: "{{ .Values.miniflux.image.repository }}:{{ .Values.miniflux.image.tag | default .Chart.AppVersion }}"
|
||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
imagePullPolicy: {{ .Values.miniflux.image.pullPolicy }}
|
||||||
envFrom:
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: {{ include "miniflux.fullname" . }}-config
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: {{ .Values.secret.existingSecretName | default (include "miniflux.fullname" .) }}
|
name: {{ .Values.miniflux.secret.existingSecretName | default (include "miniflux.fullname" .) }}
|
||||||
env:
|
|
||||||
{{- range $key, $value := .Values.env }}
|
|
||||||
- name: {{ $key }}
|
|
||||||
value: {{ $value | quote }}
|
|
||||||
{{- end }}
|
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 8080
|
containerPort: 8080
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
{{- toYaml .Values.miniflux.livenessProbe | nindent 12 }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
{{- toYaml .Values.miniflux.readinessProbe | nindent 12 }}
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
{{- toYaml .Values.miniflux.resources | nindent 12 }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.miniflux.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.miniflux.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.tolerations }}
|
{{- with .Values.miniflux.tolerations }}
|
||||||
tolerations:
|
tolerations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
33
k8s/charts/miniflux/templates/hpa.yaml
Normal file
33
k8s/charts/miniflux/templates/hpa.yaml
Normal 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 }}
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
{{- if .Values.ingress.enabled -}}
|
{{- if .Values.miniflux.ingress.enabled -}}
|
||||||
{{- $fullName := include "miniflux.fullname" . -}}
|
{{- $fullName := include "miniflux.fullname" . -}}
|
||||||
{{- $svcPort := .Values.service.port -}}
|
{{- $svcPort := .Values.miniflux.service.port -}}
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "miniflux.labels" . | nindent 4 }}
|
{{- include "miniflux.labels" . | nindent 4 }}
|
||||||
{{- with .Values.ingress.annotations }}
|
{{- with .Values.miniflux.ingress.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
{{- if .Values.ingress.className }}
|
{{- if .Values.miniflux.ingress.className }}
|
||||||
ingressClassName: {{ .Values.ingress.className }}
|
ingressClassName: {{ .Values.miniflux.ingress.className }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.ingress.tls }}
|
{{- if .Values.miniflux.ingress.tls }}
|
||||||
tls:
|
tls:
|
||||||
{{- range .Values.ingress.tls }}
|
{{- range .Values.miniflux.ingress.tls }}
|
||||||
- hosts:
|
- hosts:
|
||||||
{{- range .hosts }}
|
{{- range .hosts }}
|
||||||
- {{ . | quote }}
|
- {{ . | quote }}
|
||||||
|
|
@ -26,7 +26,7 @@ spec:
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
{{- range .Values.ingress.hosts }}
|
{{- range .Values.miniflux.ingress.hosts }}
|
||||||
- host: {{ .host | quote }}
|
- host: {{ .host | quote }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
{{- if .Values.podDisruptionBudget.enabled -}}
|
{{- if .Values.miniflux.podDisruptionBudget.enabled -}}
|
||||||
apiVersion: policy/v1
|
apiVersion: policy/v1
|
||||||
kind: PodDisruptionBudget
|
kind: PodDisruptionBudget
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -6,11 +6,11 @@ metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "miniflux.labels" . | nindent 4 }}
|
{{- include "miniflux.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
{{- if .Values.podDisruptionBudget.minAvailable }}
|
{{- if .Values.miniflux.podDisruptionBudget.minAvailable }}
|
||||||
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
|
minAvailable: {{ .Values.miniflux.podDisruptionBudget.minAvailable }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.podDisruptionBudget.maxUnavailable }}
|
{{- if .Values.miniflux.podDisruptionBudget.maxUnavailable }}
|
||||||
maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
|
maxUnavailable: {{ .Values.miniflux.podDisruptionBudget.maxUnavailable }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
|
|
|
||||||
|
|
@ -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
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -7,7 +7,7 @@ metadata:
|
||||||
{{- include "miniflux.labels" . | nindent 4 }}
|
{{- include "miniflux.labels" . | nindent 4 }}
|
||||||
type: Opaque
|
type: Opaque
|
||||||
stringData:
|
stringData:
|
||||||
{{- range $key, $value := .Values.secret.data }}
|
{{- range $key, $value := .Values.miniflux.secret.data }}
|
||||||
{{ $key }}: {{ $value | quote }}
|
{{ $key }}: {{ $value | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "miniflux.labels" . | nindent 4 }}
|
{{- include "miniflux.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.service.type }}
|
type: {{ .Values.miniflux.service.type }}
|
||||||
ports:
|
ports:
|
||||||
- port: {{ .Values.service.port }}
|
- port: {{ .Values.miniflux.service.port }}
|
||||||
targetPort: {{ .Values.service.targetPort }}
|
targetPort: {{ .Values.miniflux.service.targetPort }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: http
|
name: http
|
||||||
selector:
|
selector:
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,68 @@
|
||||||
# Configuration is managed in k8s/argo-apps/miniflux.yaml
|
miniflux:
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: miniflux/miniflux
|
repository: miniflux/miniflux
|
||||||
pullpolicy: always
|
pullpolicy: always
|
||||||
tag: ""
|
tag: ""
|
||||||
|
|
||||||
imagePullSecrets: []
|
imagePullSecrets: []
|
||||||
podAnnotations: {}
|
podAnnotations: {}
|
||||||
podSecurityContext: {}
|
podSecurityContext: {}
|
||||||
securityContext: {}
|
securityContext: {}
|
||||||
|
|
||||||
service:
|
service:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 8888
|
port: 8888
|
||||||
targetPort: 8080
|
targetPort: 8080
|
||||||
|
|
||||||
ingress:
|
ingress:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
resources: {}
|
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
|
||||||
|
|
||||||
livenessProbe: {}
|
resources: {}
|
||||||
readinessProbe: {}
|
|
||||||
|
|
||||||
autoscaling:
|
livenessProbe: {}
|
||||||
enabled: false
|
readinessProbe: {}
|
||||||
|
|
||||||
nodeSelector: {}
|
autoscaling:
|
||||||
tolerations: []
|
enabled: false
|
||||||
affinity: {}
|
minReplicas: 1
|
||||||
|
maxReplicas: 3
|
||||||
|
targetCPUUtilizationPercentage: 80
|
||||||
|
targetMemoryUtilizationPercentage: 80
|
||||||
|
|
||||||
podDisruptionBudget:
|
nodeSelector: {}
|
||||||
enabled: false
|
tolerations: []
|
||||||
|
affinity: {}
|
||||||
|
|
||||||
env: {}
|
podDisruptionBudget:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
secret:
|
secret:
|
||||||
enabled: false
|
enabled: false
|
||||||
existingSecretName: ""
|
existingSecretName: ""
|
||||||
data: {}
|
data: {}
|
||||||
|
|
||||||
|
debug:
|
||||||
|
enabled: false
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ description: 'Ultra-fast, secure & lightweight self-hosted cloud storage — you
|
||||||
|
|
||||||
'
|
'
|
||||||
type: application
|
type: application
|
||||||
version: 1.0.0
|
version: 1.0.1
|
||||||
appVersion: 0.8.6
|
appVersion: 0.8.6
|
||||||
annotations:
|
annotations:
|
||||||
artifacthub.io/images: "- name: oxicloud\n image: diocrafts/oxicloud:0.8.6\n"
|
artifacthub.io/images: "- name: oxicloud\n image: diocrafts/oxicloud:0.8.6\n"
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ oxicloud:
|
||||||
| `oxicloud.persistence.size` | string | `50Gi` | PVC size |
|
| `oxicloud.persistence.size` | string | `50Gi` | PVC size |
|
||||||
| `oxicloud.secret.existingSecretName` | string | `""` | Existing secret name |
|
| `oxicloud.secret.existingSecretName` | string | `""` | Existing secret name |
|
||||||
| `oxicloud.wopi.enabled` | bool | `false` | Enable WOPI/Collabora integration |
|
| `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.enabled` | bool | `false` | Enable ingress |
|
||||||
| `oxicloud.ingress.className` | string | `""` | Ingress class |
|
| `oxicloud.ingress.className` | string | `""` | Ingress class |
|
||||||
| `oxicloud.resources` | object | `{}` | Container resources |
|
| `oxicloud.resources` | object | `{}` | Container resources |
|
||||||
|
|
@ -115,6 +116,24 @@ Required secret keys depend on configuration:
|
||||||
|
|
||||||
## Troubleshooting
|
## 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
|
### Pod not starting
|
||||||
|
|
||||||
Check if the PVC is bound:
|
Check if the PVC is bound:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
---
|
|
||||||
{{- if .Values.oxicloud.ingress.enabled -}}
|
{{- if .Values.oxicloud.ingress.enabled -}}
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
|
|
@ -51,7 +50,6 @@ spec:
|
||||||
port:
|
port:
|
||||||
number: {{ .Values.oxicloud.wopi.collabora.service.port }}
|
number: {{ .Values.oxicloud.wopi.collabora.service.port }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
- path: /
|
- path: /
|
||||||
pathType: Prefix
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
|
|
|
||||||
|
|
@ -85,3 +85,6 @@ oxicloud:
|
||||||
enabled: true
|
enabled: true
|
||||||
existingSecretName: "oxicloud-secret"
|
existingSecretName: "oxicloud-secret"
|
||||||
data: {}
|
data: {}
|
||||||
|
|
||||||
|
debug:
|
||||||
|
enabled: false
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@ apiVersion: v2
|
||||||
name: vaultwarden
|
name: vaultwarden
|
||||||
description: A Helm chart for Vaultwarden password manager
|
description: A Helm chart for Vaultwarden password manager
|
||||||
type: application
|
type: application
|
||||||
version: 0.1.0
|
version: 1.0.2
|
||||||
appVersion: 1.37.1
|
appVersion: 1.37.1
|
||||||
annotations:
|
annotations:
|
||||||
artifacthub.io/images: |
|
artifacthub.io/images: "- name: vaultwarden\n image: vaultwarden/server:1.37.1-alpine\n"
|
||||||
- name: vaultwarden
|
|
||||||
image: vaultwarden/server:1.37.1-alpine
|
|
||||||
|
|
|
||||||
132
k8s/charts/vaultwarden/README.md
Normal file
132
k8s/charts/vaultwarden/README.md
Normal 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
|
||||||
|
```
|
||||||
37
k8s/charts/vaultwarden/templates/configmap.yaml
Normal file
37
k8s/charts/vaultwarden/templates/configmap.yaml
Normal 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 }}
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
{{- if .Values.ingress.enabled -}}
|
{{- if .Values.vaultwarden.ingress.enabled -}}
|
||||||
{{- $fullName := include "vaultwarden.fullname" . -}}
|
{{- $fullName := include "vaultwarden.fullname" . -}}
|
||||||
{{- $svcPort := .Values.service.port -}}
|
{{- $svcPort := .Values.vaultwarden.service.port -}}
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "vaultwarden.labels" . | nindent 4 }}
|
{{- include "vaultwarden.labels" . | nindent 4 }}
|
||||||
{{- with .Values.ingress.annotations }}
|
{{- with .Values.vaultwarden.ingress.annotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 4 }}
|
{{- toYaml . | nindent 4 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
spec:
|
spec:
|
||||||
{{- if .Values.ingress.className }}
|
{{- if .Values.vaultwarden.ingress.className }}
|
||||||
ingressClassName: {{ .Values.ingress.className }}
|
ingressClassName: {{ .Values.vaultwarden.ingress.className }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- if .Values.ingress.tls }}
|
{{- if .Values.vaultwarden.ingress.tls }}
|
||||||
tls:
|
tls:
|
||||||
{{- range .Values.ingress.tls }}
|
{{- range .Values.vaultwarden.ingress.tls }}
|
||||||
- hosts:
|
- hosts:
|
||||||
{{- range .hosts }}
|
{{- range .hosts }}
|
||||||
- {{ . | quote }}
|
- {{ . | quote }}
|
||||||
|
|
@ -26,7 +26,7 @@ spec:
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
rules:
|
rules:
|
||||||
{{- range .Values.ingress.hosts }}
|
{{- range .Values.vaultwarden.ingress.hosts }}
|
||||||
- host: {{ .host | quote }}
|
- host: {{ .host | quote }}
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ metadata:
|
||||||
labels:
|
labels:
|
||||||
{{- include "vaultwarden.labels" . | nindent 4 }}
|
{{- include "vaultwarden.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
type: {{ .Values.service.type }}
|
type: {{ .Values.vaultwarden.service.type }}
|
||||||
ports:
|
ports:
|
||||||
- port: {{ .Values.service.port }}
|
- port: {{ .Values.vaultwarden.service.port }}
|
||||||
targetPort: {{ .Values.service.targetPort }}
|
targetPort: {{ .Values.vaultwarden.service.targetPort }}
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
name: http
|
name: http
|
||||||
selector:
|
selector:
|
||||||
|
|
|
||||||
|
|
@ -6,103 +6,86 @@ metadata:
|
||||||
{{- include "vaultwarden.labels" . | nindent 4 }}
|
{{- include "vaultwarden.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
serviceName: {{ include "vaultwarden.fullname" . }}-headless
|
serviceName: {{ include "vaultwarden.fullname" . }}-headless
|
||||||
{{- if not .Values.autoscaling.enabled }}
|
{{- if not .Values.vaultwarden.autoscaling.enabled }}
|
||||||
replicas: {{ .Values.replicaCount }}
|
replicas: {{ .Values.vaultwarden.replicaCount }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
{{- include "vaultwarden.selectorLabels" . | nindent 6 }}
|
{{- include "vaultwarden.selectorLabels" . | nindent 6 }}
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
{{- with .Values.podAnnotations }}
|
{{- with .Values.vaultwarden.podAnnotations }}
|
||||||
annotations:
|
annotations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
labels:
|
labels:
|
||||||
{{- include "vaultwarden.selectorLabels" . | nindent 8 }}
|
{{- include "vaultwarden.selectorLabels" . | nindent 8 }}
|
||||||
spec:
|
spec:
|
||||||
{{- with .Values.imagePullSecrets }}
|
{{- with .Values.vaultwarden.imagePullSecrets }}
|
||||||
imagePullSecrets:
|
imagePullSecrets:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
{{- toYaml .Values.vaultwarden.podSecurityContext | nindent 8 }}
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Chart.Name }}
|
- name: {{ .Chart.Name }}
|
||||||
securityContext:
|
securityContext:
|
||||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
{{- toYaml .Values.vaultwarden.securityContext | nindent 12 }}
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default (printf "%s-alpine" .Chart.AppVersion) }}"
|
image: "{{ .Values.vaultwarden.image.repository }}:{{ .Values.vaultwarden.image.tag | default (printf "%s-alpine" .Chart.AppVersion) }}"
|
||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
imagePullPolicy: {{ .Values.vaultwarden.image.pullPolicy }}
|
||||||
envFrom:
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: {{ include "vaultwarden.fullname" . }}-config
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: {{ .Values.secret.existingSecretName | default (include "vaultwarden.fullname" .) }}
|
name: {{ .Values.vaultwarden.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 }}
|
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
containerPort: 80
|
containerPort: 80
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
{{- toYaml .Values.vaultwarden.livenessProbe | nindent 12 }}
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
{{- toYaml .Values.vaultwarden.readinessProbe | nindent 12 }}
|
||||||
resources:
|
resources:
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
{{- toYaml .Values.vaultwarden.resources | nindent 12 }}
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: data
|
- name: data
|
||||||
mountPath: /data
|
mountPath: /data
|
||||||
{{- if .Values.persistence.existingClaim }}
|
{{- if .Values.vaultwarden.persistence.existingClaim }}
|
||||||
volumes:
|
volumes:
|
||||||
- name: data
|
- name: data
|
||||||
persistentVolumeClaim:
|
persistentVolumeClaim:
|
||||||
claimName: {{ .Values.persistence.existingClaim }}
|
claimName: {{ .Values.vaultwarden.persistence.existingClaim }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.nodeSelector }}
|
{{- with .Values.vaultwarden.nodeSelector }}
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.affinity }}
|
{{- with .Values.vaultwarden.affinity }}
|
||||||
affinity:
|
affinity:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- with .Values.tolerations }}
|
{{- with .Values.vaultwarden.tolerations }}
|
||||||
tolerations:
|
tolerations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
restartPolicy: Always
|
restartPolicy: Always
|
||||||
automountServiceAccountToken: false
|
automountServiceAccountToken: false
|
||||||
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
|
{{- if and .Values.vaultwarden.persistence.enabled (not .Values.vaultwarden.persistence.existingClaim) }}
|
||||||
volumeClaimTemplates:
|
volumeClaimTemplates:
|
||||||
- metadata:
|
- metadata:
|
||||||
name: data
|
name: data
|
||||||
spec:
|
spec:
|
||||||
accessModes:
|
accessModes:
|
||||||
- {{ .Values.persistence.accessMode }}
|
- {{ .Values.vaultwarden.persistence.accessMode }}
|
||||||
{{- if .Values.persistence.storageClass }}
|
{{- if .Values.vaultwarden.persistence.storageClass }}
|
||||||
{{- if eq "-" .Values.persistence.storageClass }}
|
{{- if eq "-" .Values.vaultwarden.persistence.storageClass }}
|
||||||
storageClassName: ""
|
storageClassName: ""
|
||||||
{{- else }}
|
{{- else }}
|
||||||
storageClassName: {{ .Values.persistence.storageClass | quote }}
|
storageClassName: {{ .Values.vaultwarden.persistence.storageClass | quote }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: {{ .Values.persistence.size }}
|
storage: {{ .Values.vaultwarden.persistence.size }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,76 @@
|
||||||
# Configuration is managed in k8s/argo-apps/vaultwarden.yaml
|
vaultwarden:
|
||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: vaultwarden/server
|
repository: vaultwarden/server
|
||||||
pullpolicy: Always
|
pullpolicy: Always
|
||||||
tag: ""
|
tag: ""
|
||||||
|
|
||||||
imagePullSecrets: []
|
imagePullSecrets: []
|
||||||
podAnnotations: {}
|
podAnnotations: {}
|
||||||
podSecurityContext: {}
|
podSecurityContext: {}
|
||||||
securityContext: {}
|
securityContext: {}
|
||||||
|
|
||||||
service:
|
service:
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 80
|
port: 80
|
||||||
targetPort: 80
|
targetPort: 80
|
||||||
|
|
||||||
ingress:
|
ingress:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
conf:
|
config:
|
||||||
db:
|
database:
|
||||||
database_url: ""
|
url: ""
|
||||||
enable_db_wal: true
|
enableWal: true
|
||||||
connection_retries: 15
|
connectionRetries: 15
|
||||||
timeout: 30
|
timeout: 30
|
||||||
idle_timeout: 600
|
idleTimeout: 600
|
||||||
min_conns: 2
|
minConns: 2
|
||||||
max_conns: 10
|
maxConns: 10
|
||||||
conn_init: ""
|
connInit: ""
|
||||||
web_vault:
|
webVault:
|
||||||
|
enabled: true
|
||||||
|
folder: "web-vault/"
|
||||||
|
|
||||||
|
persistence:
|
||||||
enabled: true
|
enabled: true
|
||||||
folder: "web-vault/"
|
storageClass: "truenas-nfs-csi"
|
||||||
|
accessMode: ReadWriteMany
|
||||||
|
size: 5Gi
|
||||||
|
existingClaim: ""
|
||||||
|
|
||||||
persistence:
|
autoscaling:
|
||||||
enabled: true
|
enabled: false
|
||||||
storageClass: "truenas-nfs-csi"
|
|
||||||
accessMode: ReadWriteMany
|
|
||||||
size: 5Gi
|
|
||||||
existingClaim: ""
|
|
||||||
|
|
||||||
autoscaling:
|
resources: {}
|
||||||
enabled: false
|
|
||||||
|
|
||||||
resources: {}
|
livenessProbe:
|
||||||
|
tcpSocket:
|
||||||
|
port: 80
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 15
|
||||||
|
successThreshold: 1
|
||||||
|
failureThreshold: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
|
||||||
livenessProbe:
|
readinessProbe:
|
||||||
tcpSocket:
|
tcpSocket:
|
||||||
port: 80
|
port: 80
|
||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 15
|
||||||
timeoutSeconds: 15
|
timeoutSeconds: 2
|
||||||
successThreshold: 1
|
successThreshold: 1
|
||||||
failureThreshold: 3
|
failureThreshold: 3
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
|
|
||||||
readinessProbe:
|
nodeSelector: {}
|
||||||
tcpSocket:
|
tolerations: []
|
||||||
port: 80
|
affinity: {}
|
||||||
initialDelaySeconds: 15
|
|
||||||
timeoutSeconds: 2
|
|
||||||
successThreshold: 1
|
|
||||||
failureThreshold: 3
|
|
||||||
periodSeconds: 10
|
|
||||||
|
|
||||||
nodeSelector: {}
|
secret:
|
||||||
tolerations: []
|
enabled: true
|
||||||
affinity: {}
|
existingSecretName: "vaultwarden-kubernetes-secrets"
|
||||||
|
data: {}
|
||||||
|
|
||||||
env: {}
|
debug:
|
||||||
|
enabled: false
|
||||||
secret:
|
|
||||||
enabled: true
|
|
||||||
existingSecretName: "vaultwarden-kubernetes-secrets"
|
|
||||||
data: {}
|
|
||||||
|
|
|
||||||
|
|
@ -19,22 +19,10 @@ spec:
|
||||||
- https://git.roboces.dev/catalin/fukuops.git
|
- https://git.roboces.dev/catalin/fukuops.git
|
||||||
- https://charts.goauthentik.io/
|
- https://charts.goauthentik.io/
|
||||||
- https://kubereboot.github.io/charts
|
- 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://portainer.github.io/k8s/
|
||||||
- https://docs.renovatebot.com/helm-charts
|
- https://docs.renovatebot.com/helm-charts
|
||||||
- https://meilisearch.github.io/meilisearch-kubernetes
|
- 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
|
- ghcr.io/antoniolago/charts
|
||||||
- https://helm.elastic.co
|
|
||||||
- https://repo.helmforge.dev
|
- https://repo.helmforge.dev
|
||||||
- https://harish2k01.github.io/helm-charts/
|
- https://harish2k01.github.io/helm-charts/
|
||||||
- git.roboces.dev/catalin/fukuops
|
- git.roboces.dev/catalin/fukuops
|
||||||
- git.roboces.dev/catalin
|
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,3 @@ spec:
|
||||||
- https://git.roboces.dev/catalin/fukuops.git
|
- https://git.roboces.dev/catalin/fukuops.git
|
||||||
- https://democratic-csi.github.io/charts/
|
- https://democratic-csi.github.io/charts/
|
||||||
- https://bitnami-labs.github.io/sealed-secrets
|
- 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
|
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@ spec:
|
||||||
- group: ""
|
- group: ""
|
||||||
kind: PersistentVolume
|
kind: PersistentVolume
|
||||||
sourceRepos:
|
sourceRepos:
|
||||||
- https://git.roboces.dev/catalin/fukuops.git
|
|
||||||
- code.forgejo.org/forgejo-helm
|
- code.forgejo.org/forgejo-helm
|
||||||
- git.roboces.dev/catalin/fukuops
|
- git.roboces.dev/catalin/fukuops
|
||||||
- git.roboces.dev/catalin
|
|
||||||
- https://git.roboces.dev/catalin/huesoporro.git
|
- https://git.roboces.dev/catalin/huesoporro.git
|
||||||
- https://gitlab.com/api/v4/projects/64552889/packages/helm/release
|
|
||||||
|
|
|
||||||
174
scripts/source-repos.sh
Executable file
174
scripts/source-repos.sh
Executable 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
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
terraform {
|
terraform {
|
||||||
required_version = ">= 1.6"
|
required_version = ">= 1.6"
|
||||||
backend "s3" {
|
backend "s3" {
|
||||||
bucket = "fuku-terraform"
|
bucket = "fuku-terraform"
|
||||||
key = "adguard/terraform"
|
key = "adguard/terraform"
|
||||||
region = "us-east-1"
|
region = "us-east-1"
|
||||||
|
use_lockfile = "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
required_providers {
|
required_providers {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
terraform {
|
terraform {
|
||||||
required_version = ">= 1.6"
|
required_version = ">= 1.6"
|
||||||
backend "s3" {
|
backend "s3" {
|
||||||
bucket = "fuku-terraform"
|
bucket = "fuku-terraform"
|
||||||
key = "authentik/terraform"
|
key = "authentik/terraform"
|
||||||
region = "us-east-1"
|
region = "us-east-1"
|
||||||
|
use_lockfile = "true"
|
||||||
}
|
}
|
||||||
required_providers {
|
required_providers {
|
||||||
authentik = {
|
authentik = {
|
||||||
|
|
@ -101,21 +102,6 @@ module "portainer" {
|
||||||
sub_mode = "hashed_user_id"
|
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" {
|
module "sonarr" {
|
||||||
source = "../modules/authentik-proxy"
|
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" {
|
module "cloud" {
|
||||||
source = "../modules/authentik-oidc"
|
source = "../modules/authentik-oidc"
|
||||||
app_name = "Cloud"
|
app_name = "Cloud"
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,5 @@ TF_VAR_rustical_client_id=
|
||||||
TF_VAR_rustical_client_secret=
|
TF_VAR_rustical_client_secret=
|
||||||
TF_VAR_ganymede_client_id=
|
TF_VAR_ganymede_client_id=
|
||||||
TF_VAR_ganymede_client_secret=
|
TF_VAR_ganymede_client_secret=
|
||||||
TF_VAR_oxicloud_client_id=aef61f77326b813cf8d8ba71d1ac994b5642685ca37e4710ab0079e91d87702d55fd9775d473b05aff45603bf08e78dba26850af3a815f3c3ac171d163368aa0
|
TF_VAR_oxicloud_client_id=
|
||||||
TF_VAR_oxicloud_client_secret=a4038df17c9fd06f86372aeaaae8f3fd1374d8978983af7b398d948ef15d1efe522a1faa2fc7652bc410c516d96cd2e4211dad4e05ba6297bdd8d9090460d5fc
|
TF_VAR_oxicloud_client_secret=
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue