v2 app deployment--templating mainly in manifest now.

This commit is contained in:
2025-12-31 06:53:17 +00:00
parent 8818d822cf
commit d1304a2630
84 changed files with 630 additions and 607 deletions

View File

@@ -26,8 +26,9 @@ description: Immich is a self-hosted photo and video backup solution that allows
version: 1.0.0
icon: https://immich.app/assets/images/logo.png
requires:
- name: redis
- name: postgres
- name: pg
alias: db # Use a different reference name in templates
- name: redis # 'alias' and 'installedAs' default to 'name' value
defaultConfig:
serverImage: ghcr.io/immich-app/immich-server:release
mlImage: ghcr.io/immich-app/immich-machine-learning:release
@@ -36,13 +37,21 @@ defaultConfig:
mlPort: 3003
storage: 250Gi
cacheStorage: 10Gi
redisHostname: redis.redis.svc.cluster.local
dbHostname: postgres.postgres.svc.cluster.local
dbUsername: immich
redisHostname: "{{ .apps.redis.host }}" # Can reference 'requires' app configurations
dbHostname: "{{ .apps.pg.host }}"
db: # Configuration can be nested
name: immich
user: immich
host: "{{ .apps.pg.host }}"
port: "{{ .apps.pg.port }}"
domain: immich.{{ .cloud.domain }}
defaultSecrets:
- apps.immich.dbPassword
- apps.postgres.password
- key: password # Random value will be generated if empty
- key: dbUrl
default: "postgresql://{{ .app.db.user }}:{{ .secrets.dbPassword }}@{{ .app.db.host }}:{{ .app.db.port }}/{{ .app.db.name }}?pool=30" # Can reference secrets and config as long as they have been defined before this line. Reference config with {{ .app.? }} and secrets with {{ .secrets.? }}
requiredSecrets:
- db.password # References postgres app via 'db' alias
- redis.auth # References redis app via 'redis' name (no alias)
```
#### Manifest Fields
@@ -53,11 +62,31 @@ defaultSecrets:
| `description` | Yes | Brief app description shown in listings |
| `version` | Yes | App version (follow upstream versioning) |
| `icon` | No | URL to app icon for UI display |
| `requires` | No | List of dependency apps (e.g., `postgres`, `redis`) |
| `requires` | No | List of dependency apps with optional aliases |
| `defaultConfig` | Yes | Default configuration values merged into operator's `config.yaml` |
| `defaultSecrets` | No | List of secrets in dotted-path format (e.g., `apps.appname.dbPassword`) |
| `defaultSecrets` | No | This app's secrets (no 'default' = auto-generated) |
| `requiredSecrets` | No | List of secrets from dependency apps (format: `<app-ref>.<key>`) |
**Important:** All configuration keys referenced in templates (via `{{ .apps.appname.key }}`) must be defined in `defaultConfig` or be standard Wild Cloud variables.
**Dependency Configuration:**
- Each dependency in `requires` can have:
- `name`: The actual app name to depend on
- `alias`: Optional reference name for templates (defaults to `name`)
**Manifest Template Variable Sources:**
1. Standard Wild Cloud variables: `{{ .cloud.* }}`, `{{ .cluster.* }}`, `{{ .operator.* }}`
2. App-specific variables: `{{ .app.* }}` - resolved from current app's config
3. Dependency variables: `{{ .apps.<ref>.* }}` - resolved using app reference mapping
4. App-specific secrets (in 'defaultSecrets' ONLY): `{{ secrets.* }}`
**Manifest App Reference Resolution:**
When you use `{{ .apps.<ref>.* }}` in templates:
1. System checks if `<ref>` matches any dependency's `alias` field
2. If no alias match, checks if `<ref>` matches any dependency's `name` field
3. Uses the `installedAs` value (automatically added when the app is added) to find actual app configuration in `config.yaml`
All manifest template variables must be defined in one of these locations.
**Important:** In the rest of the app templates, ALL configuration keys referenced in templates (via `{{ .key }}`) must be defined in `defaultConfig`. Only the app config is available to app templates.
### Kustomization (`kustomization.yaml`)
@@ -111,21 +140,7 @@ This means individual resources can use simple, component-specific selectors lik
### Gomplate Templating
Resource files in this repository are **templates** that get compiled when users add apps via the web app, CLI, or API. Use gomplate syntax to reference configuration:
```yaml
# Common template variables
domain: {{ .cloud.domain }} # Operator's domain
email: {{ .operator.email }} # Operator's email
image: {{ .apps.myapp.serverImage }} # App-specific config
dbHost: {{ .apps.myapp.dbHostname }} # App-specific config
```
**Template variable sources:**
1. Standard Wild Cloud variables (`{{ .cloud.* }}`, `{{ .operator.* }}`)
2. App-specific variables defined in your manifest's `defaultConfig`
All template variables must be defined in one of these locations. The compiled files are placed in the instance's directory as standard Kubernetes manifests.
Resource files in this repository are **templates** that get compiled when users add apps via the web app, CLI, or API. Only variables defined in the manifest file's 'defaultConfig' section are available to the resource templates. Use gomplate syntax to reference configuration:
### External DNS
@@ -133,12 +148,47 @@ Ingress resources should include external-dns annotations for automatic DNS mana
```yaml
annotations:
external-dns.alpha.kubernetes.io/target: {{ .cloud.domain }}
external-dns.alpha.kubernetes.io/target: {{ .domain }}
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
```
Note: 'domain' must be defined in the app manifest's 'defaultConfig' section.
This creates a CNAME from the app subdomain to the cluster domain (e.g., `myapp.cloud.example.com``cloud.example.com`).
## App Dependencies and Reference Mapping
### How Dependency References Work
When an app depends on other apps, the reference system allows flexibility in naming while maintaining clear relationships:
1. **Define dependencies** in your manifest with optional aliases:
```yaml
requires:
- name: postgres # Actual app to depend on
alias: db # Optional: how to reference it in templates
- name: redis # No alias means use 'redis' as reference
```
2. **At installation time**, the system:
- Prompts user to map dependencies to actual installed apps
- Sets `installedAs` field in the local app manifest to track the mapping
- Example: User might have `postgres-primary` installed, mapped to the `db` dependency
### Example: Multiple Database Instances
If a user has multiple PostgreSQL instances:
```yaml
# User's config.yaml
apps:
postgres-primary:
hostname: primary.postgres.svc.cluster.local
postgres-analytics:
hostname: analytics.postgres.svc.cluster.local
```
When adding an app that requires postgres, they can choose which instance to use, and the system tracks this in the manifest's `installedAs` field.
## Database Patterns
### Database Initialization Jobs
@@ -211,13 +261,16 @@ spec:
### Secrets Management
Secrets use a **full dotted-path naming convention** to prevent naming conflicts:
Secrets are managed through two mechanisms: default secrets for the app itself and required secrets from dependencies.
**In manifest:**
```yaml
defaultSecrets:
- apps.myapp.dbPassword
- apps.postgres.password
key: dbPassword # This app's database password
key: apiKey # This app's API key
requiredSecrets:
- db.password # Password from postgres dependency (aliased as 'db')
- redis.auth # Auth from redis dependency
```
**In resources:**
@@ -227,14 +280,26 @@ env:
valueFrom:
secretKeyRef:
name: myapp-secrets
key: apps.myapp.dbPassword # Full dotted path, not just "dbPassword"
key: dbPassword # Points to the default secret
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-secrets
key: db.password # Points to the required secret
```
**Secret workflow:**
1. List secrets in manifest's `defaultSecrets`
2. When adding an app, the system generates random values in the instance's `secrets.yaml`
3. When deploying, the system creates a Kubernetes Secret named `<app-name>-secrets`
4. Resources reference secrets using full dotted paths
1. Define app's own secrets in `defaultSecrets` (key, default mappings)
2. Reference dependency secrets in `requiredSecrets` (list)
3. When adding an app, the system:
- Generates random values for empty `defaultSecrets`
- Copies referenced secrets from dependencies
- Stores all in the instance's `secrets.yaml`
4. When deploying, creates a Kubernetes Secret named `<app-name>-secrets` containing:
- All `defaultSecrets` with key format: `<key>`
- All `requiredSecrets` with key format: `<app-ref>.<key>`
**Key collision handling:** If the same key exists in both `defaultSecrets` and `requiredSecrets`, the `requiredSecrets` value takes precedence. Authors should ensure their local secrets don't collide with their required secrets.
**Important:** Never commit `secrets.yaml` to Git. Templates should only reference secrets, never contain actual secret values.
@@ -308,9 +373,11 @@ Before submitting a new or modified app, verify:
- [ ] **Manifest**
- [ ] `name` matches directory name
- [ ] All required fields present (`name`, `description`, `version`, `defaultConfig`)
- [ ] All template variables defined in `defaultConfig` or are standard Wild Cloud variables
- [ ] Secrets use dotted-path format (e.g., `apps.appname.secretname`)
- [ ] Dependencies listed in `requires` (if any)
- [ ] All template variables defined in `defaultConfig`
- [ ] `defaultSecrets` uses maps with 'key' and 'default' attributes
- [ ] `requiredSecrets` references use `<app-ref>.<key>` format
- [ ] Dependencies listed in `requires` with optional `alias` fields
- [ ] Manifest template references match dependency aliases or names
- [ ] **Kustomization**
- [ ] Includes standard Wild Cloud labels with `includeSelectors: true`
@@ -318,8 +385,6 @@ Before submitting a new or modified app, verify:
- [ ] All resource files listed under `resources:`
- [ ] **Resources**
- [ ] All hardcoded values replaced with gomplate variables
- [ ] Secrets reference full dotted paths
- [ ] Security contexts on all pods (both pod-level and container-level)
- [ ] Simple component labels, no Helm-style labels
- [ ] Ingresses include external-dns annotations

View File

@@ -1,31 +0,0 @@
---
# Source: discourse/templates/configmaps.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: discourse
namespace: discourse
data:
DISCOURSE_HOSTNAME: "{{ .apps.discourse.domain }}"
DISCOURSE_SKIP_INSTALL: "no"
DISCOURSE_SITE_NAME: "{{ .apps.discourse.siteName }}"
DISCOURSE_USERNAME: "{{ .apps.discourse.adminUsername }}"
DISCOURSE_EMAIL: "{{ .apps.discourse.adminEmail }}"
DISCOURSE_REDIS_HOST: "{{ .apps.discourse.redisHostname }}"
DISCOURSE_REDIS_PORT_NUMBER: "6379"
DISCOURSE_DATABASE_HOST: "{{ .apps.discourse.dbHostname }}"
DISCOURSE_DATABASE_PORT_NUMBER: "5432"
DISCOURSE_DATABASE_NAME: "{{ .apps.discourse.dbName }}"
DISCOURSE_DATABASE_USER: "{{ .apps.discourse.dbUsername }}"
DISCOURSE_SMTP_HOST: "{{ .apps.discourse.smtp.host }}"
DISCOURSE_SMTP_PORT: "{{ .apps.discourse.smtp.port }}"
DISCOURSE_SMTP_USER: "{{ .apps.discourse.smtp.user }}"
DISCOURSE_SMTP_PROTOCOL: "tls"
DISCOURSE_SMTP_AUTH: "login"
# DISCOURSE_PRECOMPILE_ASSETS: "false"
# DISCOURSE_SKIP_INSTALL: "no"
# DISCOURSE_SKIP_BOOTSTRAP: "yes"

View File

@@ -27,7 +27,7 @@ spec:
readOnlyRootFilesystem: false
env:
- name: PGHOST
value: "{{ .apps.discourse.dbHostname }}"
value: "{{ .dbHostname }}"
- name: PGPORT
value: "5432"
- name: PGUSER
@@ -36,16 +36,16 @@ spec:
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.postgres.password
key: postgres.password
- name: DISCOURSE_DB_USER
value: "{{ .apps.discourse.dbUsername }}"
value: "{{ .dbUsername }}"
- name: DISCOURSE_DB_NAME
value: "{{ .apps.discourse.dbName }}"
value: "{{ .dbName }}"
- name: DISCOURSE_DB_PASSWORD
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.dbPassword
key: dbPassword
command:
- /bin/sh
- -c

View File

@@ -1,5 +1,4 @@
---
# Source: discourse/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -18,219 +17,133 @@ spec:
component: web
spec:
automountServiceAccountToken: false
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchLabels:
component: web
topologyKey: kubernetes.io/hostname
weight: 1
serviceAccountName: discourse
securityContext:
fsGroup: 0
fsGroupChangePolicy: Always
supplementalGroups: []
sysctls: []
initContainers:
containers:
- name: discourse
image: docker.io/bitnami/discourse:3.4.7-debian-12-r0
image: tiredofit/discourse:latest
imagePullPolicy: "IfNotPresent"
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
add:
- CHOWN
- SYS_CHROOT
- DAC_OVERRIDE
- FOWNER
- SETGID
- SETUID
- DAC_OVERRIDE
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsGroup: 0
runAsNonRoot: false
runAsUser: 0
seLinuxOptions: {}
seccompProfile:
type: RuntimeDefault
env:
- name: BITNAMI_DEBUG
value: "false"
- name: DISCOURSE_PASSWORD
# Admin configuration
- name: ADMIN_USER
value: {{ .adminUsername }}
- name: ADMIN_EMAIL
value: {{ .adminEmail }}
- name: ADMIN_PASS
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.adminPassword
- name: DISCOURSE_PORT_NUMBER
value: "8080"
- name: DISCOURSE_EXTERNAL_HTTP_PORT_NUMBER
value: "80"
- name: DISCOURSE_DATABASE_PASSWORD
key: adminPassword
# Site configuration
- name: SITE_TITLE
value: {{ .siteName }}
- name: HOSTNAME
value: {{ .domain }}
# Database configuration
- name: DB_HOST
value: {{ .dbHostname }}
- name: DB_PORT
value: "{{ .dbPort }}"
- name: DB_NAME
value: {{ .dbName }}
- name: DB_USER
value: {{ .dbUsername }}
- name: DB_PASS
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.dbPassword
- name: POSTGRESQL_CLIENT_CREATE_DATABASE_PASSWORD
key: dbPassword
# Redis configuration
- name: REDIS_HOST
value: {{ .redisHostname }}
- name: REDIS_PASS
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.dbPassword
- name: DISCOURSE_REDIS_PASSWORD
key: redis.password
# SMTP configuration
- name: SMTP_ENABLED
value: "{{ .smtp.enabled }}"
- name: SMTP_HOST
value: {{ .smtp.host }}
- name: SMTP_PORT
value: "{{ .smtp.port }}"
- name: SMTP_USER
value: {{ .smtp.user }}
- name: SMTP_PASS
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.redis.password
- name: DISCOURSE_SECRET_KEY_BASE
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.secretKeyBase
- name: DISCOURSE_SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.smtpPassword
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.smtpPassword
envFrom:
- configMapRef:
name: discourse
key: smtpPassword
- name: SMTP_TLS
value: "{{ .smtp.tls }}"
# Container timezone
- name: TZ
value: {{ .timezone }}
ports:
- name: http
containerPort: 8080
containerPort: 3000
protocol: TCP
livenessProbe:
tcpSocket:
httpGet:
path: /
port: http
initialDelaySeconds: 500
periodSeconds: 10
timeoutSeconds: 5
initialDelaySeconds: 420
periodSeconds: 30
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 6
readinessProbe:
httpGet:
path: /srv/status
path: /
port: http
initialDelaySeconds: 180
periodSeconds: 10
timeoutSeconds: 5
initialDelaySeconds: 360
periodSeconds: 30
timeoutSeconds: 10
successThreshold: 1
failureThreshold: 6
resources:
limits:
cpu: 1
ephemeral-storage: 2Gi
memory: 8Gi # for precompiling assets!
cpu: 2000m
ephemeral-storage: 10Gi
memory: 4Gi
requests:
cpu: 750m
cpu: 500m
ephemeral-storage: 50Mi
memory: 1Gi
volumeMounts:
- name: discourse-data
mountPath: /bitnami/discourse
subPath: discourse
- name: sidekiq
image: docker.io/bitnami/discourse:3.4.7-debian-12-r0
imagePullPolicy: "IfNotPresent"
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- CHOWN
- SYS_CHROOT
- FOWNER
- SETGID
- SETUID
- DAC_OVERRIDE
drop:
- ALL
privileged: false
readOnlyRootFilesystem: false
runAsGroup: 0
runAsNonRoot: false
runAsUser: 0
seLinuxOptions: {}
seccompProfile:
type: RuntimeDefault
command:
- /opt/bitnami/scripts/discourse/entrypoint.sh
args:
- /opt/bitnami/scripts/discourse-sidekiq/run.sh
env:
- name: BITNAMI_DEBUG
value: "false"
- name: DISCOURSE_PASSWORD
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.adminPassword
- name: DISCOURSE_POSTGRESQL_PASSWORD
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.dbPassword
- name: DISCOURSE_REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.redis.password
- name: DISCOURSE_SECRET_KEY_BASE
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.secretKeyBase
- name: DISCOURSE_SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.smtpPassword
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: discourse-secrets
key: apps.discourse.smtpPassword
envFrom:
- configMapRef:
name: discourse
livenessProbe:
exec:
command: ["/bin/sh", "-c", "pgrep -f ^sidekiq"]
initialDelaySeconds: 500
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 6
readinessProbe:
exec:
command: ["/bin/sh", "-c", "pgrep -f ^sidekiq"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 6
resources:
limits:
cpu: 500m
ephemeral-storage: 2Gi
memory: 768Mi
requests:
cpu: 375m
ephemeral-storage: 50Mi
memory: 512Mi
volumeMounts:
- name: discourse-data
mountPath: /bitnami/discourse
subPath: discourse
- name: discourse-logs
mountPath: /data/logs
- name: discourse-uploads
mountPath: /data/uploads
- name: discourse-backups
mountPath: /data/backups
volumes:
- name: discourse-data
- name: discourse-logs
persistentVolumeClaim:
claimName: discourse
claimName: discourse-logs
- name: discourse-uploads
persistentVolumeClaim:
claimName: discourse-uploads
- name: discourse-backups
persistentVolumeClaim:
claimName: discourse-backups

View File

@@ -4,13 +4,13 @@ apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: discourse
namespace: "discourse"
namespace: "{{ .namespace }}"
annotations:
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
external-dns.alpha.kubernetes.io/target: "{{ .cloud.domain }}"
external-dns.alpha.kubernetes.io/target: "{{ .externalDnsDomain }}"
spec:
rules:
- host: "{{ .apps.discourse.domain }}"
- host: "{{ .domain }}"
http:
paths:
- path: /
@@ -22,5 +22,5 @@ spec:
name: http
tls:
- hosts:
- "{{ .apps.discourse.domain }}"
- "{{ .domain }}"
secretName: wildcard-external-wild-cloud-tls

View File

@@ -10,7 +10,6 @@ labels:
resources:
- namespace.yaml
- serviceaccount.yaml
- configmap.yaml
- pvc.yaml
- deployment.yaml
- service.yaml

View File

@@ -1,22 +1,25 @@
name: discourse
description: Discourse is a modern, open-source discussion platform designed for online communities and forums.
version: 3.4.7
icon: https://www.discourse.org/img/icon.png
version: 3.5.3
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/discourse.svg
requires:
- name: postgres
- name: redis
defaultConfig:
namespace: discourse
externalDnsDomain: "{{ .cloud.domain }}"
timezone: UTC
port: 8080
port: 3000
storage: 10Gi
adminEmail: admin@{{ .cloud.domain }}
adminEmail: "{{ .operator.email }}"
adminUsername: admin
siteName: "Community"
domain: discourse.{{ .cloud.domain }}
dbHostname: postgres.postgres.svc.cluster.local
dbHostname: "{{ .apps.postgres.host }}"
dbPort: "{{ .apps.postgres.port }}"
dbUsername: discourse
dbName: discourse
redisHostname: redis.redis.svc.cluster.local
redisHostname: "{{ .apps.redis.host }}"
tlsSecretName: wildcard-wild-cloud-tls
smtp:
enabled: false
@@ -24,13 +27,16 @@ defaultConfig:
port: "{{ .cloud.smtp.port }}"
user: "{{ .cloud.smtp.user }}"
from: "{{ .cloud.smtp.from }}"
tls: {{ .cloud.smtp.tls }}
startTls: {{ .cloud.smtp.startTls }}
tls: "{{ .cloud.smtp.tls }}"
startTls: "{{ .cloud.smtp.startTls }}"
defaultSecrets:
- - key: apps.discourse.adminPassword
- - key: apps.discourse.dbPassword
- - key: apps.discourse.dbUrl
- - key: apps.redis.password
- - key: apps.discourse.secretKeyBase
- - key: apps.discourse.smtpPassword
- - key: apps.postgres.password
- key: adminPassword
- key: secretKeyBase
default: "{{ random.AlphaNum 64 }}"
- key: smtpPassword
- key: dbPassword
- key: dbUrl
default: "postgres://{{ .app.dbUsername }}:{{ .secrets.dbPassword }}@{{ .app.dbHostname }}:{{ .app.dbPort }}/{{ .app.dbName }}?sslmode=disable"
requiredSecrets:
- postgres.password
- redis.password

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: discourse
name: "{{ .namespace }}"

View File

@@ -1,13 +1,39 @@
---
# Source: discourse/templates/pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: discourse
name: discourse-logs
namespace: discourse
spec:
accessModes:
- "ReadWriteOnce"
- ReadWriteOnce
resources:
requests:
storage: "{{ .apps.discourse.storage }}"
storage: 2Gi
storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: discourse-uploads
namespace: discourse
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .storage }}
storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: discourse-backups
namespace: discourse
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: longhorn

View File

@@ -3,7 +3,6 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: example-admin
namespace: example-admin
labels:
app: example-admin
spec:

View File

@@ -3,10 +3,9 @@ apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-admin
namespace: example-admin
spec:
rules:
- host: example-admin.{{ .cloud.internalDomain }}
- host: "{{ .host }}"
http:
paths:
- path: /
@@ -18,5 +17,5 @@ spec:
number: 80
tls:
- hosts:
- example-admin.{{ .cloud.internalDomain }}
- "{{ .host }}"
secretName: wildcard-internal-wild-cloud-tls

View File

@@ -3,4 +3,7 @@ install: true
description: An example application that is deployed with internal-only access.
version: 1.0.0
defaultConfig:
namespace: example-admin
externalDnsDomain: '{{ .cloud.domain }}'
host: '{{ .host }}'
tlsSecretName: wildcard-internal-wild-cloud-tls

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: example-admin
name: "{{ .namespace }}"

View File

@@ -4,7 +4,7 @@ kind: Ingress
metadata:
name: example-app
annotations:
external-dns.alpha.kubernetes.io/target: {{ .cloud.domain }}
external-dns.alpha.kubernetes.io/target: "{{ .cloud.externalDnsTarget }}"
external-dns.alpha.kubernetes.io/cloudflare-proxied: false
# Optional: Enable HTTPS redirection
@@ -15,7 +15,7 @@ metadata:
# traefik.ingress.kubernetes.io/auth-secret: basic-auth
spec:
rules:
- host: example-app.{{ .cloud.domain }}
- host: "{{ .host }}"
http:
paths:
- path: /
@@ -27,5 +27,5 @@ spec:
number: 80
tls:
- hosts:
- example-app.{{ .cloud.domain }}
- "{{ .host }}"
secretName: wildcard-wild-cloud-tls

View File

@@ -3,4 +3,7 @@ install: true
description: An example application that is deployed with public access.
version: 1.0.0
defaultConfig:
namespace: example-app
externalDnsDomain: '{{ .cloud.domain }}'
host: example-app.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: example-app
name: "{{ .namespace }}"

View File

@@ -12,7 +12,7 @@ spec:
spec:
containers:
- name: db-init
image: {{ .apps.mysql.image }}
image: mysql:9.1.0
command: ["/bin/bash", "-c"]
args:
- |
@@ -27,18 +27,18 @@ spec:
valueFrom:
secretKeyRef:
name: mysql-secrets
key: apps.mysql.rootPassword
key: rootPassword
- name: DB_HOSTNAME
value: "{{ .apps.ghost.dbHost }}"
value: "{{ .dbHost }}"
- name: DB_PORT
value: "{{ .apps.ghost.dbPort }}"
value: "{{ .dbPort }}"
- name: DB_DATABASE_NAME
value: "{{ .apps.ghost.dbName }}"
value: "{{ .dbName }}"
- name: DB_USERNAME
value: "{{ .apps.ghost.dbUser }}"
value: "{{ .dbUser }}"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: ghost-secrets
key: apps.ghost.dbPassword
key: dbPassword
restartPolicy: OnFailure

View File

@@ -17,10 +17,10 @@ spec:
spec:
containers:
- name: ghost
image: {{ .apps.ghost.image }}
image: {{ .image }}
ports:
- name: http
containerPort: {{ .apps.ghost.port }}
containerPort: {{ .port }}
protocol: TCP
env:
- name: BITNAMI_DEBUG
@@ -28,33 +28,33 @@ spec:
- name: ALLOW_EMPTY_PASSWORD
value: "yes"
- name: GHOST_DATABASE_HOST
value: {{ .apps.ghost.dbHost }}
value: {{ .dbHost }}
- name: GHOST_DATABASE_PORT_NUMBER
value: "{{ .apps.ghost.dbPort }}"
value: "{{ .dbPort }}"
- name: GHOST_DATABASE_NAME
value: {{ .apps.ghost.dbName }}
value: {{ .dbName }}
- name: GHOST_DATABASE_USER
value: {{ .apps.ghost.dbUser }}
value: {{ .dbUser }}
- name: GHOST_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: ghost-secrets
key: apps.ghost.dbPassword
key: dbPassword
- name: GHOST_HOST
value: {{ .apps.ghost.domain }}
value: {{ .domain }}
- name: GHOST_PORT_NUMBER
value: "{{ .apps.ghost.port }}"
value: "{{ .port }}"
- name: GHOST_USERNAME
value: {{ .apps.ghost.adminUser }}
value: {{ .adminUser }}
- name: GHOST_PASSWORD
valueFrom:
secretKeyRef:
name: ghost-secrets
key: apps.ghost.adminPassword
key: adminPassword
- name: GHOST_EMAIL
value: {{ .apps.ghost.adminEmail }}
value: {{ .adminEmail }}
- name: GHOST_BLOG_TITLE
value: {{ .apps.ghost.blogTitle }}
value: {{ .blogTitle }}
- name: GHOST_ENABLE_HTTPS
value: "yes"
- name: GHOST_EXTERNAL_HTTP_PORT_NUMBER
@@ -66,18 +66,18 @@ spec:
- name: GHOST_SMTP_SERVICE
value: SMTP
- name: GHOST_SMTP_HOST
value: {{ .apps.ghost.smtp.host }}
value: {{ .smtp.host }}
- name: GHOST_SMTP_PORT
value: "{{ .apps.ghost.smtp.port }}"
value: "{{ .smtp.port }}"
- name: GHOST_SMTP_USER
value: {{ .apps.ghost.smtp.user }}
value: {{ .smtp.user }}
- name: GHOST_SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: ghost-secrets
key: apps.ghost.smtpPassword
key: smtpPassword
- name: GHOST_SMTP_FROM_ADDRESS
value: {{ .apps.ghost.smtp.from }}
value: {{ .smtp.from }}
resources:
limits:
cpu: 375m
@@ -92,7 +92,7 @@ spec:
mountPath: /bitnami/ghost
livenessProbe:
tcpSocket:
port: {{ .apps.ghost.port }}
port: {{ .port }}
initialDelaySeconds: 120
timeoutSeconds: 5
periodSeconds: 10

View File

@@ -7,12 +7,12 @@ metadata:
kubernetes.io/ingress.class: "traefik"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
external-dns.alpha.kubernetes.io/target: {{ .cloud.domain }}
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
external-dns.alpha.kubernetes.io/ttl: "60"
traefik.ingress.kubernetes.io/redirect-entry-point: https
spec:
rules:
- host: {{ .apps.ghost.domain }}
- host: {{ .domain }}
http:
paths:
- path: /
@@ -24,5 +24,5 @@ spec:
number: 80
tls:
- hosts:
- {{ .apps.ghost.domain }}
secretName: {{ .apps.ghost.tlsSecretName }}
- {{ .domain }}
secretName: {{ .tlsSecretName }}

View File

@@ -1,10 +1,13 @@
name: ghost
description: Ghost is a powerful app for new-media creators to publish, share, and grow a business around their content.
description: Ghost is a powerful app for new-media creators to publish, share, and
grow a business around their content.
version: 5.118.1
icon: https://ghost.org/images/logos/ghost-logo-orb.png
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/ghost.png
requires:
- name: mysql
- name: mysql
defaultConfig:
namespace: ghost
externalDnsDomain: '{{ .cloud.domain }}'
image: docker.io/bitnami/ghost:5.118.1-debian-12-r0
domain: ghost.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls
@@ -15,16 +18,17 @@ defaultConfig:
dbName: ghost
dbUser: ghost
adminUser: admin
adminEmail: "admin@{{ .cloud.domain }}"
blogTitle: "My Blog"
adminEmail: {{ .operator.email }}
blogTitle: My Blog
timezone: UTC
tlsSecretName: wildcard-wild-cloud-tls
smtp:
host: "{{ .cloud.smtp.host }}"
port: "{{ .cloud.smtp.port }}"
from: "{{ .cloud.smtp.from }}"
user: "{{ .cloud.smtp.user }}"
host: '{{ .cloud.smtp.host }}'
port: '{{ .cloud.smtp.port }}'
from: '{{ .cloud.smtp.from }}'
user: '{{ .cloud.smtp.user }}'
defaultSecrets:
- key: apps.ghost.adminPassword
- key: apps.ghost.dbPassword
- key: apps.ghost.smtpPassword
- key: adminPassword
- key: dbPassword
- key: smtpPassword
requiredSecrets:
- mysql.rootPassword

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: ghost
name: "{{ .namespace }}"

View File

@@ -8,4 +8,4 @@ spec:
- ReadWriteOnce
resources:
requests:
storage: {{ .apps.ghost.storage }}
storage: {{ .storage }}

View File

@@ -9,6 +9,6 @@ spec:
- name: http
port: 80
protocol: TCP
targetPort: {{ .apps.ghost.port }}
targetPort: {{ .port }}
selector:
component: web

View File

@@ -12,7 +12,7 @@ spec:
spec:
containers:
- name: db-init
image: {{ .apps.postgres.image }}
image: postgres:17
command: ["/bin/bash", "-c"]
args:
- |
@@ -36,16 +36,16 @@ spec:
valueFrom:
secretKeyRef:
name: postgres-secrets
key: apps.postgres.password
key: password
- name: DB_HOSTNAME
value: "{{ .apps.gitea.dbHost }}"
value: "{{ .dbHost }}"
- name: DB_DATABASE_NAME
value: "{{ .apps.gitea.dbName }}"
value: "{{ .dbName }}"
- name: DB_USERNAME
value: "{{ .apps.gitea.dbUser }}"
value: "{{ .dbUser }}"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: gitea-secrets
key: apps.gitea.dbPassword
key: dbPassword
restartPolicy: OnFailure

View File

@@ -23,7 +23,7 @@ spec:
terminationGracePeriodSeconds: 60
containers:
- name: gitea
image: "{{ .apps.gitea.image }}"
image: "{{ .image }}"
imagePullPolicy: IfNotPresent
envFrom:
- configMapRef:
@@ -33,27 +33,27 @@ spec:
valueFrom:
secretKeyRef:
name: gitea-secrets
key: apps.gitea.adminPassword
key: adminPassword
- name: GITEA__security__SECRET_KEY
valueFrom:
secretKeyRef:
name: gitea-secrets
key: apps.gitea.secretKey
key: secretKey
- name: GITEA__security__INTERNAL_TOKEN
valueFrom:
secretKeyRef:
name: gitea-secrets
key: apps.gitea.jwtSecret
key: jwtSecret
- name: GITEA__database__PASSWD
valueFrom:
secretKeyRef:
name: gitea-secrets
key: apps.gitea.dbPassword
key: dbPassword
- name: GITEA__mailer__PASSWD
valueFrom:
secretKeyRef:
name: gitea-secrets
key: apps.gitea.smtpPassword
key: smtpPassword
ports:
- name: ssh
containerPort: 2222

View File

@@ -3,12 +3,12 @@ SSH_PORT=22
GITEA_WORK_DIR=/data
GITEA_TEMP=/tmp/gitea
TMPDIR=/tmp/gitea
GITEA_ADMIN_USERNAME={{ .apps.gitea.adminUser }}
GITEA_ADMIN_USERNAME={{ .adminUser }}
GITEA_ADMIN_PASSWORD_MODE=keepUpdated
# Core app settings
GITEA____APP_NAME={{ .apps.gitea.appName }}
GITEA____RUN_MODE={{ .apps.gitea.runMode }}
GITEA____APP_NAME={{ .appName }}
GITEA____RUN_MODE={{ .runMode }}
GITEA____RUN_USER=git
# Security settings
@@ -17,19 +17,19 @@ GITEA__security__PASSWORD_HASH_ALGO=pbkdf2
# Database settings (except password which comes from secret)
GITEA__database__DB_TYPE=postgres
GITEA__database__HOST={{ .apps.gitea.dbHost }}:{{ .apps.gitea.dbPort }}
GITEA__database__NAME={{ .apps.gitea.dbName }}
GITEA__database__USER={{ .apps.gitea.dbUser }}
GITEA__database__HOST={{ .dbHost }}:{{ .dbPort }}
GITEA__database__NAME={{ .dbName }}
GITEA__database__USER={{ .dbUser }}
GITEA__database__SSL_MODE=disable
GITEA__database__LOG_SQL=false
# Server settings
GITEA__server__DOMAIN={{ .apps.gitea.domain }}
GITEA__server__HTTP_PORT={{ .apps.gitea.port }}
GITEA__server__ROOT_URL=https://{{ .apps.gitea.domain }}/
GITEA__server__DOMAIN={{ .domain }}
GITEA__server__HTTP_PORT={{ .port }}
GITEA__server__ROOT_URL=https://{{ .domain }}/
GITEA__server__DISABLE_SSH=false
GITEA__server__SSH_DOMAIN={{ .apps.gitea.domain }}
GITEA__server__SSH_PORT={{ .apps.gitea.sshPort }}
GITEA__server__SSH_DOMAIN={{ .domain }}
GITEA__server__SSH_PORT={{ .sshPort }}
GITEA__server__SSH_LISTEN_PORT=2222
GITEA__server__LFS_START_SERVER=true
GITEA__server__OFFLINE_MODE=true
@@ -53,8 +53,8 @@ GITEA__webhook__ALLOWED_HOST_LIST=*
# Mailer settings (enabled via env vars, password from secret)
GITEA__mailer__ENABLED=true
GITEA__mailer__SMTP_ADDR={{ .apps.gitea.smtp.host }}
GITEA__mailer__SMTP_PORT={{ .apps.gitea.smtp.port }}
GITEA__mailer__FROM={{ .apps.gitea.smtp.from }}
GITEA__mailer__USER={{ .apps.gitea.smtp.user }}
GITEA__mailer__SMTP_ADDR={{ .smtp.host }}
GITEA__mailer__SMTP_PORT={{ .smtp.port }}
GITEA__mailer__FROM={{ .smtp.from }}
GITEA__mailer__USER={{ .smtp.user }}

View File

@@ -5,10 +5,10 @@ metadata:
namespace: gitea
annotations:
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
external-dns.alpha.kubernetes.io/target: "{{ .cloud.domain }}"
external-dns.alpha.kubernetes.io/target: "{{ .externalDnsDomain }}"
spec:
rules:
- host: "{{ .apps.gitea.domain }}"
- host: "{{ .domain }}"
http:
paths:
- path: /
@@ -19,6 +19,6 @@ spec:
port:
number: 3000
tls:
- secretName: "{{ .apps.gitea.tlsSecretName }}"
- secretName: "{{ .tlsSecretName }}"
hosts:
- "{{ .apps.gitea.domain }}"
- "{{ .domain }}"

View File

@@ -1,10 +1,12 @@
name: gitea
description: Gitea is a painless self-hosted Git service written in Go
version: 1.24.3
icon: https://github.com/go-gitea/gitea/raw/main/assets/logo.png
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg
requires:
- name: postgres
- name: postgres
defaultConfig:
namespace: gitea
externalDnsDomain: '{{ .cloud.domain }}'
image: gitea/gitea:1.24.3
appName: Gitea
domain: gitea.{{ .cloud.domain }}
@@ -16,18 +18,20 @@ defaultConfig:
dbUser: gitea
dbHost: postgres.postgres.svc.cluster.local
adminUser: admin
adminEmail: "admin@{{ .cloud.domain }}"
adminEmail: "{{ .operator.email }}"
dbPort: 5432
timezone: UTC
runMode: prod
smtp:
host: "{{ .cloud.smtp.host }}"
port: "{{ .cloud.smtp.port }}"
user: "{{ .cloud.smtp.user }}"
from: "{{ .cloud.smtp.from }}"
host: '{{ .cloud.smtp.host }}'
port: '{{ .cloud.smtp.port }}'
user: '{{ .cloud.smtp.user }}'
from: '{{ .cloud.smtp.from }}'
defaultSecrets:
- key: apps.gitea.adminPassword
- key: apps.gitea.dbPassword
- key: apps.gitea.secretKey
- key: apps.gitea.jwtSecret
- key: apps.gitea.smtpPassword
- key: adminPassword
- key: dbPassword
- key: secretKey
- key: jwtSecret
- key: smtpPassword
requiredSecrets:
- postgres.password

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: gitea
name: "{{ .namespace }}"

View File

@@ -9,4 +9,4 @@ spec:
storageClassName: longhorn
resources:
requests:
storage: "{{ .apps.gitea.storage }}"
storage: "{{ .storage }}"

View File

@@ -8,7 +8,7 @@ spec:
ports:
- name: http
port: 3000
targetPort: {{ .apps.gitea.port }}
targetPort: {{ .port }}
selector:
component: web
---
@@ -21,7 +21,7 @@ spec:
type: LoadBalancer
ports:
- name: ssh
port: {{ .apps.gitea.sshPort }}
port: {{ .sshPort }}
targetPort: 2222
protocol: TCP
selector:

View File

@@ -7,7 +7,7 @@ spec:
spec:
containers:
- name: db-init
image: {{ .apps.postgres.image }}
image: postgres:17
command: ["/bin/bash", "-c"]
args:
- |
@@ -53,16 +53,16 @@ spec:
valueFrom:
secretKeyRef:
name: immich-secrets
key: apps.postgres.password
key: postgres.password
- name: DB_HOSTNAME
value: "{{ .apps.immich.dbHostname }}"
value: "{{ .dbHostname }}"
- name: DB_DATABASE_NAME
value: "{{ .apps.immich.dbUsername }}"
value: "{{ .dbUsername }}"
- name: DB_USERNAME
value: "{{ .apps.immich.dbUsername }}"
value: "{{ .dbUsername }}"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: immich-secrets
key: apps.immich.dbPassword
key: dbPassword
restartPolicy: OnFailure

View File

@@ -15,14 +15,14 @@ spec:
component: machine-learning
spec:
containers:
- image: "{{ .apps.immich.mlImage }}"
- image: "{{ .mlImage }}"
name: immich-machine-learning
ports:
- containerPort: {{ .apps.immich.mlPort }}
- containerPort: {{ .mlPort }}
protocol: TCP
env:
- name: TZ
value: "{{ .apps.immich.timezone }}"
value: "{{ .timezone }}"
volumeMounts:
- mountPath: /cache
name: immich-cache

View File

@@ -20,27 +20,27 @@ spec:
component: microservices
spec:
containers:
- image: "{{ .apps.immich.serverImage }}"
- image: "{{ .serverImage }}"
name: immich-microservices
env:
- name: REDIS_HOSTNAME
value: "{{ .apps.immich.redisHostname }}"
value: "{{ .redisHostname }}"
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: immich-secrets
key: apps.redis.password
key: redis.password
- name: DB_HOSTNAME
value: "{{ .apps.immich.dbHostname }}"
value: "{{ .dbHostname }}"
- name: DB_USERNAME
value: "{{ .apps.immich.dbUsername }}"
value: "{{ .dbUsername }}"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: immich-secrets
key: apps.immich.dbPassword
key: dbPassword
- name: TZ
value: "{{ .apps.immich.timezone }}"
value: "{{ .timezone }}"
- name: IMMICH_WORKERS_EXCLUDE
value: api
volumeMounts:

View File

@@ -20,30 +20,30 @@ spec:
component: server
spec:
containers:
- image: "{{ .apps.immich.serverImage }}"
- image: "{{ .serverImage }}"
name: immich-server
ports:
- containerPort: {{ .apps.immich.serverPort }}
- containerPort: {{ .serverPort }}
protocol: TCP
env:
- name: REDIS_HOSTNAME
value: "{{ .apps.immich.redisHostname }}"
value: "{{ .redisHostname }}"
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: immich-secrets
key: apps.redis.password
key: redis.password
- name: DB_HOSTNAME
value: "{{ .apps.immich.dbHostname }}"
value: "{{ .dbHostname }}"
- name: DB_USERNAME
value: "{{ .apps.immich.dbUsername }}"
value: "{{ .dbUsername }}"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: immich-secrets
key: apps.immich.dbPassword
key: dbPassword
- name: TZ
value: "{{ .apps.immich.timezone }}"
value: "{{ .timezone }}"
- name: IMMICH_WORKERS_EXCLUDE
value: microservices
volumeMounts:

View File

@@ -4,11 +4,11 @@ kind: Ingress
metadata:
name: immich-public
annotations:
external-dns.alpha.kubernetes.io/target: "{{ .cloud.domain }}"
external-dns.alpha.kubernetes.io/target: "{{ .externalDnsDomain }}"
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
spec:
rules:
- host: "{{ .apps.immich.domain }}"
- host: "{{ .domain }}"
http:
paths:
- path: /
@@ -21,4 +21,4 @@ spec:
tls:
- secretName: wildcard-wild-cloud-tls
hosts:
- "{{ .apps.immich.domain }}"
- "{{ .domain }}"

View File

@@ -1,12 +1,15 @@
name: immich
install: true
description: Immich is a self-hosted photo and video backup solution that allows you to store, manage, and share your media files securely.
version: 1.0.0
icon: https://immich.app/assets/images/logo.png
description: Immich is a self-hosted photo and video backup solution that allows you
to store, manage, and share your media files securely.
version: release
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg
requires:
- name: redis
- name: postgres
- name: redis
- name: postgres
defaultConfig:
namespace: immich
externalDnsDomain: '{{ .cloud.domain }}'
serverImage: ghcr.io/immich-app/immich-server:release
mlImage: ghcr.io/immich-app/immich-machine-learning:release
timezone: UTC
@@ -20,6 +23,7 @@ defaultConfig:
domain: immich.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls
defaultSecrets:
- key: apps.immich.dbPassword
- key: apps.postgres.password
- key: apps.redis.password
- key: dbPassword
requiredSecrets:
- redis.password
- postgres.password

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: immich
name: "{{ .namespace }}"

View File

@@ -9,7 +9,7 @@ spec:
- ReadWriteOnce
resources:
requests:
storage: {{ .apps.immich.storage }}
storage: {{ .storage }}
---
apiVersion: v1
kind: PersistentVolumeClaim
@@ -21,4 +21,4 @@ spec:
- ReadWriteOnce
resources:
requests:
storage: {{ .apps.immich.cacheStorage }}
storage: {{ .cacheStorage }}

View File

@@ -9,7 +9,7 @@ metadata:
spec:
ports:
- port: 3001
targetPort: {{ .apps.immich.serverPort }}
targetPort: {{ .serverPort }}
selector:
app: immich
component: server
@@ -25,7 +25,7 @@ metadata:
app: immich-machine-learning
spec:
ports:
- port: {{ .apps.immich.mlPort }}
- port: {{ .mlPort }}
selector:
app: immich
component: machine-learning

View File

@@ -26,23 +26,23 @@ spec:
readOnlyRootFilesystem: false
env:
- name: PGHOST
value: {{ .apps.keila.dbHostname }}
value: {{ .dbHostname }}
- name: PGUSER
value: postgres
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: keila-secrets
key: apps.postgres.password
key: postgres.password
- name: DB_NAME
value: {{ .apps.keila.dbName }}
value: {{ .dbName }}
- name: DB_USER
value: {{ .apps.keila.dbUsername }}
value: {{ .dbUsername }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: keila-secrets
key: apps.keila.dbPassword
key: dbPassword
command:
- /bin/bash
- -c

View File

@@ -14,54 +14,54 @@ spec:
spec:
containers:
- name: keila
image: {{ .apps.keila.image }}
image: "{{ .image }}"
ports:
- containerPort: {{ .apps.keila.port }}
- containerPort: {{ .port }}
env:
- name: DB_URL
valueFrom:
secretKeyRef:
name: keila-secrets
key: apps.keila.dbUrl
key: dbUrl
- name: URL_HOST
value: {{ .apps.keila.domain }}
value: "{{ .domain }}"
- name: URL_SCHEMA
value: https
- name: URL_PORT
value: "443"
- name: PORT
value: "{{ .apps.keila.port }}"
value: "{{ .port }}"
- name: SECRET_KEY_BASE
valueFrom:
secretKeyRef:
name: keila-secrets
key: apps.keila.secretKeyBase
key: secretKeyBase
- name: MAILER_SMTP_HOST
value: {{ .apps.keila.smtp.host }}
value: "{{ .smtp.host }}"
- name: MAILER_SMTP_PORT
value: "{{ .apps.keila.smtp.port }}"
value: "{{ .smtp.port }}"
- name: MAILER_ENABLE_SSL
value: "{{ .apps.keila.smtp.tls }}"
value: "{{ .smtp.tls }}"
- name: MAILER_ENABLE_STARTTLS
value: "{{ .apps.keila.smtp.startTls }}"
value: "{{ .smtp.startTls }}"
- name: MAILER_SMTP_USER
value: {{ .apps.keila.smtp.user }}
value: "{{ .smtp.user }}"
- name: MAILER_SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: keila-secrets
key: apps.keila.smtpPassword
key: smtpPassword
- name: MAILER_SMTP_FROM_EMAIL
value: {{ .apps.keila.smtp.from }}
value: "{{ .smtp.from }}"
- name: DISABLE_REGISTRATION
value: "{{ .apps.keila.disableRegistration }}"
value: "{{ .disableRegistration }}"
- name: KEILA_USER
value: "{{ .apps.keila.adminUser }}"
value: "{{ .adminUser }}"
- name: KEILA_PASSWORD
valueFrom:
secretKeyRef:
name: keila-secrets
key: apps.keila.adminPassword
key: adminPassword
- name: USER_CONTENT_DIR
value: /var/lib/keila/uploads
volumeMounts:
@@ -70,13 +70,13 @@ spec:
livenessProbe:
httpGet:
path: /
port: {{ .apps.keila.port }}
port: {{ .port }}
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: {{ .apps.keila.port }}
port: {{ .port }}
initialDelaySeconds: 5
periodSeconds: 5
volumes:

View File

@@ -5,12 +5,12 @@ metadata:
annotations:
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
external-dns.alpha.kubernetes.io/target: {{ .cloud.domain }}
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
traefik.ingress.kubernetes.io/router.middlewares: keila-cors@kubernetescrd
spec:
rules:
- host: {{ .apps.keila.domain }}
- host: {{ .domain }}
http:
paths:
- path: /
@@ -23,4 +23,4 @@ spec:
tls:
- secretName: "wildcard-wild-cloud-tls"
hosts:
- "{{ .apps.keila.domain }}"
- "{{ .domain }}"

View File

@@ -1,15 +1,18 @@
name: keila
description: Keila is an open-source email marketing platform that allows you to send newsletters and manage mailing lists with privacy and control.
version: 1.0.0
icon: https://www.keila.io/images/logo.svg
version: 0.17.1
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/keila.svg
requires:
- name: postgres
defaultConfig:
image: pentacent/keila:latest
namespace: keila
externalDnsDomain: "{{ .cloud.domain }}"
image: pentacent/keila:0.17.1
port: 4000
storage: 1Gi
domain: keila.{{ .cloud.domain }}
dbHostname: postgres.postgres.svc.cluster.local
dbHostname: "{{ .apps.postgres.host }}"
dbPort: "{{ .apps.postgres.port }}"
dbName: keila
dbUsername: keila
disableRegistration: "true"
@@ -20,12 +23,15 @@ defaultConfig:
port: "{{ .cloud.smtp.port }}"
from: "{{ .cloud.smtp.from }}"
user: "{{ .cloud.smtp.user }}"
tls: {{ .cloud.smtp.tls }}
startTls: {{ .cloud.smtp.startTls }}
tls: "{{ .cloud.smtp.tls }}"
startTls: "{{ .cloud.smtp.startTls }}"
defaultSecrets:
- key: apps.keila.secretKeyBase
- key: apps.keila.dbPassword
- key: apps.keila.dbUrl
- key: apps.keila.adminPassword
- key: apps.keila.smtpPassword
- key: apps.postgres.password
- key: secretKeyBase
default: "{{ random.AlphaNum 64 }}"
- key: dbPassword
- key: dbUrl
default: "postgres://{{ .app.dbUsername }}:{{ .secrets.dbPassword }}@{{ .app.dbHostname }}:{{ .app.dbPort }}/keila?sslmode=disable"
- key: adminPassword
- key: smtpPassword
requiredSecrets:
- postgres.password

View File

@@ -21,8 +21,8 @@ spec:
- "OPTIONS"
accessControlAllowOriginList:
- "http://localhost:1313"
- "https://*.{{ .cloud.domain }}"
- "https://{{ .cloud.domain }}"
- "https://*.{{ .externalDnsDomain }}"
- "https://{{ .externalDnsDomain }}"
accessControlExposeHeaders:
- "*"
accessControlMaxAge: 86400

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: keila
name: "{{ .namespace }}"

View File

@@ -7,4 +7,4 @@ spec:
- ReadWriteOnce
resources:
requests:
storage: {{ .apps.keila.storage }}
storage: {{ .storage }}

View File

@@ -7,5 +7,5 @@ spec:
component: web
ports:
- port: 80
targetPort: {{ .apps.keila.port }}
targetPort: {{ .port }}
protocol: TCP

View File

@@ -28,23 +28,23 @@ spec:
readOnlyRootFilesystem: false
env:
- name: PGHOST
value: {{ .apps.listmonk.dbHost }}
value: {{ .dbHost }}
- name: PGUSER
value: postgres
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: listmonk-secrets
key: apps.postgres.password
key: postgres.password
- name: DB_NAME
value: {{ .apps.listmonk.dbName }}
value: {{ .dbName }}
- name: DB_USER
value: {{ .apps.listmonk.dbUser }}
value: {{ .dbUser }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: listmonk-secrets
key: apps.listmonk.dbPassword
key: dbPassword
command:
- /bin/bash
- -c

View File

@@ -30,21 +30,23 @@ spec:
env:
- name: LISTMONK_app__address
value: "0.0.0.0:9000"
- name: LISTMONK_app__root_url
value: "{{ .rootUrl }}"
- name: LISTMONK_db__host
value: {{ .apps.listmonk.dbHost }}
value: {{ .dbHost }}
- name: LISTMONK_db__port
value: "{{ .apps.listmonk.dbPort }}"
value: "{{ .dbPort }}"
- name: LISTMONK_db__user
value: {{ .apps.listmonk.dbUser }}
value: {{ .dbUser }}
- name: LISTMONK_db__database
value: {{ .apps.listmonk.dbName }}
value: {{ .dbName }}
- name: LISTMONK_db__ssl_mode
value: {{ .apps.listmonk.dbSSLMode }}
value: {{ .dbSSLMode }}
- name: LISTMONK_db__password
valueFrom:
secretKeyRef:
name: listmonk-secrets
key: apps.listmonk.dbPassword
key: dbPassword
resources:
limits:
cpu: 500m

View File

@@ -6,16 +6,16 @@ metadata:
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
external-dns.alpha.kubernetes.io/target: {{ .cloud.domain }}
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
spec:
ingressClassName: traefik
tls:
- hosts:
- {{ .apps.listmonk.domain }}
secretName: {{ .apps.listmonk.tlsSecretName }}
- {{ .domain }}
secretName: {{ .tlsSecretName }}
rules:
- host: {{ .apps.listmonk.domain }}
- host: {{ .domain }}
http:
paths:
- path: /

View File

@@ -1,11 +1,15 @@
name: listmonk
description: Listmonk is a standalone, self-hosted, newsletter and mailing list manager. It is fast, feature-rich, and packed into a single binary.
description: Listmonk is a standalone, self-hosted, newsletter and mailing list manager.
It is fast, feature-rich, and packed into a single binary.
version: 5.0.3
icon: https://listmonk.app/static/images/logo.svg
requires:
- name: postgres
- name: postgres
defaultConfig:
namespace: listmonk
externalDnsDomain: '{{ .cloud.domain }}'
domain: listmonk.{{ .cloud.domain }}
rootUrl: https://listmonk.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls
storage: 1Gi
dbHost: postgres.postgres.svc.cluster.local
@@ -15,6 +19,8 @@ defaultConfig:
dbSSLMode: disable
timezone: UTC
defaultSecrets:
- key: apps.listmonk.dbPassword
- key: apps.listmonk.dbUrl
- key: apps.postgres.password
- key: dbPassword
- key: dbUrl
default: 'postgres://{{ .app.dbUser }}:{{ .secrets.dbPassword }}@{{ .app.dbHost }}:{{ .app.dbPort }}/{{ .app.dbName }}?sslmode={{ .app.dbSSLMode }}'
requiredSecrets:
- postgres.password

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: listmonk
name: "{{ .namespace }}"

View File

@@ -8,4 +8,4 @@ spec:
- ReadWriteOnce
resources:
requests:
storage: {{ .apps.listmonk.storage }}
storage: {{ .storage }}

View File

@@ -1,27 +1,27 @@
name: loomio
description: Loomio is a collaborative decision-making tool that makes it easy for groups to make decisions together
version: 3.0.11
icon: https://www.loomio.com/favicon.ico
icon: https://www.loomio.com/brand/logo_gold.svg
requires:
- name: postgres
installed_as: postgres
- name: redis
defaultConfig:
namespace: loomio
externalDnsDomain: {{ .cloud.domain }}
externalDnsDomain: "{{ .cloud.domain }}"
image: loomio/loomio:v3.0.11
workerImage: loomio/loomio:v3.0.11
appName: Loomio
domain: loomio.{{ .cloud.domain }}
domain: "loomio.{{ .cloud.domain }}"
tlsSecretName: wildcard-wild-cloud-tls
port: 3000
storage:
uploads: 5Gi
files: 5Gi
plugins: 1Gi
redisUrl: {{ .apps.redis.uri }}
adminEmail: "admin@{{ .cloud.domain }}"
supportEmail: "support@{{ .cloud.domain }}"
redisUrl: "{{ .apps.redis.uri }}"
adminEmail: "{{ .operator.email }}"
supportEmail: "{{ .operator.email }}"
forceSSL: "1"
useRackAttack: "1"
pumaWorkers: "2"
@@ -31,8 +31,8 @@ defaultConfig:
db:
name: loomio
user: loomio
host: {{ .apps.postgres.host }}
port: {{ .apps.postgres.port }}
host: "{{ .apps.postgres.host }}"
port: "{{ .apps.postgres.port }}"
smtp:
auth: plain
domain: "{{ .cloud.domain }}"

View File

@@ -3,7 +3,7 @@ kind: Deployment
metadata:
name: memcached
spec:
replicas: {{ .apps.memcached.replicas }}
replicas: {{ .replicas }}
selector:
matchLabels:
component: cache
@@ -14,24 +14,24 @@ spec:
spec:
containers:
- name: memcached
image: {{ .apps.memcached.image }}
image: "{{ .image }}"
ports:
- containerPort: {{ .apps.memcached.port }}
- containerPort: {{ .port }}
name: memcached
args:
- -m
- {{ .apps.memcached.memoryLimit }}
- "{{ .memoryLimit }}"
- -c
- "{{ .apps.memcached.maxConnections }}"
- "{{ .maxConnections }}"
- -p
- "{{ .apps.memcached.port }}"
- "{{ .port }}"
resources:
requests:
memory: {{ .apps.memcached.resources.requests.memory }}
cpu: {{ .apps.memcached.resources.requests.cpu }}
memory: "{{ .resources.requests.memory }}"
cpu: "{{ .resources.requests.cpu }}"
limits:
memory: {{ .apps.memcached.resources.limits.memory }}
cpu: {{ .apps.memcached.resources.limits.cpu }}
memory: "{{ .resources.limits.memory }}"
cpu: "{{ .resources.limits.cpu }}"
securityContext:
runAsNonRoot: true
runAsUser: 11211

View File

@@ -1,9 +1,11 @@
name: memcached
description: Memcached is an in-memory key-value store for small chunks of arbitrary data, commonly used as a cache layer.
description: Memcached is an in-memory key-value store for small chunks of arbitrary
data, commonly used as a cache layer.
version: 1.6.32
icon: https://memcached.org/memcached-logo.png
icon: https://www.vectorlogo.zone/logos/memcached/memcached-icon.svg
requires: []
defaultConfig:
namespace: memcached
image: memcached:1.6.32-alpine
port: 11211
memoryLimit: 64m
@@ -16,4 +18,4 @@ defaultConfig:
limits:
memory: 128Mi
cpu: 200m
defaultSecrets: []
defaultSecrets: []

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: memcached
name: "{{ .namespace }}"

View File

@@ -4,8 +4,8 @@ metadata:
name: memcached
spec:
ports:
- port: {{ .apps.memcached.port }}
targetPort: {{ .apps.memcached.port }}
- port: {{ .port }}
targetPort: {{ .port }}
protocol: TCP
name: memcached
selector:

View File

@@ -4,6 +4,8 @@ version: 9.1.0
icon: https://www.mysql.com/common/logos/logo-mysql-170x115.png
requires: []
defaultConfig:
namespace: mysql
externalDnsDomain: '{{ .cloud.domain }}'
image: mysql:9.1.0
port: 3306
storage: 20Gi
@@ -13,5 +15,5 @@ defaultConfig:
timezone: UTC
enableSSL: false
defaultSecrets:
- key: apps.mysql.rootPassword
- key: apps.mysql.password
- key: rootPassword
- key: password

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: mysql
name: "{{ .namespace }}"

View File

@@ -9,7 +9,7 @@ spec:
publishNotReadyAddresses: true
ports:
- name: mysql
port: {{ .apps.mysql.port }}
port: {{ .port }}
protocol: TCP
targetPort: mysql
selector:

View File

@@ -7,7 +7,7 @@ spec:
type: ClusterIP
ports:
- name: mysql
port: {{ .apps.mysql.port }}
port: {{ .port }}
protocol: TCP
targetPort: mysql
selector:

View File

@@ -29,7 +29,7 @@ spec:
type: RuntimeDefault
containers:
- name: mysql
image: {{ .apps.mysql.image }}
image: {{ .image }}
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
@@ -42,21 +42,21 @@ spec:
valueFrom:
secretKeyRef:
name: mysql-secrets
key: apps.mysql.rootPassword
key: rootPassword
- name: MYSQL_USER
value: {{ .apps.mysql.user }}
value: {{ .user }}
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secrets
key: apps.mysql.password
key: password
- name: MYSQL_DATABASE
value: {{ .apps.mysql.dbName }}
value: {{ .dbName }}
- name: TZ
value: {{ .apps.mysql.timezone }}
value: {{ .timezone }}
ports:
- name: mysql
containerPort: {{ .apps.mysql.port }}
containerPort: {{ .port }}
protocol: TCP
livenessProbe:
exec:
@@ -113,4 +113,4 @@ spec:
- ReadWriteOnce
resources:
requests:
storage: {{ .apps.mysql.storage }}
storage: {{ .storage }}

View File

@@ -45,7 +45,7 @@ spec:
valueFrom:
secretKeyRef:
name: open-webui-secrets
key: apps.openWebui.secretKey
key: openWebui.secretKey
volumeMounts:
- name: data
mountPath: /app/backend/data

View File

@@ -4,12 +4,12 @@ kind: Ingress
metadata:
name: open-webui
annotations:
external-dns.alpha.kubernetes.io/target: {{ .cloud.domain }}
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
traefik.ingress.kubernetes.io/router.middlewares: crowdsec-crowdsec-bouncer@kubernetescrd,crowdsec-rate-limit@kubernetescrd
spec:
rules:
- host: {{ .apps.open-webui.domain }}
- host: {{ .domain }}
http:
paths:
- path: /
@@ -22,4 +22,4 @@ spec:
tls:
- secretName: wildcard-wild-cloud-tls
hosts:
- {{ .apps.open-webui.domain }}
- {{ .domain }}

View File

@@ -1,17 +1,18 @@
name: openWebui
description: Open WebUI is a comprehensive, open-source web interface for AI models. Features a user-friendly design, supports various LLM runners, and operates entirely offline. Perfect for creating a ChatGPT-like experience with local or hosted models.
description: Open WebUI is a comprehensive, open-source web interface for AI models.
Features a user-friendly design, supports various LLM runners, and operates entirely
offline. Perfect for creating a ChatGPT-like experience with local or hosted models.
version: 0.4.5
icon: https://docs.openwebui.com/assets/logo-dark.png
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/open-webui.svg
requires: []
defaultConfig:
namespace: open-webui
externalDnsDomain: '{{ .cloud.domain }}'
image: ghcr.io/open-webui/open-webui:main
port: 8080
storage: 10Gi
domain: chat.{{ .cloud.domain }}
# vLLM integration - connect to existing vLLM service
vllmApiUrl: http://vllm-service.llm.svc.cluster.local:8000/v1
# Authentication settings
enableAuth: true
enableSignup: false
defaultSecrets:
- key: apps.openWebui.secretKey
defaultSecrets: []

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: open-webui
name: "{{ .namespace }}"

View File

@@ -5,17 +5,17 @@ kind: "ConfigMap"
metadata:
name: "openproject-core"
data:
DATABASE_HOST: "{{ .apps.openproject.dbHostname }}"
DATABASE_HOST: "{{ .dbHostname }}"
DATABASE_PORT: "5432"
DATABASE_URL: "postgresql://{{ .apps.openproject.dbUsername }}@{{ .apps.openproject.dbHostname }}:5432/{{ .apps.openproject.dbName }}"
OPENPROJECT_SEED_ADMIN_USER_PASSWORD_RESET: "{{ .apps.openproject.adminPasswordReset }}"
OPENPROJECT_SEED_ADMIN_USER_NAME: "{{ .apps.openproject.adminUserName }}"
OPENPROJECT_SEED_ADMIN_USER_MAIL: "{{ .apps.openproject.adminUserEmail }}"
OPENPROJECT_HTTPS: "{{ .apps.openproject.https }}"
OPENPROJECT_SEED_LOCALE: "{{ .apps.openproject.seedLocale }}"
OPENPROJECT_HOST__NAME: "{{ .apps.openproject.domain }}"
OPENPROJECT_HSTS: "{{ .apps.openproject.hsts }}"
OPENPROJECT_RAILS__CACHE__STORE: "{{ .apps.openproject.cacheStore }}"
OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "{{ .apps.openproject.railsRelativeUrlRoot }}"
POSTGRES_STATEMENT_TIMEOUT: "{{ .apps.openproject.postgresStatementTimeout }}"
DATABASE_URL: "postgresql://{{ .dbUsername }}@{{ .dbHostname }}:5432/{{ .dbName }}"
OPENPROJECT_SEED_ADMIN_USER_PASSWORD_RESET: "{{ .adminPasswordReset }}"
OPENPROJECT_SEED_ADMIN_USER_NAME: "{{ .adminUserName }}"
OPENPROJECT_SEED_ADMIN_USER_MAIL: "{{ .adminUserEmail }}"
OPENPROJECT_HTTPS: "{{ .https }}"
OPENPROJECT_SEED_LOCALE: "{{ .seedLocale }}"
OPENPROJECT_HOST__NAME: "{{ .domain }}"
OPENPROJECT_HSTS: "{{ .hsts }}"
OPENPROJECT_RAILS__CACHE__STORE: "{{ .cacheStore }}"
OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "{{ .railsRelativeUrlRoot }}"
POSTGRES_STATEMENT_TIMEOUT: "{{ .postgresStatementTimeout }}"
...

View File

@@ -5,5 +5,5 @@ kind: "ConfigMap"
metadata:
name: "openproject-memcached"
data:
OPENPROJECT_CACHE__MEMCACHE__SERVER: "{{ .apps.openproject.memcachedHostname }}:{{ .apps.openproject.memcachedPort }}"
OPENPROJECT_CACHE__MEMCACHE__SERVER: "{{ .memcachedHostname }}:{{ .memcachedPort }}"
...

View File

@@ -12,7 +12,7 @@ spec:
spec:
containers:
- name: db-init
image: {{ .apps.postgres.image }}
image: postgres:17
command: ["/bin/bash", "-c"]
args:
- |
@@ -36,16 +36,16 @@ spec:
valueFrom:
secretKeyRef:
name: postgres-secrets
key: apps.postgres.password
key: password
- name: DB_HOSTNAME
value: "{{ .apps.openproject.dbHostname }}"
value: "{{ .dbHostname }}"
- name: DB_DATABASE_NAME
value: "{{ .apps.openproject.dbName }}"
value: "{{ .dbName }}"
- name: DB_USERNAME
value: "{{ .apps.openproject.dbUsername }}"
value: "{{ .dbUsername }}"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.dbPassword
key: dbPassword
restartPolicy: OnFailure

View File

@@ -7,10 +7,10 @@ metadata:
spec:
tls:
- hosts:
- "{{ .apps.openproject.domain }}"
- "{{ .domain }}"
secretName: "wildcard-wild-cloud-tls"
rules:
- host: "{{ .apps.openproject.domain }}"
- host: "{{ .domain }}"
http:
paths:
- path: /

View File

@@ -1,11 +1,14 @@
name: openproject
description: OpenProject is an open-source project management software that provides comprehensive features for project planning, tracking, and collaboration.
description: OpenProject is an open-source project management software that provides
comprehensive features for project planning, tracking, and collaboration.
version: 16.1.1
icon: https://www.openproject.org/assets/images/openproject-logo.png
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/openproject.svg
requires:
- name: postgres
- name: memcached
- name: postgres
- name: memcached
defaultConfig:
namespace: openproject
externalDnsDomain: '{{ .cloud.domain }}'
serverImage: openproject/openproject:16.1.1-slim
timezone: UTC
serverPort: 8080
@@ -20,14 +23,15 @@ defaultConfig:
hsts: true
seedLocale: en
adminUserName: OpenProject Admin
adminUserEmail: "{{ .operator.email }}"
adminUserEmail: '{{ .operator.email }}'
adminPasswordReset: true
postgresStatementTimeout: 120s
tmpVolumesStorage: 2Gi
tlsSecretName: wildcard-wild-cloud-tls
cacheStore: memcache
railsRelativeUrlRoot: ""
railsRelativeUrlRoot: ''
defaultSecrets:
- key: apps.openproject.dbPassword
- key: apps.openproject.adminPassword
- key: apps.postgres.password
- key: dbPassword
- key: adminPassword
requiredSecrets:
- postgres.password

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: openproject
name: "{{ .namespace }}"

View File

@@ -8,5 +8,5 @@ spec:
accessModes: [ReadWriteMany]
resources:
requests:
storage: "{{ .apps.openproject.storage }}"
storage: "{{ .storage }}"
...

View File

@@ -27,7 +27,7 @@ spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: {{ .apps.openproject.tmpVolumesStorage }}
storage: {{ .tmpVolumesStorage }}
- name: app-tmp
# we can't use emptyDir due to the sticky bit / world writable issue
# see: https://github.com/kubernetes/kubernetes/issues/110835
@@ -39,13 +39,13 @@ spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: {{ .apps.openproject.tmpVolumesStorage }}
storage: {{ .tmpVolumesStorage }}
- name: "data"
persistentVolumeClaim:
claimName: openproject
initContainers:
- name: check-db-ready
image: "{{ .apps.postgres.image }}"
image: "postgres:17"
imagePullPolicy: Always
command: [
'sh',
@@ -62,12 +62,12 @@ spec:
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.dbPassword
key: dbPassword
- name: OPENPROJECT_SEED_ADMIN_USER_PASSWORD
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.adminPassword
key: adminPassword
resources:
limits:
memory: 200Mi
@@ -91,7 +91,7 @@ spec:
type: RuntimeDefault
containers:
- name: seeder
image: "{{ .apps.openproject.serverImage }}"
image: "{{ .serverImage }}"
imagePullPolicy: Always
args:
- bash
@@ -106,12 +106,12 @@ spec:
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.dbPassword
key: dbPassword
- name: OPENPROJECT_SEED_ADMIN_USER_PASSWORD
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.adminPassword
key: adminPassword
resources:
limits:
memory: 512Mi

View File

@@ -43,7 +43,7 @@ spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: {{ .apps.openproject.tmpVolumesStorage }}
storage: {{ .tmpVolumesStorage }}
- name: app-tmp
# we can't use emptyDir due to the sticky bit / world writable issue
# see: https://github.com/kubernetes/kubernetes/issues/110835
@@ -55,12 +55,12 @@ spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: {{ .apps.openproject.tmpVolumesStorage }}
storage: {{ .tmpVolumesStorage }}
- name: "data"
persistentVolumeClaim:
claimName: openproject
initContainers:
- name: wait-for-db
- name: wait-for-db
securityContext:
allowPrivilegeEscalation: false
capabilities:
@@ -72,8 +72,13 @@ spec:
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
image: {{ .apps.openproject.serverImage }}
image: postgres:17
imagePullPolicy: Always
command: [
'sh',
'-c',
'until pg_isready -h $DATABASE_HOST -p $DATABASE_PORT; do echo "waiting for database $DATABASE_HOST:$DATABASE_PORT"; sleep 2; done; echo "Database is ready!"'
]
envFrom:
- configMapRef:
name: openproject-core
@@ -84,14 +89,12 @@ spec:
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.dbPassword
key: dbPassword
- name: OPENPROJECT_SEED_ADMIN_USER_PASSWORD
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.adminPassword
args:
- /app/docker/prod/wait-for-db
key: adminPassword
resources:
limits:
memory: 1Gi
@@ -115,7 +118,7 @@ spec:
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
image: {{ .apps.openproject.serverImage }}
image: {{ .serverImage }}
imagePullPolicy: Always
envFrom:
- configMapRef:
@@ -127,12 +130,12 @@ spec:
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.dbPassword
key: dbPassword
- name: OPENPROJECT_SEED_ADMIN_USER_PASSWORD
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.adminPassword
key: adminPassword
args:
- /app/docker/prod/web
volumeMounts:

View File

@@ -43,7 +43,7 @@ spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: {{ .apps.openproject.tmpVolumesStorage }}
storage: {{ .tmpVolumesStorage }}
- name: app-tmp
# we can't use emptyDir due to the sticky bit / world writable issue
# see: https://github.com/kubernetes/kubernetes/issues/110835
@@ -55,7 +55,7 @@ spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: {{ .apps.openproject.tmpVolumesStorage }}
storage: {{ .tmpVolumesStorage }}
- name: "data"
persistentVolumeClaim:
claimName: openproject
@@ -72,8 +72,13 @@ spec:
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
image: {{ .apps.openproject.serverImage }}
image: postgres:17
imagePullPolicy: Always
command: [
'sh',
'-c',
'until pg_isready -h $DATABASE_HOST -p $DATABASE_PORT; do echo "waiting for database $DATABASE_HOST:$DATABASE_PORT"; sleep 2; done; echo "Database is ready!"'
]
envFrom:
- configMapRef:
name: openproject-core
@@ -84,15 +89,12 @@ spec:
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.dbPassword
key: dbPassword
- name: OPENPROJECT_SEED_ADMIN_USER_PASSWORD
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.adminPassword
args:
- bash
- /app/docker/prod/wait-for-db
key: adminPassword
resources:
limits:
memory: 1Gi
@@ -116,7 +118,7 @@ spec:
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
image: {{ .apps.openproject.serverImage }}
image: {{ .serverImage }}
imagePullPolicy: Always
envFrom:
- configMapRef:
@@ -132,7 +134,7 @@ spec:
valueFrom:
secretKeyRef:
name: openproject-secrets
key: apps.openproject.dbPassword
key: dbPassword
- name: "OPENPROJECT_GOOD_JOB_QUEUES"
value: ""
volumeMounts:

View File

@@ -2,7 +2,7 @@ name: redis
install: true
description: Redis is an open source, in-memory data structure store, used as a database, cache and message broker.
version: 1.0.0
icon: <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 85 27"><path fill="#FF4438" fill-rule="evenodd" d="M75.29 13.813c0-2.968 2.2-4.69 4.947-4.69 2.052 0 3.884.99 4.763 3.444-.257 1.245-1.795 2.638-2.455 2.858-.55-1.173-1.172-1.869-1.758-1.869-.733 0-.77.513-.77 1.172 0 .467.134 1.155.295 1.983.243 1.25.548 2.82.548 4.43 0 2.93-2.052 5.092-5.203 5.092-2.885 0-4.48-1.892-5.19-4.913-1.885 3.377-4.641 4.913-6.754 4.913-3.302 0-4.08-2.441-4-4.917-1.328 2.345-3.882 4.917-6.331 4.917-2.501 0-3.384-2.177-3.182-4.712-1.498 2.791-4.208 4.712-6.82 4.712-2.836 0-4.239-2.252-3.785-5.044-1.907 2.344-5.458 5.044-9.149 5.044-4.209 0-6.04-2.27-6.258-5.114-2.031 3.256-4.77 5.224-8.03 5.224-4.709 0-6.393-4.187-6.638-7.611a111 111 0 0 1-6.113 7.464c-.256.257-.476.403-.732.403C1.832 26.6.11 22.862 0 21.47c.723-1.121 5.281-6.13 8.95-10.161 1.29-1.417 2.471-2.714 3.336-3.679-2.247.678-4.564 2.03-7.486 4.132-.513.366-1.942-2.968-1.906-5.533 3.371-2.49 8.5-4.066 12.64-4.066 5.79 0 9.123 3.224 9.123 7.694 0 3.737-3.114 7.84-7.657 7.987-2.362.061-3.876-1.265-4.65-2.902.092 2.532 1.409 5.65 4.943 5.65 3.853 0 5.704-2.326 8.463-5.795q.269-.339.55-.69c2.345-2.895 5.056-5.46 9.013-5.46 2.418 0 4.067 1.503 4.067 3.774 0 2.748-3.224 6.558-7.73 6.558-.77 0-1.472-.101-2.064-.301q-.024.173-.025.338c0 1.282.476 2.052 2.565 2.052 3.077 0 5.971-1.832 9.489-6.119 3.444-4.213 6.045-6.045 8.793-6.045 1.855 0 3.262 1.005 3.883 2.698C57.98 6.283 61.104 2.514 63.75 0c2.601 1.1 4.47 3.26 3.957 3.7-1.942 1.76-8.427 8.83-10.991 13.044-.66 1.099-1.283 2.308-1.283 2.894 0 .55.33.733.696.733 1.76 0 5.289-4.156 8.336-7.746 1.138-1.34 2.209-2.602 3.095-3.539 2.052.843 4.14 2.638 3.627 3.261-2.71 3.224-4.762 5.862-4.762 7.364 0 .403.146.66.696.66 1.026 0 1.978-.916 3.554-2.858.33-.403.732-.403.989.22.696 1.685 1.722 2.601 2.528 2.601.952 0 1.429-.843 1.429-2.125 0-.876-.107-1.895-.2-2.772-.069-.664-.13-1.246-.13-1.624m-59.096-.477c1.942 0 4.067-1.062 4.067-3.224 0-1.312-.814-2.521-2.99-2.89l-.342.535c-1.106 1.729-2.149 3.359-3.2 4.953.63.354 1.427.626 2.465.626m19.638.66c0-.587-.367-.99-.953-.99-1.47 0-3.687 2.063-4.73 4.055.385.15.837.232 1.323.232 2.601 0 4.36-1.978 4.36-3.297m9.636 5.312c0 .66.366 1.1 1.135 1.1 2.382 0 5.35-4.324 5.35-6.083 0-.732-.403-1.172-1.063-1.172-2.162 0-5.422 4.103-5.422 6.155M76.06 6.082c-.843 1.392-2.125 2.968-2.601 3.444-2.199-.916-4.25-2.748-3.957-3.26.806-1.43 2.125-2.968 2.601-3.445 2.198.916 4.25 2.785 3.957 3.261" clip-rule="evenodd"></path></svg>
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/redis.svg
defaultConfig:
namespace: redis
image: redis:alpine

View File

@@ -19,10 +19,10 @@ spec:
seccompProfile:
type: RuntimeDefault
nodeSelector:
nvidia.com/gpu.product: "{{ .apps.vllm.gpuProduct }}"
nvidia.com/gpu.product: "{{ .gpuProduct }}"
containers:
- name: vllm
image: "{{ .apps.vllm.image }}"
image: "{{ .image }}"
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
@@ -31,10 +31,10 @@ spec:
- ALL
readOnlyRootFilesystem: false
args:
- --model={{ .apps.vllm.model }}
- --max-model-len={{ .apps.vllm.maxModelLen }}
- --tensor-parallel-size={{ .apps.vllm.tensorParallelSize }}
- --gpu-memory-utilization={{ .apps.vllm.gpuMemoryUtilization }}
- --model={{ .model }}
- --max-model-len={{ .maxModelLen }}
- --tensor-parallel-size={{ .tensorParallelSize }}
- --gpu-memory-utilization={{ .gpuMemoryUtilization }}
{{- if .apps.vllm.enforceEager }}
- --enforce-eager=True
{{- end }}
@@ -48,13 +48,13 @@ spec:
containerPort: 8000
resources:
requests:
cpu: "{{ .apps.vllm.cpuRequest }}"
memory: "{{ .apps.vllm.memoryRequest }}"
nvidia.com/gpu: {{ .apps.vllm.gpuCount }}
cpu: "{{ .cpuRequest }}"
memory: "{{ .memoryRequest }}"
nvidia.com/gpu: {{ .gpuCount }}
limits:
cpu: "{{ .apps.vllm.cpuLimit }}"
memory: "{{ .apps.vllm.memoryLimit }}"
nvidia.com/gpu: {{ .apps.vllm.gpuCount }}
cpu: "{{ .cpuLimit }}"
memory: "{{ .memoryLimit }}"
nvidia.com/gpu: {{ .gpuCount }}
readinessProbe:
httpGet:
path: /v1/models

View File

@@ -3,13 +3,13 @@ kind: Ingress
metadata:
name: vllm
annotations:
external-dns.alpha.kubernetes.io/target: {{ .cloud.domain }}
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
spec:
rules:
- host: {{ .apps.vllm.domain }}
- host: {{ .domain }}
http:
paths:
- path: /
@@ -21,5 +21,5 @@ spec:
number: 8000
tls:
- hosts:
- {{ .apps.vllm.domain }}
- {{ .domain }}
secretName: vllm-tls

View File

@@ -1,6 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: {{ .apps.vllm.namespace }}
namespace: {{ .namespace }}
labels:
- includeSelectors: true
pairs:

View File

@@ -1,21 +1,22 @@
name: vllm
description: vLLM is a fast and easy-to-use library for LLM inference and serving with OpenAI-compatible API
description: vLLM is a fast and easy-to-use library for LLM inference and serving
with OpenAI-compatible API
version: 0.5.4
icon: https://raw.githubusercontent.com/vllm-project/vllm/main/docs/source/assets/logos/vllm-logo-text-light.png
icon: https://unpkg.com/@lobehub/icons-static-png@latest/dark/vllm.png
requires: []
defaultConfig:
image: vllm/vllm-openai:v0.5.4
model: Qwen/Qwen2.5-7B-Instruct
maxModelLen: 8192
tensorParallelSize: 1
gpuMemoryUtilization: 0.90
gpuMemoryUtilization: 0.9
enforceEager: true
gpuProduct: "RTX 4090"
cpuRequest: "4"
cpuLimit: "8"
memoryRequest: "16Gi"
memoryLimit: "24Gi"
gpuProduct: RTX 4090
cpuRequest: '4'
cpuLimit: '8'
memoryRequest: 16Gi
memoryLimit: 24Gi
gpuCount: 1
domain: vllm.{{ .cloud.domain }}
namespace: llm
defaultSecrets: []
defaultSecrets: []

View File

@@ -1,4 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: {{ .apps.vllm.namespace }}
name: {{ .namespace }}