diff --git a/ADDING-APPS.md b/ADDING-APPS.md index 24f30f6..d9b6ba1 100644 --- a/ADDING-APPS.md +++ b/ADDING-APPS.md @@ -14,7 +14,7 @@ Each app directory must contain: 2. **`kustomization.yaml`** - Kustomize configuration with Wild Cloud labels 3. **Resource files** - Kubernetes manifests (deployments, services, ingresses, etc.) -### App Manifest (`manifest.yaml`) +## App Manifest (`manifest.yaml`) The manifest defines the app's metadata, dependencies, configuration schema, and secret requirements. @@ -22,6 +22,7 @@ This is the contents of an example `manifest.yaml` file for an app named "immich ```yaml name: immich +is: immich 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 @@ -54,11 +55,12 @@ requiredSecrets: - redis.auth # References redis app via 'redis' name (no alias) ``` -#### Manifest Fields +### Manifest Fields | Field | Required | Description | |-------|----------|-------------| | `name` | Yes | App identifier (must match directory name) | +| `is` | Yes | Unique id for this app. Used for `requires` mapping | | `description` | Yes | Brief app description shown in listings | | `version` | Yes | App version (follow upstream versioning) | | `icon` | No | URL to app icon for UI display | @@ -67,18 +69,156 @@ requiredSecrets: | `defaultSecrets` | No | This app's secrets (no 'default' = auto-generated) | | `requiredSecrets` | No | List of secrets from dependency apps (format: `.`) | -**Dependency Configuration:** +### Dependency Configuration + - Each dependency in `requires` can have: - - `name`: The actual app name to depend on + - `name`: The app name to depend on (any app with a matching `is` field can satisfy this requirement) - `alias`: Optional reference name for templates (defaults to `name`) -**Manifest Template Variable Sources:** +### Manifest Template Variables (configuration and secrets) + +#### 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..* }}` - resolved using app reference mapping 4. App-specific secrets (in 'defaultSecrets' ONLY): `{{ secrets.* }}` -**Manifest App Reference Resolution:** +#### Available Configuration Variiables + +Here's a comprehensive rundown of all config variables that get set during cluster and service setup in config.yaml: + +##### operator (Set during initial setup) + + - operator.email - Email for cluster operator/admin + +##### cloud (Infrastructure-level settings) + +###### DNS Configuration: +- cloud.dns.ip - IP address of the DNS server (Wild Central) +- cloud.dns.externalResolver - External DNS resolver (e.g., 1.1.1.1, 8.8.8.8) + +###### Network Configuration: + +- cloud.router.ip - Router gateway IP +- cloud.router.dynamicDns - Dynamic DNS hostname (optional) +- cloud.dhcpRange - DHCP range for the network (e.g., "192.168.8.34,192.168.8.79") +- cloud.dnsmasq.interface - Network interface for dnsmasq + +###### Domain Configuration: + +- cloud.baseDomain - Base domain for the cloud (e.g., "payne.io") +- cloud.domain - Full cloud domain (e.g., "cloud2.payne.io") +- cloud.internalDomain - Internal cluster domain (e.g., "internal.cloud2.payne.io") + +###### Storage Configuration (NFS Service): + +- cloud.nfs.host - NFS server hostname/IP +- cloud.nfs.mediaPath - NFS export path for media storage +- cloud.nfs.storageCapacity - NFS storage capacity (e.g., "50Gi", "1Ti") + +###### Registry Configuration (Docker Registry Service): + +- cloud.dockerRegistryHost - Docker registry hostname (e.g., "registry.internal.cloud2.payne.io") + +##### SMTP Configuration (SMTP Service): + +- cloud.smtp.host - SMTP server hostname +- cloud.smtp.port - SMTP port (typically "465" or "587") +- cloud.smtp.user - SMTP username +- cloud.smtp.from - Default 'from' email address +- cloud.smtp.tls - Enable TLS (true/false) +- cloud.smtp.startTls - Enable STARTTLS (true/false) + +###### Backup Configuration: + +- cloud.backup.root - Root path for backups + +##### cluster (Kubernetes cluster settings) + +###### Basic Cluster Info: + +- cluster.name - Cluster name identifier +- cluster.hostnamePrefix - Prefix for node hostnames + +###### Node Configuration: + +- cluster.nodes.talos.version - Talos Linux version (e.g., "v1.11.5") +- cluster.nodes.talos.schematicId - Talos Image Factory schematic ID +- cluster.nodes.control.vip - Virtual IP for control plane +- cluster.nodes.active.* - Individual node configurations with: + - role - "controlplane" or "worker" + - interface - Network interface name + - disk - Disk device path + - currentIp - Current IP address + - targetIp - Target IP address + - configured - Configuration status + - applied - Applied status + - maintenance - Maintenance mode + - schematicId - Node-specific schematic ID + - version - Node-specific Talos version + +###### MetalLB Service: + +- cluster.ipAddressPool - IP range for MetalLB (e.g., "192.168.8.80-192.168.8.89") +- cluster.loadBalancerIp - Primary load balancer IP (e.g., "192.168.8.80") + +###### Cert-Manager Service: + +- cluster.certManager.cloudflare.domain - Cloudflare domain for DNS-01 challenge +- cluster.certManager.cloudflare.zoneID - Cloudflare zone ID + +###### ExternalDNS Service: + +- cluster.externalDns.ownerId - Unique identifier for this cluster's DNS records + +###### Docker Registry Service: + +- cluster.dockerRegistry.storage - Storage size for registry (e.g., "10Gi") + +##### apps (Application configurations) + +Each app added to the cluster gets its own section under apps. with app-specific configuration from the app's manifest. Common patterns include: + +Standard app fields: +- apps..namespace - Kubernetes namespace +- apps..domain - App domain (e.g., "ghost.cloud2.payne.io") +- apps..externalDnsDomain - Domain for external DNS +- apps..tlsSecretName - TLS certificate secret name +- apps..image - Container image +- apps..port - Service port +- apps..storage - Persistent volume size +- apps..timezone - Timezone setting + +Database-dependent apps: +- apps..dbHost / dbHostname - Database hostname +- apps..dbPort - Database port +- apps..dbName - Database name +- apps..dbUser / dbUsername - Database user + +SMTP-enabled apps: +- apps..smtp.host - SMTP server +- apps..smtp.port - SMTP port +- apps..smtp.user - SMTP username +- apps..smtp.from - From address +- apps..smtp.tls - TLS enabled +- apps..smtp.startTls - STARTTLS enabled + +Configuration Flow + +1. Initial Setup: operator.email, basic cloud.* settings +2. Cluster Bootstrap: cluster.name, cluster.nodes.* settings +3. Infrastructure Services: Each service prompts for its serviceConfig from its manifest + - MetalLB → cluster.ipAddressPool, cluster.loadBalancerIp + - Cert-Manager → cluster.certManager.* + - ExternalDNS → cluster.externalDns.ownerId + - NFS → cloud.nfs.* + - Docker Registry → cloud.dockerRegistryHost, cluster.dockerRegistry.storage + - SMTP → cloud.smtp.* +4. Apps: Each app adds its configuration under apps..* based on its manifest + +#### Manifest App Reference Resolution: + When you use `{{ .apps..* }}` in templates: 1. System checks if `` matches any dependency's `alias` field 2. If no alias match, checks if `` matches any dependency's `name` field diff --git a/discourse/deployment.yaml b/discourse/deployment.yaml index 590de72..6393a34 100644 --- a/discourse/deployment.yaml +++ b/discourse/deployment.yaml @@ -19,11 +19,11 @@ spec: automountServiceAccountToken: false serviceAccountName: discourse securityContext: - fsGroup: 0 + fsGroup: 1000 fsGroupChangePolicy: Always containers: - name: discourse - image: tiredofit/discourse:latest + image: discourse/discourse:3.5.3 imagePullPolicy: "IfNotPresent" securityContext: allowPrivilegeEscalation: false @@ -32,10 +32,10 @@ spec: - ALL add: - CHOWN - - DAC_OVERRIDE - FOWNER - SETGID - SETUID + - DAC_OVERRIDE privileged: false readOnlyRootFilesystem: false runAsNonRoot: false @@ -43,78 +43,68 @@ spec: seccompProfile: type: RuntimeDefault env: - # Admin configuration - - name: ADMIN_USER - value: {{ .adminUsername }} - - name: ADMIN_EMAIL - value: {{ .adminEmail }} - - name: ADMIN_PASS - valueFrom: - secretKeyRef: - name: discourse-secrets - key: adminPassword - # Site configuration - - name: SITE_TITLE - value: {{ .siteName }} - - name: HOSTNAME - value: {{ .domain }} - # Database configuration - - name: DB_HOST + # Discourse database configuration + - name: DISCOURSE_DB_HOST value: {{ .dbHostname }} - - name: DB_PORT + - name: DISCOURSE_DB_PORT value: "{{ .dbPort }}" - - name: DB_NAME + - name: DISCOURSE_DB_NAME value: {{ .dbName }} - - name: DB_USER + - name: DISCOURSE_DB_USERNAME value: {{ .dbUsername }} - - name: DB_PASS + - name: DISCOURSE_DB_PASSWORD valueFrom: secretKeyRef: name: discourse-secrets key: dbPassword # Redis configuration - - name: REDIS_HOST + - name: DISCOURSE_REDIS_HOST value: {{ .redisHostname }} - - name: REDIS_PASS + - name: DISCOURSE_REDIS_PASSWORD valueFrom: secretKeyRef: name: discourse-secrets key: redis.password + # Site configuration + - name: DISCOURSE_HOSTNAME + value: {{ .domain }} + - name: DISCOURSE_DEVELOPER_EMAILS + value: {{ .adminEmail }} + - name: DISCOURSE_SECRET_KEY_BASE + valueFrom: + secretKeyRef: + name: discourse-secrets + key: secretKeyBase # SMTP configuration - - name: SMTP_ENABLED - value: "{{ .smtp.enabled }}" - - name: SMTP_HOST + - name: DISCOURSE_SMTP_ADDRESS value: {{ .smtp.host }} - - name: SMTP_PORT + - name: DISCOURSE_SMTP_PORT value: "{{ .smtp.port }}" - - name: SMTP_USER + - name: DISCOURSE_SMTP_USER_NAME value: {{ .smtp.user }} - - name: SMTP_PASS + - name: DISCOURSE_SMTP_PASSWORD valueFrom: secretKeyRef: name: discourse-secrets key: smtpPassword - - name: SMTP_TLS - value: "{{ .smtp.tls }}" - # Container timezone - - name: TZ - value: {{ .timezone }} + - name: DISCOURSE_SMTP_ENABLE_START_TLS + value: "{{ .smtp.startTls }}" ports: - name: http containerPort: 3000 protocol: TCP livenessProbe: httpGet: - path: / + path: /srv/status port: http - initialDelaySeconds: 420 + initialDelaySeconds: 500 periodSeconds: 30 timeoutSeconds: 10 successThreshold: 1 failureThreshold: 6 readinessProbe: httpGet: - path: / + path: /srv/status port: http initialDelaySeconds: 360 periodSeconds: 30 @@ -125,25 +115,120 @@ spec: limits: cpu: 2000m ephemeral-storage: 10Gi - memory: 4Gi + memory: 8Gi requests: - cpu: 500m + cpu: 750m ephemeral-storage: 50Mi memory: 1Gi volumeMounts: - - name: discourse-logs - mountPath: /data/logs - - name: discourse-uploads - mountPath: /data/uploads - - name: discourse-backups - mountPath: /data/backups + - name: discourse-data + mountPath: /shared + - name: sidekiq + image: discourse/discourse:3.5.3 + imagePullPolicy: "IfNotPresent" + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + add: + - CHOWN + - FOWNER + - SETGID + - SETUID + - DAC_OVERRIDE + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: false + runAsUser: 0 + seccompProfile: + type: RuntimeDefault + command: + - /bin/bash + - -c + - "cd /var/www/discourse && exec bundle exec sidekiq" + env: + # Discourse database configuration + - name: DISCOURSE_DB_HOST + value: {{ .dbHostname }} + - name: DISCOURSE_DB_PORT + value: "{{ .dbPort }}" + - name: DISCOURSE_DB_NAME + value: {{ .dbName }} + - name: DISCOURSE_DB_USERNAME + value: {{ .dbUsername }} + - name: DISCOURSE_DB_PASSWORD + valueFrom: + secretKeyRef: + name: discourse-secrets + key: dbPassword + # Redis configuration + - name: DISCOURSE_REDIS_HOST + value: {{ .redisHostname }} + - name: DISCOURSE_REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: discourse-secrets + key: redis.password + # Site configuration + - name: DISCOURSE_HOSTNAME + value: {{ .domain }} + - name: DISCOURSE_DEVELOPER_EMAILS + value: {{ .adminEmail }} + - name: DISCOURSE_SECRET_KEY_BASE + valueFrom: + secretKeyRef: + name: discourse-secrets + key: secretKeyBase + # SMTP configuration + - name: DISCOURSE_SMTP_ADDRESS + value: {{ .smtp.host }} + - name: DISCOURSE_SMTP_PORT + value: "{{ .smtp.port }}" + - name: DISCOURSE_SMTP_USER_NAME + value: {{ .smtp.user }} + - name: DISCOURSE_SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: discourse-secrets + key: smtpPassword + - name: DISCOURSE_SMTP_ENABLE_START_TLS + value: "{{ .smtp.startTls }}" + livenessProbe: + exec: + command: + - /bin/bash + - -c + - "pgrep -f sidekiq" + initialDelaySeconds: 500 + periodSeconds: 30 + timeoutSeconds: 10 + successThreshold: 1 + failureThreshold: 6 + readinessProbe: + exec: + command: + - /bin/bash + - -c + - "pgrep -f sidekiq" + initialDelaySeconds: 180 + periodSeconds: 30 + timeoutSeconds: 10 + successThreshold: 1 + failureThreshold: 6 + resources: + limits: + cpu: 1000m + ephemeral-storage: 2Gi + memory: 1Gi + requests: + cpu: 375m + ephemeral-storage: 50Mi + memory: 512Mi + volumeMounts: + - name: discourse-data + mountPath: /shared volumes: - - name: discourse-logs + - name: discourse-data persistentVolumeClaim: - claimName: discourse-logs - - name: discourse-uploads - persistentVolumeClaim: - claimName: discourse-uploads - - name: discourse-backups - persistentVolumeClaim: - claimName: discourse-backups + claimName: discourse-data diff --git a/discourse/manifest.yaml b/discourse/manifest.yaml index 5ad33fa..65df970 100644 --- a/discourse/manifest.yaml +++ b/discourse/manifest.yaml @@ -1,4 +1,5 @@ name: discourse +is: discourse description: Discourse is a modern, open-source discussion platform designed for online communities and forums. version: 3.5.3 icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/discourse.svg diff --git a/discourse/pvc.yaml b/discourse/pvc.yaml index 3583078..e3908a4 100644 --- a/discourse/pvc.yaml +++ b/discourse/pvc.yaml @@ -2,20 +2,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: discourse-logs - namespace: discourse -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 2Gi - storageClassName: longhorn ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: discourse-uploads + name: discourse-data namespace: discourse spec: accessModes: @@ -24,16 +11,3 @@ spec: requests: storage: {{ .storage }} storageClassName: longhorn ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: discourse-backups - namespace: discourse -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi - storageClassName: longhorn diff --git a/example-admin/manifest.yaml b/example-admin/manifest.yaml index b34c165..09c5988 100644 --- a/example-admin/manifest.yaml +++ b/example-admin/manifest.yaml @@ -1,4 +1,5 @@ name: example-admin +is: example install: true description: An example application that is deployed with internal-only access. version: 1.0.0 diff --git a/example-app/manifest.yaml b/example-app/manifest.yaml index a486bc5..76c4c6c 100644 --- a/example-app/manifest.yaml +++ b/example-app/manifest.yaml @@ -1,4 +1,5 @@ name: example-app +is: example install: true description: An example application that is deployed with public access. version: 1.0.0 diff --git a/ghost/manifest.yaml b/ghost/manifest.yaml index 7225814..bcbe0bd 100644 --- a/ghost/manifest.yaml +++ b/ghost/manifest.yaml @@ -1,4 +1,5 @@ name: ghost +is: ghost description: Ghost is a powerful app for new-media creators to publish, share, and grow a business around their content. version: 5.118.1 diff --git a/gitea/manifest.yaml b/gitea/manifest.yaml index 335367e..27c4f8c 100644 --- a/gitea/manifest.yaml +++ b/gitea/manifest.yaml @@ -1,4 +1,5 @@ name: gitea +is: gitea description: Gitea is a painless self-hosted Git service written in Go version: 1.24.3 icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/gitea.svg diff --git a/immich/manifest.yaml b/immich/manifest.yaml index b80b865..7ddc959 100644 --- a/immich/manifest.yaml +++ b/immich/manifest.yaml @@ -1,4 +1,5 @@ name: immich +is: 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. diff --git a/keila/manifest.yaml b/keila/manifest.yaml index 9004ec3..6b8c834 100644 --- a/keila/manifest.yaml +++ b/keila/manifest.yaml @@ -1,4 +1,5 @@ name: keila +is: 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: 0.17.1 icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/keila.svg diff --git a/listmonk/manifest.yaml b/listmonk/manifest.yaml index 7435b69..e6c8b9e 100644 --- a/listmonk/manifest.yaml +++ b/listmonk/manifest.yaml @@ -1,4 +1,5 @@ name: listmonk +is: listmonk 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 diff --git a/loomio/manifest.yaml b/loomio/manifest.yaml index 5778baa..3b7013e 100644 --- a/loomio/manifest.yaml +++ b/loomio/manifest.yaml @@ -1,4 +1,5 @@ name: loomio +is: 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/brand/logo_gold.svg diff --git a/memcached/manifest.yaml b/memcached/manifest.yaml index c512286..31e6346 100644 --- a/memcached/manifest.yaml +++ b/memcached/manifest.yaml @@ -1,4 +1,5 @@ name: memcached +is: memcached 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 diff --git a/mysql/manifest.yaml b/mysql/manifest.yaml index 8c4aca7..66d4253 100644 --- a/mysql/manifest.yaml +++ b/mysql/manifest.yaml @@ -1,4 +1,5 @@ name: mysql +is: mysql description: MySQL is an open-source relational database management system version: 9.1.0 icon: https://www.mysql.com/common/logos/logo-mysql-170x115.png diff --git a/open-webui/manifest.yaml b/open-webui/manifest.yaml index 98a5017..ce09215 100644 --- a/open-webui/manifest.yaml +++ b/open-webui/manifest.yaml @@ -1,4 +1,5 @@ -name: openWebui +name: open-webui +is: open-webui 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. diff --git a/openproject/manifest.yaml b/openproject/manifest.yaml index 48bf900..45fea2a 100644 --- a/openproject/manifest.yaml +++ b/openproject/manifest.yaml @@ -1,4 +1,5 @@ name: openproject +is: openproject description: OpenProject is an open-source project management software that provides comprehensive features for project planning, tracking, and collaboration. version: 16.1.1 diff --git a/postgres/manifest.yaml b/postgres/manifest.yaml index 2095c69..3e789a5 100644 --- a/postgres/manifest.yaml +++ b/postgres/manifest.yaml @@ -1,4 +1,5 @@ name: postgres +is: postgres install: true description: PostgreSQL is a powerful, open source object-relational database system. version: 1.0.0 diff --git a/redis/manifest.yaml b/redis/manifest.yaml index 9de23e9..17a12b4 100644 --- a/redis/manifest.yaml +++ b/redis/manifest.yaml @@ -1,4 +1,5 @@ name: redis +is: 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 diff --git a/vllm/manifest.yaml b/vllm/manifest.yaml index 22e1fc6..38827f9 100644 --- a/vllm/manifest.yaml +++ b/vllm/manifest.yaml @@ -1,4 +1,5 @@ name: vllm +is: vllm description: vLLM is a fast and easy-to-use library for LLM inference and serving with OpenAI-compatible API version: 0.5.4