Reduces apps default resource usage.

This commit is contained in:
2026-06-19 21:38:56 +00:00
parent 63dcd1b82b
commit c223e2b5e0
55 changed files with 661 additions and 174 deletions

254
ADDING-APPS-NOTES.md Normal file
View File

@@ -0,0 +1,254 @@
# Adding Apps - Notes & Documentation Improvements
## Documentation Improvement Candidates
Issues and gaps discovered while adding apps. These should be folded back into ADDING-APPS.md.
### 1. Node.js app memory requirements
Node.js apps (NocoDB, Outline, etc.) need more than 512Mi memory. NocoDB crashed with OOM at 512Mi.
- Recommended: 1Gi limit, 512Mi request for Node.js apps
- Set `NODE_OPTIONS=--max-old-space-size=768` to keep heap within the limit
- **Doc suggestion**: Add a section in ADDING-APPS.md about runtime-specific resource defaults:
- Node.js apps: 1Gi limit, 512Mi request; add NODE_OPTIONS env var
- JVM apps (Java/Kotlin): 1Gi+ limit
- Python/Ruby apps: 512Mi typically sufficient
- Go/Rust apps: 256-512Mi typically sufficient
### 2. db-init-job is repeated boilerplate
The db-init-job.yaml for PostgreSQL apps is nearly identical across all apps. Only the app name, namespace, and secret name change.
- **Doc suggestion**: Provide a copy-paste template for the standard PostgreSQL db-init-job with placeholders marked clearly.
### 4. Apps requiring hex-encoded secrets
Some apps (Outline) require secrets in specific formats (e.g., exactly 64 hex chars). The default Wild Cloud `GenerateSecret` produces alphanumeric (not hex) strings.
- **Solution**: Use `crypto.SHA256` gomplate function in the manifest `default` field to generate a valid 64-char hex string from another secret:
```yaml
defaultSecrets:
- key: utilsSecret
- key: secretKey
default: '{{ crypto.SHA256 .secrets.utilsSecret }}'
```
- **Doc suggestion**: Add this pattern to ADDING-APPS.md with examples of apps that need it. Note that the order of `defaultSecrets` matters — referenced secrets must come first.
### 6. Always use `?sslmode=disable` in PostgreSQL connection strings
The Wild Cloud internal PostgreSQL does not have SSL enabled. Apps that construct PostgreSQL connection strings must include `?sslmode=disable`, and deployments should set `PGSSLMODE=disable`. Without it, apps crash with "The server does not support SSL connections."
- Already done correctly in: listmonk, gitea, discourse
- Missing from: outline (fixed), any new app
- **Doc suggestion**: Add to the ADDING-APPS.md dbUrl template examples: always append `?sslmode=disable` to postgres connection strings.
### 5. Re-running `wild app add` is required after template changes
When you modify files in wild-directory (e.g., fix a template), you must re-run `wild app add <app>` before deploying. The compiled templates in the instance `apps/` directory are NOT automatically updated when wild-directory changes. Only then will `wild app deploy` use the updated templates.
- **Doc suggestion**: Add to ADDING-APPS.md: "After modifying templates in wild-directory, always re-run `wild app add <app>` to recompile before deploying."
### 7. WriteFreely requires root user (jrasanen/writefreely image)
The `jrasanen/writefreely` image startup script creates `config.ini` in `/writefreely/` which is a root-owned directory. The image's default user is non-root, causing "Permission denied" errors at startup.
- **Fix**: Set `runAsUser: 0` and `runAsNonRoot: false` in the pod securityContext
- Still safe with `allowPrivilegeEscalation: false` and `capabilities.drop: ALL`
- **Doc suggestion**: Note that some community images require root. When an image fails with permission denied on startup, check if it needs root and add the security context override with capabilities dropped.
### 8. Redis URL must include password for authenticated Redis
Wild Cloud's Redis requires authentication. Apps that take a Redis URL must include the password in the URL: `redis://:password@host:6379`.
- **Pattern**: Add `redis.password` to `requiredSecrets`, then use K8s env var expansion:
```yaml
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-secrets
key: redis.password
- name: REDIS_URL
value: redis://:$(REDIS_PASSWORD)@{{ .redis.host }}:6379
```
- The `$(VAR_NAME)` syntax is supported by Kubernetes in container env values (evaluated at pod start)
- **Doc suggestion**: Add this pattern to ADDING-APPS.md under "Redis Authentication" section.
### 3. Consistent health check paths across apps
Different apps use different health check paths (`/health`, `/healthz`, `/_health`, `/api/v1/health`). This should be documented per app type or noted that you must verify the actual path from the app's Docker docs.
- For apps where no valid HTTP health path exists (e.g., Typebot viewer which only serves chatbot URLs), use `tcpSocket` probe instead of `httpGet`.
### 9. Linuxserver.io and s6-overlay images require root + capabilities
Images using s6-overlay init system (most linuxserver.io images) must run as root and need full capabilities to switch to their internal "abc" user.
- Set `runAsUser: 0, runAsNonRoot: false` in pod securityContext
- Do NOT set `allowPrivilegeEscalation: false` or `capabilities.drop: ALL` in container securityContext
- Only keep `readOnlyRootFilesystem: false`
- Same applies to Nextcloud (uses rsync/chown during setup)
- **Affects**: BookStack (linuxserver.io), any linuxserver.io image
### 10. CryptPad needs emptyDir + initContainer for config directory
The CryptPad image's `/cryptpad/config/` directory is in the image overlay filesystem and not writable by the container process even when running as root.
- **Fix**: Mount an emptyDir at `/cryptpad/config`, and use an initContainer to pre-seed `config.example.js` from the image into the emptyDir.
- The initContainer mounts the emptyDir at a different path (e.g., `/config-dest`) and copies the file.
- **CPAD_CONF** env var must be set to `/cryptpad/config/config.js` (startup script copies config.example.js to config.js if config.js doesn't exist).
### 11. Pixelfed image requires authentication (ghcr.io access restriction)
~~`ghcr.io/pixelfed/pixelfed` returns 403 Forbidden for anonymous pulls.~~
- **Resolved**: Use `ghcr.io/mattlqx/docker-pixelfed:v0.12.7-nginx` (community image, publicly accessible)
- Note: the mattlqx image uses port 80 (nginx), not 8080. Update `containerPort` and service `targetPort` accordingly.
### 12. Bitnami images no longer on Docker Hub
Bitnami moved their images from Docker Hub (`bitnami/appname`) to their own OCI registry. Apps that were packaged using Bitnami images (e.g., Ghost) need to be updated to use the official upstream image or Bitnami's new registry.
- **Ghost fix**: Switched from `bitnami/ghost:5.x` to official `ghost:5.130.6-alpine`
- The official ghost image uses different env vars (`database__connection__host` instead of `GHOST_DATABASE_HOST`) and a different mount path (`/var/lib/ghost/content` instead of `/bitnami/ghost`)
- Check image availability before packaging: `docker manifest inspect docker.io/bitnami/appname:tag`
### 13. Required secrets belong in app-secrets, not dep-secrets
Apps that use `requiredSecrets` (e.g., `mysql.rootPassword`) receive those secrets copied into their own `<app>-secrets` K8s Secret, under the key `<dep>.<key>` (e.g., `mysql.rootPassword`). db-init jobs and deployments must reference `<app>-secrets`, NOT `mysql-secrets`.
```yaml
# WRONG - mysql-secrets doesn't exist in the app namespace
secretKeyRef:
name: mysql-secrets
key: rootPassword
# CORRECT - the required secret is copied into ghost-secrets
secretKeyRef:
name: ghost-secrets
key: mysql.rootPassword
```
### 15. mattlqx/docker-pixelfed requires APP_PORT env var
The `ghcr.io/mattlqx/docker-pixelfed` community image generates nginx config from env vars using `sed`. The `listen` directive uses `APP_PORT`, which must be set explicitly or nginx crashes with "invalid number of arguments in 'listen' directive".
- Add `APP_PORT: "80"` to the deployment env vars
- The image listens on port 80 (nginx), not 8080 as the original pixelfed image did
- Both the web and worker deployments must mount the shared storage PVC — use ReadWriteMany access mode, not ReadWriteOnce, since both pods need it simultaneously
### 14. Your Priorities and Polis have no public Docker images
- `ghcr.io/citizensfoundation/your-priorities-app` — 403 Forbidden, images require auth
- `compdemocracy/polis-server` — private, no public Docker Hub or ghcr.io images
- Both apps require building custom images from source before they can be packaged
- Attempting to add these to wild-directory results in ImagePullBackOff
### 16. PHP/Laravel and other heavy-framework apps need extended probe delays
Laravel's startup process runs package discovery, autoload optimization, and encryption key generation before the HTTP server is ready. This commonly takes 23 minutes on cluster restart or first boot.
- The default `initialDelaySeconds: 60` is too short — the liveness probe fires before the app is ready, kills the container, and a restart loop begins
- Set `initialDelaySeconds: 120` and `failureThreshold: 6` for liveness probes on Laravel/Symfony apps
- Set `initialDelaySeconds: 60` for readiness probes
- The symptom is: pod shows `Running` for ~2 minutes then gets `Killing` due to liveness probe failure, followed by a new pod starting the same cycle
- The same applies to other frameworks with heavy startup initialization: Rails (`bundle exec`, asset precompilation), Spring Boot (JVM + Spring context loading), Django with migrations
- **Affects**: Ushahidi API (Laravel) — fixed with 120s initial delay + failureThreshold: 6
### 17. MySQL db-init `CREATE USER IF NOT EXISTS` doesn't update passwords
The common db-init pattern `CREATE USER IF NOT EXISTS 'user'@'%' IDENTIFIED BY '${PASSWORD}'` only sets the password at creation time. If the MySQL user already exists from a previous deployment (e.g., after `wild app delete` + `wild app add` without dropping the database), the password is silently left unchanged and the new deployment fails with "Access denied for user".
- **Symptom**: App starts, connects to MySQL, gets "Access denied" even though the db-init job completed successfully
- **Quick fix**: Exec into the MySQL pod and run: `ALTER USER 'user'@'%' IDENTIFIED BY 'current_password'; FLUSH PRIVILEGES;`
- **Better pattern** — make db-init jobs truly idempotent for both user creation and password:
```sql
CREATE USER IF NOT EXISTS '${DB_USERNAME}'@'%' IDENTIFIED BY '${DB_PASSWORD}';
ALTER USER '${DB_USERNAME}'@'%' IDENTIFIED BY '${DB_PASSWORD}';
GRANT ALL PRIVILEGES ON ${DB_DATABASE_NAME}.* TO '${DB_USERNAME}'@'%';
FLUSH PRIVILEGES;
```
- This `CREATE ... IF NOT EXISTS` + `ALTER USER` pattern is safe to run repeatedly regardless of whether the user existed before
- **Affects**: Ghost (MySQL backend) — hit this when redeploying after a previous partial test run
### 18. Gancio "Non empty db" crash on re-deploy
Gancio stores its setup state in `config.json` on the data PVC. If the PVC is lost or the app is deleted and redeployed with an existing database, gancio finds a non-empty DB but no `config.json` and refuses to start with "Non empty db! Please move your current db elsewhere than retry."
- **Fix**: Drop the database schema and let gancio recreate it:
```bash
kubectl exec -n postgres <postgres-pod> -- psql -U postgres gancio \
-c "DROP SCHEMA public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO gancio; GRANT ALL ON SCHEMA public TO public;"
kubectl scale deployment -n gancio gancio --replicas=0
# (wait for pod to terminate)
kubectl scale deployment -n gancio gancio --replicas=1
```
- Scale down first to release the DB connection before dropping the schema
- This only affects re-deployments; fresh installs work fine
### 19. Odoo requires explicit database name and `-i base` on first run
The official Odoo Docker image does not auto-initialize the database. Without specifying the database name and installing the base module, Odoo starts in multi-database manager mode and health checks fail with "Database not initialized".
- Add `args: ["-d", "DATABASE_NAME", "-i", "base"]` to the container spec
- The `-i base` flag installs Odoo's base module and creates all database tables on first run
- Subsequent runs with `-i base` are safe (idempotent) — it updates rather than reinstalls
- Use a `startupProbe` with `failureThreshold: 40, periodSeconds: 30` (20 minutes) since first-run initialization takes 1015 minutes
- Once startup probe passes, the regular liveness/readiness probes with `initialDelaySeconds: 0` take over
### 20. Headscale v0.29+ CLI: inconsistent user flag types
In headscale v0.29, different commands accept different user identifiers:
- `preauthkeys create --user <id>` — requires numeric ID
- `auth register --user <username>` — accepts username string
- `users destroy --identifier <id>` — requires numeric ID, not a positional arg
```bash
# Always list users first to get the ID
headscale users list
headscale preauthkeys create --user 1 # numeric ID
headscale auth register --auth-id <id> --user payne # username string OK
headscale users destroy --identifier 1 # numeric ID
```
Node registration flow: when a Tailscale client connects via browser (not pre-auth key), headscale
shows a command like `headscale auth register --auth-id hskey-authreq-XXXX --user USERNAME`.
Run it via `kubectl exec -n headscale deploy/headscale -- headscale auth register --auth-id <id> --user <username>`.
## App Status Tracking
| App | Status | Notes |
|-----|--------|-------|
| Firefly III | ✅ working | |
| Wiki.js | ✅ working | |
| Outline | ✅ working | Redis URL needs password via K8s env var expansion |
| WriteFreely | ✅ working | Needs runAsUser: 0 (community image quirk) |
| NocoDB | ✅ working | Node.js needs 2Gi memory |
| Mattermost | ✅ working | |
| Etherpad | ✅ working | |
| CryptPad | ✅ working | Needs emptyDir + initContainer for config seeding |
| MediaWiki | ✅ working | Post-deploy web installer required for LocalSettings.php |
| Nextcloud | ✅ working | |
| PeerTube | ✅ working | |
| BookStack | ✅ working | linuxserver.io needs root + no capabilities.drop |
| Typebot | ✅ working | Builder: /signin health path; Viewer: tcpSocket probe |
| Pixelfed | ✅ working | Use ghcr.io/mattlqx/docker-pixelfed:v0.12.7-nginx (community), port 80 not 8080 |
| Wekan | pending | needs MongoDB |
| Mautic | ✅ working | |
| Taiga | ✅ working | |
| Pol.is | ❌ blocked | Private AWS ECR images, no public alternative |
| Zulip | ✅ working | Bundles own postgres/rabbitmq/memcached; first-run migrations take ~10min |
| Rocket.Chat | pending | needs MongoDB |
| LimeSurvey | ✅ working | |
| Gancio | ✅ working | Re-deploy: must drop DB schema first if prior data exists (see note 18) |
| Mobilizon | ✅ working | |
| Formbricks | ✅ working | |
| Jitsi | ✅ working | |
| Mastodon | already done | |
| Chamilo | ✅ working | |
| Moodle | ✅ working | |
| Open edX | pending | complex |
| Pretix | ✅ working | |
| Indico | ✅ working | |
| Eventyay | ✅ working | No versioned Docker tags upstream — main tag is intentional |
| Leihs | pending | |
| LibreBooking | ✅ working | |
| uMap | ✅ working | Host header needed in health probes (Django ALLOWED_HOSTS) |
| MapComplete | pending | |
| Terrastories | pending | |
| Karrot | already done | |
| LiquidFeedback | pending | |
| Your Priorities | ❌ blocked | No public Docker images (ghcr.io requires auth) |
| Helios Voting | pending | |
| Belenios | pending | |
| Decidim | already done | |
| Alaveteli | pending | |
| FixMyStreet | pending | |
| Ghost | ✅ working | Bitnami image gone; use official ghost:alpine; different env vars + mount path (see note 12) |
| Ushahidi | ✅ working | Client: runAsUser:0 + CHOWN/SETUID/SETGID; API: liveness initialDelay 120s (Laravel slow start) |
| CiviCRM | pending | |
| Open Collective | pending | |
| Open Food Network | pending | |
| Resonate | pending | |
| BookWyrm | ✅ working | |
| Lemmy | already done | |
| Akaunting | ✅ working | |
| GnuCash | skip | desktop app, not containerizable |
| OhMyForm | ✅ working | |
| Odoo | ✅ working | Needs `-d DATABASE -i base` args; uses startupProbe (20min window) for first-run DB init |
| Headscale | ✅ working | SQLite, ConfigMap-based config, no secrets; v0.29 CLI uses numeric user IDs (see note 20) |
| Baserow | ✅ working | |
| Keila | already done | |
| Listmonk | already done | |
| Discourse | already done | |
| Gitea | already done | |
| Immich | already done | |
| Loomio | already done | |
| Synapse | already done | App renamed from matrix to synapse |
| Open WebUI | already done | |
| OpenProject | already done | |
| vLLM | already done | |

View File

@@ -736,6 +736,7 @@ Apps requiring PostgreSQL or MySQL should include a database initialization job
- Use `restartPolicy: OnFailure` - Use `restartPolicy: OnFailure`
- Include in `kustomization.yaml` resources - Include in `kustomization.yaml` resources
- Use appropriate security context (e.g., `runAsUser: 999` for PostgreSQL) - Use appropriate security context (e.g., `runAsUser: 999` for PostgreSQL)
- **Write idempotent SQL**: For MySQL, use `CREATE USER IF NOT EXISTS ... ; ALTER USER ...` so re-running the job after a redeploy doesn't leave stale passwords. See "MySQL db-init User Password Idempotency" in Common Gotchas.
**Example apps:** `immich`, `gitea`, `openproject`, `discourse` **Example apps:** `immich`, `gitea`, `openproject`, `discourse`
@@ -935,6 +936,247 @@ labels:
component: server # Simple component label component: server # Simple component label
``` ```
## Common Gotchas
Lessons learned from deploying apps to Wild Cloud. These are the non-obvious issues that cause failures.
### Always Verify Docker Image Availability
Before packaging an app, confirm the image is **publicly accessible without authentication**:
```bash
docker pull registry.example.com/org/image:tag
# or check via curl for ghcr.io:
curl -s "https://ghcr.io/token?scope=repository:org/image:pull&service=ghcr.io" | python3 -c "import json,sys; print(json.load(sys.stdin).get('token','no token')[:20])"
```
Common pitfalls:
- **Bitnami images** moved away from Docker Hub — their `bitnami/appname` images may no longer be available; check `oci.bitnami.com` or use the official upstream image instead
- **ghcr.io images** frequently require authentication even when the repo is "public" on GitHub — test anonymous pull before committing to them
- **compdemocracy/polis** and some other community apps use private AWS ECR or require GitHub tokens — these cannot be packaged for general use without finding a public mirror
If no public image exists, note it clearly in the wild-directory `app.yaml` description rather than packaging a broken app.
### Re-run `wild app add` After Template Changes
`wild app deploy <app>` applies whatever is already compiled in the instance's `apps/` directory. It does **not** recompile templates from wild-directory. After modifying any template file in wild-directory, always re-run:
```bash
wild app add <app> # recompiles templates into the instance dir
wild app deploy <app> # applies the recompiled files
```
### PostgreSQL Connection Strings: Always Include `?sslmode=disable`
Wild Cloud's internal PostgreSQL does not have SSL enabled. Any app that constructs a PostgreSQL connection string must include `?sslmode=disable`, or the connection will fail with "The server does not support SSL connections."
```yaml
defaultSecrets:
- key: dbUrl
default: "postgresql://{{ .app.db.user }}:{{ .secrets.dbPassword }}@{{ .app.db.host }}:{{ .app.db.port }}/{{ .app.db.name }}?sslmode=disable"
```
Also add `PGSSLMODE=disable` as an env var for apps that use libpq directly.
### Redis URLs Must Include the Password
Wild Cloud's Redis requires authentication. Apps that accept a Redis URL must include the password:
```yaml
# In manifest.yaml requiredSecrets:
requiredSecrets:
- redis.password
# In deployment.yaml env — use K8s env var expansion for the URL:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-secrets
key: redis.password
- name: REDIS_URL
value: "redis://:$(REDIS_PASSWORD)@{{ .redis.host }}:6379"
```
The `$(VAR_NAME)` syntax is evaluated by Kubernetes at pod start from other env vars in the same container spec.
### Runtime-Specific Resource Defaults
Default resource requests vary by runtime. The standard 256Mi/50m is too low for heavier runtimes:
| Runtime | Suggested request | Notes |
|---------|------------------|-------|
| Go/Rust | 64128Mi | Very efficient |
| Python/Ruby/PHP | 128256Mi | Typical range |
| Node.js | 256512Mi | Add `NODE_OPTIONS=--max-old-space-size=<N>` to cap heap |
| JVM (Java/Kotlin/Scala) | 512Mi1Gi | Set `-Xmx` to keep within limit |
| Celery/Sidekiq workers | 256512Mi | Full worker process per instance |
For Node.js apps, cap memory use explicitly:
```yaml
- name: NODE_OPTIONS
value: "--max-old-space-size=768" # ~75% of 1Gi limit
```
### Health Check Probes for Django/FastAPI (ALLOWED_HOSTS)
Django and similar frameworks reject health probe requests because Kubernetes sends them directly to the pod IP, which is not in `ALLOWED_HOSTS`. Fix by adding a `Host` header to the probe:
```yaml
livenessProbe:
httpGet:
path: /
port: 8000
httpHeaders:
- name: Host
value: "{{ .domain }}"
```
### Images That Need Root + Special Capabilities
Some images run an init system (nginx, s6-overlay, custom entrypoint scripts) that requires elevated privileges to set up directories and drop to a lower-privileged user. These fail when `runAsNonRoot: true` or `capabilities.drop: [ALL]` is set.
**Pattern for nginx-based images** (e.g., community app frontends):
```yaml
spec:
securityContext:
runAsNonRoot: false
runAsUser: 0
seccompProfile:
type: RuntimeDefault
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
add: [CHOWN, SETUID, SETGID] # needed for nginx to drop to worker user
readOnlyRootFilesystem: false
```
**Pattern for linuxserver.io / s6-overlay images** (e.g., BookStack, most linuxserver images):
```yaml
spec:
securityContext:
runAsNonRoot: false
runAsUser: 0
seccompProfile:
type: RuntimeDefault
containers:
- name: app
securityContext:
readOnlyRootFilesystem: false # Only this — do NOT drop capabilities
```
Signs you need this pattern:
- `chown(...) failed (1: Operation not permitted)` — add `CHOWN` capability
- `Permission denied` on a directory during startup — try running as root
- Container exits immediately with code 1 despite image being "official"
### Hex-Format Secrets
Some apps require secrets in specific formats (e.g., exactly 64 hex chars for Outline). The default Wild Cloud secret generator produces random alphanumeric strings. Use `crypto.SHA256` in the manifest `default` field to derive a hex string from another secret:
```yaml
defaultSecrets:
- key: utilsSecret # generated first (alphanumeric)
- key: secretKey
default: '{{ crypto.SHA256 .secrets.utilsSecret }}' # hex derivative
```
The order of `defaultSecrets` matters — a referenced secret must appear before the entry that references it.
### Odoo (and Other Apps) Requiring Explicit Database Initialization
Some apps don't auto-initialize their database — they need to be told which database to use AND to install their schema. Odoo is the most common case: without explicit `-d DATABASE -i base` args, it starts in multi-database manager mode and all health checks fail.
```yaml
# In the container spec:
args: ["-d", "{{ .db.name }}", "-i", "base"]
```
The `-i base` flag is **idempotent**: it installs on first run and updates on subsequent runs. The downside is slightly slower startup on subsequent restarts. For very large apps, use an initContainer to run initialization separately from the main container.
Combine with a `startupProbe` to give the initialization time to complete:
```yaml
startupProbe:
httpGet:
path: /web/health
port: 8069
failureThreshold: 40 # 40 × 30s = 20 minutes
periodSeconds: 30
timeoutSeconds: 10
livenessProbe:
httpGet:
path: /web/health
port: 8069
initialDelaySeconds: 0 # startupProbe takes over startup detection
periodSeconds: 30
failureThreshold: 6
```
The `startupProbe` runs until it succeeds (or exhausts `failureThreshold`), at which point the liveness and readiness probes kick in with `initialDelaySeconds: 0`. This is cleaner than inflating `initialDelaySeconds` on the main probes.
### Gancio Re-deploy: "Non empty db" Crash
Gancio stores setup state in `config.json` on its data PVC. If the PVC is deleted or the app is re-added without wiping the database, gancio finds tables in PostgreSQL but no `config.json` and refuses to start.
**Fix**: Drop the schema before restarting:
```bash
kubectl exec -n postgres <postgres-pod> -- psql -U postgres gancio \
-c "DROP SCHEMA public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO gancio; GRANT ALL ON SCHEMA public TO public;"
```
Scale the deployment to 0 before dropping the schema to avoid active connection errors.
### Heavy-Framework Startup Times and Probe Delays
Frameworks with heavy startup initialization (Laravel, Rails, Spring Boot, Symfony) run tasks like package discovery, autoload generation, database migrations, and asset compilation before accepting HTTP requests. This takes 23 minutes — much longer than the default 60-second initial delay.
The failure mode is a restart loop: pod runs for ~2 minutes, liveness probe fires and fails, container is killed, new pod starts. The app never gets far enough to become Ready.
For these runtimes, use extended probe delays:
```yaml
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 120 # Give the framework time to fully initialize
periodSeconds: 30
failureThreshold: 6 # Tolerate transient failures during startup
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 60
periodSeconds: 15
failureThreshold: 3
```
**Runtimes that typically need this:**
| Runtime | Suggested liveness initialDelaySeconds |
|---------|----------------------------------------|
| PHP/Laravel/Symfony | 120s |
| Ruby on Rails | 90120s |
| Spring Boot (JVM) | 90120s |
| Django (with migrations) | 6090s |
| Node.js | 60s (usually sufficient) |
### MySQL db-init User Password Idempotency
The common pattern `CREATE USER IF NOT EXISTS 'user'@'%' IDENTIFIED BY '${PASSWORD}'` only sets the password when the user is first created. If the MySQL user already exists from a previous deployment (e.g., after deleting and re-adding an app without dropping the database), the password is silently unchanged and the new deployment fails with "Access denied".
Use the `CREATE ... IF NOT EXISTS` + `ALTER USER` pattern to make db-init jobs idempotent regardless of prior state:
```sql
CREATE DATABASE IF NOT EXISTS ${DB_DATABASE_NAME};
CREATE USER IF NOT EXISTS '${DB_USERNAME}'@'%' IDENTIFIED BY '${DB_PASSWORD}';
ALTER USER '${DB_USERNAME}'@'%' IDENTIFIED BY '${DB_PASSWORD}';
GRANT ALL PRIVILEGES ON ${DB_DATABASE_NAME}.* TO '${DB_USERNAME}'@'%';
FLUSH PRIVILEGES;
```
The `ALTER USER` line is safe even when the user was just created — it's a no-op in that case. This applies to MySQL/MariaDB only; PostgreSQL's `CREATE USER IF NOT EXISTS` does not exist but `DO $$ BEGIN IF NOT EXISTS ... END $$` patterns serve the same purpose.
## Validation Checklist ## Validation Checklist
Before submitting a new or modified app, verify: Before submitting a new or modified app, verify:
@@ -967,6 +1209,11 @@ Before submitting a new or modified app, verify:
- [ ] Ingresses include external-dns annotations - [ ] Ingresses include external-dns annotations
- [ ] Database apps include init jobs (if applicable) - [ ] Database apps include init jobs (if applicable)
- [ ] **Images**
- [ ] Docker images are publicly accessible (test anonymous pull)
- [ ] Image tags are specific versions, not `latest` or branch names
- [ ] Architecture constraints (amd64-only) noted in manifest if applicable
- [ ] **Testing** - [ ] **Testing**
- [ ] Templates compile successfully with sample config - [ ] Templates compile successfully with sample config
- [ ] App deploys without errors in test cluster - [ ] App deploys without errors in test cluster

View File

@@ -1,10 +1,10 @@
version: 1.6.2 version: 1.6.2-1
defaultConfig: defaultConfig:
namespace: aptly namespace: aptly
externalDnsDomain: "{{ .cloud.domain }}" externalDnsDomain: "{{ .cloud.domain }}"
domain: "aptly.{{ .cloud.domain }}" domain: "aptly.{{ .cloud.domain }}"
tlsSecretName: wildcard-wild-cloud-tls tlsSecretName: wildcard-wild-cloud-tls
storage: 50Gi storage: 5Gi
apiUser: aptly apiUser: aptly
defaultSecrets: defaultSecrets:
- key: apiPassword - key: apiPassword

View File

@@ -1,4 +1,4 @@
version: v1.17.2 version: v1.17.2-1
requires: requires:
- name: traefik - name: traefik
defaultConfig: defaultConfig:

View File

@@ -23,7 +23,7 @@ spec:
type: RuntimeDefault type: RuntimeDefault
containers: containers:
- name: community-search - name: community-search
image: payneio/community-search:0.1.1 image: payneio/community-search:0.1.2
imagePullPolicy: Always imagePullPolicy: Always
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
@@ -59,7 +59,7 @@ spec:
mountPath: /data mountPath: /data
resources: resources:
requests: requests:
cpu: 100m cpu: 50m
memory: 256Mi memory: 256Mi
limits: limits:
cpu: 1 cpu: 1

View File

@@ -1,11 +1,11 @@
version: 0.1.1-1 version: 0.1.2_1
requires: [] requires: []
defaultConfig: defaultConfig:
namespace: community-search namespace: community-search
externalDnsDomain: '{{ .cloud.domain }}' externalDnsDomain: '{{ .cloud.domain }}'
domain: search.{{ .cloud.domain }} domain: search.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls tlsSecretName: wildcard-wild-cloud-tls
storage: 10Gi storage: 2Gi
selfUrl: 'https://search.{{ .cloud.domain }}' selfUrl: 'https://search.{{ .cloud.domain }}'
selfName: 'Community Search' selfName: 'Community Search'
defaultSecrets: defaultSecrets:

View File

@@ -1,4 +1,4 @@
version: v1.12.0 version: v1.12.0-1
requires: requires:
- name: metallb - name: metallb
defaultConfig: defaultConfig:

View File

@@ -93,7 +93,7 @@ spec:
periodSeconds: 10 periodSeconds: 10
resources: resources:
requests: requests:
cpu: 100m cpu: 50m
memory: 200Mi memory: 200Mi
limits: limits:
cpu: 500m cpu: 500m

View File

@@ -1,4 +1,4 @@
version: v1.7.8 version: v1.7.8-1
requires: requires:
- name: longhorn - name: longhorn
- name: traefik - name: traefik

View File

@@ -62,6 +62,11 @@ spec:
- OPTIONS - OPTIONS
accessControlAllowOriginList: accessControlAllowOriginList:
- "*" - "*"
accessControlAllowHeaders:
- X-Requested-With
- Content-Type
- Authorization
- Date
accessControlMaxAge: 100 accessControlMaxAge: 100
customRequestHeaders: customRequestHeaders:
X-Forwarded-Proto: https X-Forwarded-Proto: https

View File

@@ -95,7 +95,7 @@ spec:
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: decidim-secrets name: decidim-secrets
key: smtpPassword key: smtp.password
- name: SMTP_DOMAIN - name: SMTP_DOMAIN
value: {{ .domain }} value: {{ .domain }}
- name: SMTP_FROM - name: SMTP_FROM
@@ -136,9 +136,9 @@ spec:
ephemeral-storage: 10Gi ephemeral-storage: 10Gi
memory: 4Gi memory: 4Gi
requests: requests:
cpu: 500m cpu: 50m
ephemeral-storage: 50Mi ephemeral-storage: 50Mi
memory: 1Gi memory: 256Mi
volumeMounts: volumeMounts:
- name: decidim-data - name: decidim-data
mountPath: /code/public/uploads mountPath: /code/public/uploads
@@ -203,8 +203,8 @@ spec:
cpu: 1000m cpu: 1000m
memory: 2Gi memory: 2Gi
requests: requests:
cpu: 250m cpu: 50m
memory: 512Mi memory: 256Mi
volumeMounts: volumeMounts:
- name: decidim-data - name: decidim-data
mountPath: /code/public/uploads mountPath: /code/public/uploads

View File

@@ -1,14 +1,12 @@
version: 0.31.0-1 version: 0.31.0-2
requires: requires:
- name: postgres - name: postgres
installed_as: postgres
- name: redis - name: redis
installed_as: redis
- name: smtp - name: smtp
defaultConfig: defaultConfig:
namespace: decidim namespace: decidim
externalDnsDomain: '{{ .cloud.domain }}' externalDnsDomain: '{{ .cloud.domain }}'
storage: 20Gi storage: 2Gi
systemAdminEmail: '{{ .operator.email }}' systemAdminEmail: '{{ .operator.email }}'
siteName: 'Decidim' siteName: 'Decidim'
domain: decidim.{{ .cloud.domain }} domain: decidim.{{ .cloud.domain }}
@@ -32,10 +30,10 @@ defaultSecrets:
- key: systemAdminPassword - key: systemAdminPassword
- key: secretKeyBase - key: secretKeyBase
default: "{{ random.AlphaNum 128 }}" default: "{{ random.AlphaNum 128 }}"
- key: smtpPassword
- key: dbPassword - key: dbPassword
- key: dbUrl - key: dbUrl
default: "postgres://{{ .app.db.user }}:{{ .secrets.dbPassword }}@{{ .app.db.host }}:{{ .app.db.port }}/{{ .app.db.name }}" default: "postgres://{{ .app.db.user }}:{{ .secrets.dbPassword }}@{{ .app.db.host }}:{{ .app.db.port }}/{{ .app.db.name }}"
requiredSecrets: requiredSecrets:
- postgres.password - postgres.password
- redis.password - redis.password
- smtp.password

View File

@@ -185,9 +185,9 @@ spec:
ephemeral-storage: 10Gi ephemeral-storage: 10Gi
memory: 8Gi memory: 8Gi
requests: requests:
cpu: 750m cpu: 50m
ephemeral-storage: 50Mi ephemeral-storage: 50Mi
memory: 1Gi memory: 512Mi
volumeMounts: volumeMounts:
- name: discourse-data - name: discourse-data
mountPath: /shared mountPath: /shared
@@ -292,7 +292,7 @@ spec:
ephemeral-storage: 2Gi ephemeral-storage: 2Gi
memory: 1Gi memory: 1Gi
requests: requests:
cpu: 375m cpu: 50m
ephemeral-storage: 50Mi ephemeral-storage: 50Mi
memory: 512Mi memory: 512Mi
volumeMounts: volumeMounts:

View File

@@ -1,4 +1,4 @@
version: 3.5.3-1 version: 3.5.3-3
requires: requires:
- name: postgres - name: postgres
- name: redis - name: redis
@@ -6,7 +6,7 @@ requires:
defaultConfig: defaultConfig:
namespace: discourse namespace: discourse
externalDnsDomain: '{{ .cloud.domain }}' externalDnsDomain: '{{ .cloud.domain }}'
storage: 10Gi storage: 2Gi
adminEmail: '{{ .operator.email }}' adminEmail: '{{ .operator.email }}'
adminUsername: admin adminUsername: admin
siteName: 'Community' siteName: 'Community'
@@ -30,7 +30,7 @@ defaultConfig:
defaultSecrets: defaultSecrets:
- key: adminPassword - key: adminPassword
- key: secretKeyBase - key: secretKeyBase
default: "{{ random.AlphaNum 64 }}" default: "{{ random.Hex 32 }}"
- key: smtpPassword - key: smtpPassword
- key: dbPassword - key: dbPassword
- key: dbUrl - key: dbUrl

View File

@@ -5,4 +5,4 @@ requires:
defaultConfig: defaultConfig:
namespace: docker-registry namespace: docker-registry
host: "registry.{{ .cloud.internalDomain }}" host: "registry.{{ .cloud.internalDomain }}"
storage: "100Gi" storage: 5Gi

View File

@@ -26,7 +26,7 @@ spec:
cpu: 500m cpu: 500m
memory: 50Mi memory: 50Mi
requests: requests:
cpu: 100m cpu: 50m
memory: 10Mi memory: 10Mi
livenessProbe: livenessProbe:
httpGet: httpGet:

View File

@@ -1,4 +1,4 @@
version: 1.0.0 version: 1.0.0-1
defaultConfig: defaultConfig:
namespace: example-admin namespace: example-admin
externalDnsDomain: '{{ .cloud.domain }}' externalDnsDomain: '{{ .cloud.domain }}'

View File

@@ -24,7 +24,7 @@ spec:
cpu: 500m cpu: 500m
memory: 128Mi memory: 128Mi
requests: requests:
cpu: 100m cpu: 50m
memory: 32Mi memory: 32Mi
livenessProbe: livenessProbe:
httpGet: httpGet:

View File

@@ -1,4 +1,4 @@
version: 1.0.0 version: 1.0.0-1
defaultConfig: defaultConfig:
namespace: example-app namespace: example-app
externalDnsDomain: '{{ .cloud.domain }}' externalDnsDomain: '{{ .cloud.domain }}'

View File

@@ -1,4 +1,4 @@
version: v0.13.4 version: v0.13.4-1
deploymentName: external-dns deploymentName: external-dns
requires: requires:
- name: cert-manager - name: cert-manager

View File

@@ -26,8 +26,8 @@ spec:
- name: MYSQL_ROOT_PASSWORD - name: MYSQL_ROOT_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: mysql-secrets name: ghost-secrets
key: rootPassword key: mysql.rootPassword
- name: DB_HOSTNAME - name: DB_HOSTNAME
value: "{{ .db.host }}" value: "{{ .db.host }}"
- name: DB_PORT - name: DB_PORT

View File

@@ -2,7 +2,7 @@ apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: ghost name: ghost
namespace: ghost namespace: {{ .namespace }}
spec: spec:
replicas: 1 replicas: 1
strategy: strategy:
@@ -15,118 +15,91 @@ spec:
labels: labels:
component: web component: web
spec: spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers: containers:
- name: ghost - name: ghost
image: docker.io/bitnami/ghost:5.118.1-debian-12-r0 image: ghost:5.130.6-alpine
ports: ports:
- name: http - name: http
containerPort: 2368 containerPort: 2368
protocol: TCP protocol: TCP
env: env:
- name: BITNAMI_DEBUG - name: NODE_ENV
value: "false" value: production
- name: ALLOW_EMPTY_PASSWORD - name: url
value: "yes" value: "https://{{ .domain }}"
- name: GHOST_DATABASE_HOST - name: database__client
value: {{ .db.host }} value: mysql
- name: GHOST_DATABASE_PORT_NUMBER - name: database__connection__host
value: "{{ .db.host }}"
- name: database__connection__port
value: "{{ .db.port }}" value: "{{ .db.port }}"
- name: GHOST_DATABASE_NAME - name: database__connection__database
value: {{ .db.name }} value: "{{ .db.name }}"
- name: GHOST_DATABASE_USER - name: database__connection__user
value: {{ .db.user }} value: "{{ .db.user }}"
- name: GHOST_DATABASE_PASSWORD - name: database__connection__password
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: ghost-secrets name: ghost-secrets
key: dbPassword key: dbPassword
- name: GHOST_HOST - name: mail__transport
value: {{ .domain }}
- name: GHOST_PORT_NUMBER
value: "2368"
- name: GHOST_USERNAME
value: {{ .adminUser }}
- name: GHOST_PASSWORD
valueFrom:
secretKeyRef:
name: ghost-secrets
key: adminPassword
- name: GHOST_EMAIL
value: {{ .adminEmail }}
- name: GHOST_BLOG_TITLE
value: {{ .blogTitle }}
- name: GHOST_ENABLE_HTTPS
value: "yes"
- name: GHOST_EXTERNAL_HTTP_PORT_NUMBER
value: "80"
- name: GHOST_EXTERNAL_HTTPS_PORT_NUMBER
value: "443"
- name: GHOST_SKIP_BOOTSTRAP
value: "no"
- name: GHOST_SMTP_SERVICE
value: SMTP value: SMTP
- name: GHOST_SMTP_HOST - name: mail__options__host
value: {{ .smtp.host }} value: "{{ .smtp.host }}"
- name: GHOST_SMTP_PORT - name: mail__options__port
value: "{{ .smtp.port }}" value: "{{ .smtp.port }}"
- name: GHOST_SMTP_USER - name: mail__options__auth__user
value: {{ .smtp.user }} value: "{{ .smtp.user }}"
- name: GHOST_SMTP_PASSWORD - name: mail__options__auth__pass
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: ghost-secrets name: ghost-secrets
key: smtpPassword key: smtpPassword
- name: GHOST_SMTP_FROM_ADDRESS - name: mail__from
value: {{ .smtp.from }} value: "{{ .smtp.from }}"
resources: resources:
limits: limits:
cpu: 375m cpu: 1000m
ephemeral-storage: 2Gi ephemeral-storage: 2Gi
memory: 384Mi memory: 512Mi
requests: requests:
cpu: 250m cpu: 50m
ephemeral-storage: 50Mi ephemeral-storage: 50Mi
memory: 256Mi memory: 256Mi
volumeMounts: volumeMounts:
- name: ghost-data - name: ghost-data
mountPath: /bitnami/ghost mountPath: /var/lib/ghost/content
livenessProbe: livenessProbe:
tcpSocket: httpGet:
path: /ghost/api/admin/site/
port: 2368 port: 2368
initialDelaySeconds: 120 initialDelaySeconds: 120
timeoutSeconds: 5 timeoutSeconds: 5
periodSeconds: 10 periodSeconds: 30
successThreshold: 1
failureThreshold: 6 failureThreshold: 6
readinessProbe: readinessProbe:
httpGet: httpGet:
path: / path: /ghost/api/admin/site/
port: http port: 2368
scheme: HTTP initialDelaySeconds: 60
httpHeaders: timeoutSeconds: 5
- name: x-forwarded-proto periodSeconds: 15
value: https failureThreshold: 3
initialDelaySeconds: 30
timeoutSeconds: 3
periodSeconds: 5
successThreshold: 1
failureThreshold: 6
securityContext: securityContext:
allowPrivilegeEscalation: false
capabilities: capabilities:
drop: drop:
- ALL - ALL
privileged: false
runAsUser: 1001
runAsGroup: 1001
runAsNonRoot: true
readOnlyRootFilesystem: false readOnlyRootFilesystem: false
allowPrivilegeEscalation: false
seccompProfile:
type: RuntimeDefault
volumes: volumes:
- name: ghost-data - name: ghost-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: ghost-data claimName: ghost-data
restartPolicy: Always restartPolicy: Always
securityContext:
fsGroup: 1001

View File

@@ -1,4 +1,4 @@
version: 5.118.1-1 version: 5.130.6-1
requires: requires:
- name: mysql - name: mysql
- name: smtp - name: smtp
@@ -7,10 +7,7 @@ defaultConfig:
externalDnsDomain: '{{ .cloud.domain }}' externalDnsDomain: '{{ .cloud.domain }}'
domain: ghost.{{ .cloud.domain }} domain: ghost.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls tlsSecretName: wildcard-wild-cloud-tls
storage: 10Gi storage: 2Gi
adminUser: admin
adminEmail: '{{ .operator.email }}'
blogTitle: My Blog
db: db:
host: '{{ .apps.mysql.host }}' host: '{{ .apps.mysql.host }}'
port: '3306' port: '3306'
@@ -22,7 +19,6 @@ defaultConfig:
from: '{{ .apps.smtp.from }}' from: '{{ .apps.smtp.from }}'
user: '{{ .apps.smtp.user }}' user: '{{ .apps.smtp.user }}'
defaultSecrets: defaultSecrets:
- key: adminPassword
- key: dbPassword - key: dbPassword
- key: smtpPassword - key: smtpPassword
requiredSecrets: requiredSecrets:

View File

@@ -1,4 +1,4 @@
version: 1.24.3-1 version: 1.24.3-2
requires: requires:
- name: postgres - name: postgres
- name: smtp - name: smtp
@@ -8,7 +8,7 @@ defaultConfig:
appName: Gitea appName: Gitea
domain: gitea.{{ .cloud.domain }} domain: gitea.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls tlsSecretName: wildcard-wild-cloud-tls
storage: 10Gi storage: 2Gi
adminUser: admin adminUser: admin
adminEmail: "{{ .operator.email }}" adminEmail: "{{ .operator.email }}"
db: db:

View File

@@ -1,12 +1,12 @@
version: 1.135.3-1 version: 1.135.3-2
requires: requires:
- name: redis - name: redis
- name: postgres - name: postgres
defaultConfig: defaultConfig:
namespace: immich namespace: immich
externalDnsDomain: '{{ .cloud.domain }}' externalDnsDomain: '{{ .cloud.domain }}'
storage: 250Gi storage: 5Gi
cacheStorage: 10Gi cacheStorage: 5Gi
domain: immich.{{ .cloud.domain }} domain: immich.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls tlsSecretName: wildcard-wild-cloud-tls
db: db:

View File

@@ -1,4 +1,4 @@
version: 0.17.1-1 version: 0.17.1-2
requires: requires:
- name: postgres - name: postgres
- name: smtp - name: smtp

View File

@@ -52,7 +52,7 @@ spec:
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: lemmy-secrets name: lemmy-secrets
key: smtpPassword key: smtp.password
- name: ADMIN_PASSWORD - name: ADMIN_PASSWORD
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:

View File

@@ -1,4 +1,4 @@
version: 0.19.15-2 version: 0.19.15-3
requires: requires:
- name: postgres - name: postgres
- name: smtp - name: smtp
@@ -7,8 +7,8 @@ defaultConfig:
externalDnsDomain: lemmy.{{ .cloud.baseDomain }} externalDnsDomain: lemmy.{{ .cloud.baseDomain }}
domain: lemmy.{{ .cloud.domain }} domain: lemmy.{{ .cloud.domain }}
tlsSecretName: wildcard-wild-cloud-tls tlsSecretName: wildcard-wild-cloud-tls
storage: 10Gi storage: 2Gi
pictrsStorage: 50Gi pictrsStorage: 2Gi
db: db:
host: '{{ .apps.postgres.host }}' host: '{{ .apps.postgres.host }}'
port: '{{ .apps.postgres.port }}' port: '{{ .apps.postgres.port }}'
@@ -24,6 +24,6 @@ defaultSecrets:
- key: dbPassword - key: dbPassword
- key: adminPassword - key: adminPassword
- key: jwtSecret - key: jwtSecret
- key: smtpPassword
requiredSecrets: requiredSecrets:
- postgres.password - postgres.password
- smtp.password

View File

@@ -53,7 +53,7 @@ spec:
ephemeral-storage: 1Gi ephemeral-storage: 1Gi
memory: 512Mi memory: 512Mi
requests: requests:
cpu: 100m cpu: 50m
ephemeral-storage: 50Mi ephemeral-storage: 50Mi
memory: 128Mi memory: 128Mi
volumeMounts: volumeMounts:

View File

@@ -1,4 +1,4 @@
version: 5.0.3-1 version: 5.0.3-2
requires: requires:
- name: postgres - name: postgres
defaultConfig: defaultConfig:

View File

@@ -1,4 +1,4 @@
version: v1.8.1-2 version: v1.8.1-3
deploymentName: longhorn-ui deploymentName: longhorn-ui
requires: requires:
- name: traefik - name: traefik

View File

@@ -28,7 +28,7 @@ spec:
resources: resources:
requests: requests:
memory: 64Mi memory: 64Mi
cpu: 100m cpu: 50m
limits: limits:
memory: 128Mi memory: 128Mi
cpu: 200m cpu: 200m

View File

@@ -1,4 +1,4 @@
version: 1.6.32-2 version: 1.6.32-3
requires: [] requires: []
defaultConfig: defaultConfig:
namespace: memcached namespace: memcached

View File

@@ -1,4 +1,4 @@
version: v0.15.0 version: v0.15.0-1
deploymentName: controller deploymentName: controller
defaultConfig: defaultConfig:
namespace: metallb-system namespace: metallb-system

View File

@@ -1,9 +1,9 @@
version: 9.1.0-2 version: 9.1.0-3
requires: [] requires: []
defaultConfig: defaultConfig:
namespace: mysql namespace: mysql
host: mysql.mysql.svc.cluster.local host: mysql.mysql.svc.cluster.local
storage: 20Gi storage: 2Gi
dbName: mysql dbName: mysql
user: mysql user: mysql
backup: backup:

View File

@@ -93,8 +93,8 @@ spec:
cpu: 1000m cpu: 1000m
memory: 1Gi memory: 1Gi
requests: requests:
cpu: 500m cpu: 50m
memory: 512Mi memory: 256Mi
volumeMounts: volumeMounts:
- name: data - name: data
mountPath: /var/lib/mysql mountPath: /var/lib/mysql

View File

@@ -1,4 +1,4 @@
version: v4.0.18-4 version: v4.0.18-5
scripts: scripts:
- name: check-nfs - name: check-nfs
path: scripts/check-nfs.sh path: scripts/check-nfs.sh

View File

@@ -1,4 +1,4 @@
version: v0.17.1 version: v0.17.1-1
deploymentName: nvidia-device-plugin-daemonset deploymentName: nvidia-device-plugin-daemonset
requires: requires:
- name: node-feature-discovery - name: node-feature-discovery

View File

@@ -56,8 +56,8 @@ spec:
mountPath: /app/backend/data mountPath: /app/backend/data
resources: resources:
requests: requests:
cpu: 100m cpu: 50m
memory: 512Mi memory: 256Mi
limits: limits:
cpu: 1 cpu: 1
memory: 2Gi memory: 2Gi

View File

@@ -1,9 +1,9 @@
version: 0.9.5-1 version: 0.9.5-2
requires: [] requires: []
defaultConfig: defaultConfig:
namespace: open-webui namespace: open-webui
externalDnsDomain: '{{ .cloud.domain }}' externalDnsDomain: '{{ .cloud.domain }}'
storage: 10Gi storage: 2Gi
domain: chat.{{ .cloud.domain }} domain: chat.{{ .cloud.domain }}
vllmApiUrl: http://vllm-service.llm.svc.cluster.local:8000/v1 vllmApiUrl: http://vllm-service.llm.svc.cluster.local:8000/v1
adminEmail: '{{ .operator.email }}' adminEmail: '{{ .operator.email }}'

View File

@@ -20,4 +20,5 @@ data:
OPENPROJECT_RAILS__CACHE__STORE: "memcache" OPENPROJECT_RAILS__CACHE__STORE: "memcache"
OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "" OPENPROJECT_RAILS__RELATIVE__URL__ROOT: ""
POSTGRES_STATEMENT_TIMEOUT: "120s" POSTGRES_STATEMENT_TIMEOUT: "120s"
WEB_CONCURRENCY: "1"
... ...

View File

@@ -1,11 +1,11 @@
version: 16.1.1-1 version: 16.1.1-3
requires: requires:
- name: postgres - name: postgres
- name: memcached - name: memcached
defaultConfig: defaultConfig:
namespace: openproject namespace: openproject
externalDnsDomain: '{{ .cloud.domain }}' externalDnsDomain: '{{ .cloud.domain }}'
storage: 5Gi storage: 2Gi
adminUserName: OpenProject Admin adminUserName: OpenProject Admin
adminUserEmail: '{{ .operator.email }}' adminUserEmail: '{{ .operator.email }}'
domain: openproject.{{ .cloud.domain }} domain: openproject.{{ .cloud.domain }}

View File

@@ -116,7 +116,7 @@ spec:
limits: limits:
memory: 512Mi memory: 512Mi
requests: requests:
memory: 512Mi memory: 256Mi
volumeMounts: volumeMounts:
- mountPath: /tmp - mountPath: /tmp
name: tmp name: tmp

View File

@@ -99,7 +99,7 @@ spec:
limits: limits:
memory: 1Gi memory: 1Gi
requests: requests:
memory: 512Mi memory: 256Mi
volumeMounts: volumeMounts:
- mountPath: /tmp - mountPath: /tmp
name: tmp name: tmp
@@ -180,5 +180,5 @@ spec:
cpu: "4" cpu: "4"
memory: 4Gi memory: 4Gi
requests: requests:
cpu: 250m cpu: 50m
memory: 512Mi memory: 256Mi

View File

@@ -99,7 +99,7 @@ spec:
limits: limits:
memory: 1Gi memory: 1Gi
requests: requests:
memory: 512Mi memory: 256Mi
volumeMounts: volumeMounts:
- mountPath: /tmp - mountPath: /tmp
name: tmp name: tmp
@@ -149,5 +149,5 @@ spec:
cpu: "4" cpu: "4"
memory: 4Gi memory: 4Gi
requests: requests:
cpu: 250m cpu: 50m
memory: 512Mi memory: 256Mi

View File

@@ -18,19 +18,25 @@ spec:
containers: containers:
- name: postgres - name: postgres
image: "pgvector/pgvector:pg15" image: "pgvector/pgvector:pg15"
command: ["/bin/bash", "-c"]
args: args:
[ - |
"-c", # Install English Hunspell dictionary (needed by apps like Zulip for full-text search)
"tcp_keepalives_idle=600", if [ ! -f /usr/share/postgresql/15/tsearch_data/en_us.dict ]; then
"-c", apt-get install -y -qq hunspell-en-us 2>/dev/null || true
"tcp_keepalives_interval=30", [ -f /usr/share/hunspell/en_US.aff ] && cp /usr/share/hunspell/en_US.aff /usr/share/postgresql/15/tsearch_data/en_us.affix
"-c", [ -f /usr/share/hunspell/en_US.dic ] && cp /usr/share/hunspell/en_US.dic /usr/share/postgresql/15/tsearch_data/en_us.dict
"tcp_keepalives_count=3", fi
"-c", # Install PostGIS (needed by apps like Mobilizon for geographic data)
"statement_timeout=300000", if [ ! -f /usr/share/postgresql/15/extension/postgis.control ]; then
"-c", apt-get update -qq 2>/dev/null && apt-get install -y -qq postgresql-15-postgis-3 postgresql-15-postgis-3-scripts 2>/dev/null || true
"idle_in_transaction_session_timeout=600000", fi
] exec docker-entrypoint.sh postgres \
-c tcp_keepalives_idle=600 \
-c tcp_keepalives_interval=30 \
-c tcp_keepalives_count=3 \
-c statement_timeout=300000 \
-c idle_in_transaction_session_timeout=600000
ports: ports:
- containerPort: 5432 - containerPort: 5432
env: env:

View File

@@ -1,11 +1,11 @@
version: 1.0.0-2 version: 1.0.0-6
defaultConfig: defaultConfig:
namespace: postgres namespace: postgres
host: postgres.postgres.svc.cluster.local host: postgres.postgres.svc.cluster.local
port: 5432 port: 5432
database: postgres database: postgres
user: postgres user: postgres
storage: 10Gi storage: 2Gi
backup: backup:
restoreMode: in-place restoreMode: in-place
defaultSecrets: defaultSecrets:

View File

@@ -1,4 +1,4 @@
version: 1.0.0-2 version: 1.0.0-3
defaultConfig: defaultConfig:
namespace: redis namespace: redis
host: redis.redis.svc.cluster.local host: redis.redis.svc.cluster.local

View File

@@ -1,4 +1,4 @@
version: 1.0.0 version: 1.0.0-2
defaultConfig: defaultConfig:
host: "" host: ""
port: "465" port: "465"
@@ -6,3 +6,5 @@ defaultConfig:
from: "no-reply@{{ .cloud.domain }}" from: "no-reply@{{ .cloud.domain }}"
tls: "true" tls: "true"
startTls: "true" startTls: "true"
defaultSecrets:
- key: password

View File

@@ -1,4 +1,4 @@
version: v8.1.0_1 version: v8.1.0_1-1
deploymentName: snapshot-controller deploymentName: snapshot-controller
defaultConfig: defaultConfig:
namespace: kube-system namespace: kube-system

View File

@@ -73,16 +73,18 @@ spec:
containerPort: 8080 containerPort: 8080
protocol: TCP protocol: TCP
- name: web - name: web
containerPort: 8000 containerPort: 80
protocol: TCP protocol: TCP
- name: websecure - name: websecure
containerPort: 8443 containerPort: 443
protocol: TCP protocol: TCP
securityContext: securityContext:
allowPrivilegeEscalation: false allowPrivilegeEscalation: false
capabilities: capabilities:
drop: drop:
- ALL - ALL
add:
- NET_BIND_SERVICE
readOnlyRootFilesystem: true readOnlyRootFilesystem: true
volumeMounts: volumeMounts:
- name: data - name: data
@@ -100,8 +102,8 @@ spec:
- "--experimental.plugins.bouncer.version=v1.4.2" - "--experimental.plugins.bouncer.version=v1.4.2"
- "--entryPoints.metrics.address=:9100/tcp" - "--entryPoints.metrics.address=:9100/tcp"
- "--entryPoints.traefik.address=:8080/tcp" - "--entryPoints.traefik.address=:8080/tcp"
- "--entryPoints.web.address=:8000/tcp" - "--entryPoints.web.address=:80/tcp"
- "--entryPoints.websecure.address=:8443/tcp" - "--entryPoints.websecure.address=:443/tcp"
- "--api.dashboard=true" - "--api.dashboard=true"
- "--ping=true" - "--ping=true"
- "--metrics.prometheus=true" - "--metrics.prometheus=true"
@@ -119,6 +121,9 @@ spec:
- "--accesslog.format=json" - "--accesslog.format=json"
- "--log.level=INFO" - "--log.level=INFO"
- "--entryPoints.websecure.http.middlewares=crowdsec-security-chain@kubernetescrd" - "--entryPoints.websecure.http.middlewares=crowdsec-security-chain@kubernetescrd"
- "--entryPoints.web.http.redirections.entryPoint.to=websecure"
- "--entryPoints.web.http.redirections.entryPoint.scheme=https"
- "--entryPoints.web.http.redirections.entryPoint.permanent=true"
env: env:
- name: POD_NAME - name: POD_NAME

View File

@@ -1,4 +1,4 @@
version: v3.4 version: v3.4-1
requires: requires:
- name: metallb - name: metallb
defaultConfig: defaultConfig:

View File

@@ -1,4 +1,4 @@
version: v1.0.1 version: v1.0.1-1
deploymentName: netdebug deploymentName: netdebug
defaultConfig: defaultConfig:
namespace: debug namespace: debug

View File

@@ -53,7 +53,7 @@ spec:
cpu: 200m cpu: 200m
memory: 256Mi memory: 256Mi
requests: requests:
cpu: 100m cpu: 50m
memory: 128Mi memory: 128Mi
securityContext: securityContext:
privileged: true privileged: true

View File

@@ -1,4 +1,4 @@
version: 0.5.4-1 version: 0.5.4-2
requires: [] requires: []
defaultConfig: defaultConfig:
namespace: llm namespace: llm