diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index cf653f2..d221bbc 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -103,6 +103,20 @@ jobs: fi echo "Alle compose-bestanden zijn geldig" + - name: Images controleren + run: | + set -euo pipefail + # Een image dat niet meer bestaat merk je anders pas als iemand op + # installeren drukt. Zo verdween Huntarr nadat dat project offline + # ging, en wezen Forgejo, Planka en Baby Buddy naar tags die er niet + # zijn. Draait bewust rustig: registries knijpen af, en een afgeknepen + # verzoek is niet te onderscheiden van een ontbrekend image. + docker run --rm \ + -v "$PWD:/w" -w /w \ + -e PYTHONDONTWRITEBYTECODE=1 \ + python:3.12-slim \ + python tools/controleer_images.py + - name: Inloggen op de registry run: | set -euo pipefail diff --git a/.forgejo/workflows/deploy-prod.yml b/.forgejo/workflows/deploy-prod.yml index e749177..a288daa 100644 --- a/.forgejo/workflows/deploy-prod.yml +++ b/.forgejo/workflows/deploy-prod.yml @@ -91,6 +91,20 @@ jobs: fi echo "Alle compose-bestanden zijn geldig" + - name: Images controleren + run: | + set -euo pipefail + # Een image dat niet meer bestaat merk je anders pas als iemand op + # installeren drukt. Zo verdween Huntarr nadat dat project offline + # ging, en wezen Forgejo, Planka en Baby Buddy naar tags die er niet + # zijn. Draait bewust rustig: registries knijpen af, en een afgeknepen + # verzoek is niet te onderscheiden van een ontbrekend image. + docker run --rm \ + -v "$PWD:/w" -w /w \ + -e PYTHONDONTWRITEBYTECODE=1 \ + python:3.12-slim \ + python tools/controleer_images.py + - name: Sync code naar deploy-directory run: | set -euo pipefail diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 291bd21..e00e877 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -91,6 +91,20 @@ jobs: fi echo "Alle compose-bestanden zijn geldig" + - name: Images controleren + run: | + set -euo pipefail + # Een image dat niet meer bestaat merk je anders pas als iemand op + # installeren drukt. Zo verdween Huntarr nadat dat project offline + # ging, en wezen Forgejo, Planka en Baby Buddy naar tags die er niet + # zijn. Draait bewust rustig: registries knijpen af, en een afgeknepen + # verzoek is niet te onderscheiden van een ontbrekend image. + docker run --rm \ + -v "$PWD:/w" -w /w \ + -e PYTHONDONTWRITEBYTECODE=1 \ + python:3.12-slim \ + python tools/controleer_images.py + - name: Sync code naar deploy-directory run: | set -euo pipefail diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ce27a3..7a07c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +# v0.8.20-beta — Prometheus startte niet, en 23 apps erbij + +**Prometheus stond kapot in de catalogus.** Het sjabloon koppelde een lege map op +`/etc/prometheus` en startte met `--config.file=/etc/prometheus/prometheus.yml` — +een bestand dat er nooit kwam. Prometheus stopt daar meteen op. En zelfs mét +configuratie viel er niets te meten: er was in de hele catalogus geen +node-exporter en geen cAdvisor. Er zit nu een startconfiguratie bij, met +koppelvelden naar allebei. + +Datzelfde patroon zat op meer plekken, en dat is waar de meeste apps van deze +ronde vandaan komen — niet nieuwe categorieën, maar de ontbrekende helft van wat +er al stond: + +- **node-exporter**, **cAdvisor** en **Alertmanager** maken Prometheus en Grafana + pas bruikbaar +- **Loki** en **Grafana Alloy** bewaren logs; Dozzle liet alleen zien wat er nú + gebeurt, en na een herstart was de oorzaak weg +- **Unbound** zoekt DNS zelf op. Pi-hole en AdGuard blokkeerden wel reclame, maar + stuurden de rest gewoon door naar Google of Cloudflare + +**Domeinen die ontbraken:** evcc (zonnepanelen, thuisbatterij en laadpaal op +elkaar afstemmen), Healthchecks (merkt dat een cronjob níét liep — Uptime Kuma +merkt alleen dat iets uit ligt), Infisical (geheimen voor je applicaties, waar +Vaultwarden voor mensen is), Music Assistant en pgAdmin. + +**Verder:** Crafty Controller, Manyfold, ErsatzTV, FitTrackee, ComfyUI, Omada +Controller, en de zwaardere Zabbix, Graylog, Wazuh, NetBox, Seafile en OpenCloud. +Van 218 naar 241 apps. Monitoring gaat van 18 naar 27. + +**Een controle die zichzelf tegensprak.** De image-controle draaide met zestien +verzoeken tegelijk en meldde negen ontbrekende lscr.io-images. Alle negen bleken +te bestaan: registries knijpen af, en een afgeknepen verzoek ziet er precies zo +uit als een image dat er niet is. Bijna had ik negen werkende sjablonen +"gerepareerd". Die controle zit nu in `tools/controleer_images.py`, met vier +tegelijk en opnieuw proberen bij twijfel, en draait als stap in CI. + # v0.8.10-beta — Vervangers voor big tech, en sleutels in het juiste formaat **Een sleutel is niet zomaar een willekeurige string.** De generator maakte diff --git a/README.md b/README.md index 3a6e4e7..4f4b7f0 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ IP-adres in je netwerk geven. | | | |---|---| -| **App-catalogus** | 218 kant-en-klare apps, ingedeeld in categorieën met kleur. Zoeken, filteren en installeren met een formulier in plaats van YAML. | +| **App-catalogus** | 241 kant-en-klare apps, ingedeeld in categorieën met kleur. Zoeken, filteren en installeren met een formulier in plaats van YAML. | | **Stacks beheren** | Starten, stoppen, bijwerken en herstarten — per stack of per losse container, met logs en live CPU-/geheugengebruik. | | **Apps koppelen** | Stacks komen op een gedeeld netwerk, zodat ze elkaar op naam bereiken. Een veld dat naar een andere app verwijst toont een keuzelijst met wat je al draait. | | **Eigen IP per stack** | Met macvlan of ipvlan krijgt een app een eigen adres in je LAN in plaats van poorten op de host. | diff --git a/VERSION b/VERSION index 6ec662d..3c2e9ad 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.10-beta +0.8.20-beta diff --git a/apps/alertmanager/files/alertmanager.yml b/apps/alertmanager/files/alertmanager.yml new file mode 100644 index 0000000..3915199 --- /dev/null +++ b/apps/alertmanager/files/alertmanager.yml @@ -0,0 +1,13 @@ +# Startconfiguratie. Zonder dit bestand weigert Alertmanager te starten. +route: + receiver: standaard + group_by: [alertname] + group_wait: 30s + repeat_interval: 4h + +receivers: + - name: standaard +<%- if webhook_url %> + webhook_configs: + - url: "<< webhook_url >>" +<%- endif %> diff --git a/apps/alertmanager/files/compose.yaml b/apps/alertmanager/files/compose.yaml new file mode 100644 index 0000000..95d978e --- /dev/null +++ b/apps/alertmanager/files/compose.yaml @@ -0,0 +1,13 @@ +services: + << service_name >>: + image: quay.io/prometheus/alertmanager:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/etc/alertmanager + - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro + command: --config.file=/etc/alertmanager/alertmanager.yml --storage.path=/alertmanager + ports: + - "<< port >>:9093" + restart: unless-stopped diff --git a/apps/alertmanager/template.json b/apps/alertmanager/template.json new file mode 100644 index 0000000..5097de6 --- /dev/null +++ b/apps/alertmanager/template.json @@ -0,0 +1,77 @@ +{ + "kind": "compose", + "metadata": { + "name": "Alertmanager", + "description": "Neemt de alarmen van Prometheus aan, bundelt ze en stuurt ze door naar e-mail, ntfy of Discord. Onderdrukt herhalingen.", + "tags": [ + "monitoring", + "meldingen", + "prometheus" + ], + "icon": { + "provider": "selfhst", + "id": "prometheus" + }, + "version": { + "name": "latest" + }, + "categories": [ + "monitoring", + "communicatie" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "alertmanager", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 9093, + "required": true + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "webhook_url", + "type": "str", + "title": "Webhook", + "default": "", + "required": false, + "connect": { + "app": "ntfy", + "scheme": "http", + "port": 80 + }, + "help": "Waar de alarmen naartoe gaan. Wijs dit naar ntfy of Gotify, of laat het leeg en stel het later in het bestand in." + } + ] + } + ] +} diff --git a/apps/alloy/files/compose.yaml b/apps/alloy/files/compose.yaml new file mode 100644 index 0000000..0812cfc --- /dev/null +++ b/apps/alloy/files/compose.yaml @@ -0,0 +1,14 @@ +services: + << service_name >>: + image: grafana/alloy:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/var/lib/alloy/data + - ./config.alloy:/etc/alloy/config.alloy:ro + - /var/run/docker.sock:/var/run/docker.sock:ro + command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy + ports: + - "<< port >>:12345" + restart: unless-stopped diff --git a/apps/alloy/files/config.alloy b/apps/alloy/files/config.alloy new file mode 100644 index 0000000..b0f0134 --- /dev/null +++ b/apps/alloy/files/config.alloy @@ -0,0 +1,16 @@ +// Leest de logs van elke draaiende container en stuurt ze naar Loki. +discovery.docker "containers" { + host = "unix:///var/run/docker.sock" +} + +loki.source.docker "containers" { + host = "unix:///var/run/docker.sock" + targets = discovery.docker.containers.targets + forward_to = [loki.write.opslag.receiver] +} + +loki.write "opslag" { + endpoint { + url = "<< loki_url >>/loki/api/v1/push" + } +} diff --git a/apps/alloy/template.json b/apps/alloy/template.json new file mode 100644 index 0000000..b9f1916 --- /dev/null +++ b/apps/alloy/template.json @@ -0,0 +1,76 @@ +{ + "kind": "compose", + "metadata": { + "name": "Grafana Alloy", + "description": "Verzamelt de logs van je containers en stuurt ze naar Loki. De opvolger van Promtail.", + "tags": [ + "logs", + "monitoring", + "grafana" + ], + "icon": { + "provider": "selfhst", + "id": "grafana" + }, + "version": { + "name": "latest" + }, + "categories": [ + "monitoring" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "alloy", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 12345, + "required": true + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "loki_url", + "type": "str", + "title": "Adres van Loki", + "default": "http://loki:3100", + "required": true, + "connect": { + "app": "loki", + "scheme": "http", + "port": 3100 + }, + "help": "Waar Alloy de logs naartoe stuurt. Draaien ze op hetzelfde gedeelde netwerk, dan is de containernaam genoeg." + } + ] + } + ] +} diff --git a/apps/cadvisor/files/compose.yaml b/apps/cadvisor/files/compose.yaml new file mode 100644 index 0000000..0b500da --- /dev/null +++ b/apps/cadvisor/files/compose.yaml @@ -0,0 +1,18 @@ +services: + << service_name >>: + image: gcr.io/cadvisor/cadvisor:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - /:/rootfs:ro + - /var/run:/var/run:ro + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + - /dev/disk/:/dev/disk:ro + ports: + - "<< port >>:8080" + devices: + - /dev/kmsg + privileged: true + restart: unless-stopped diff --git a/apps/cadvisor/template.json b/apps/cadvisor/template.json new file mode 100644 index 0000000..35d3f80 --- /dev/null +++ b/apps/cadvisor/template.json @@ -0,0 +1,53 @@ +{ + "kind": "compose", + "metadata": { + "name": "cAdvisor", + "description": "Meet per container hoeveel processor, geheugen en netwerk er verbruikt wordt. Vult node-exporter aan, dat alleen de host als geheel ziet. Let op: Draait met verhoogde rechten omdat het bij de cgroups van elke container moet kunnen.", + "tags": [ + "monitoring", + "metrics", + "containers", + "prometheus" + ], + "icon": { + "provider": "selfhst", + "id": "cadvisor" + }, + "version": { + "name": "latest" + }, + "categories": [ + "monitoring", + "beheer" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "cadvisor", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8202, + "required": true, + "description": "Standaard 8080; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/comfyui/files/compose.yaml b/apps/comfyui/files/compose.yaml new file mode 100644 index 0000000..fe17144 --- /dev/null +++ b/apps/comfyui/files/compose.yaml @@ -0,0 +1,12 @@ +services: + << service_name >>: + image: ghcr.io/ai-dock/comfyui:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/opt/ComfyUI/models + - << appdata_dir >>/<< service_name >>-output:/opt/ComfyUI/output + ports: + - "<< port >>:8188" + restart: unless-stopped diff --git a/apps/comfyui/template.json b/apps/comfyui/template.json new file mode 100644 index 0000000..7e13510 --- /dev/null +++ b/apps/comfyui/template.json @@ -0,0 +1,59 @@ +{ + "kind": "compose", + "metadata": { + "name": "ComfyUI", + "description": "Beeldgeneratie met modellen die op je eigen machine draaien, opgebouwd als een schema dat je zelf aan elkaar klikt. Let op: Zonder videokaart die doorgegeven wordt aan de container draait dit op de processor, en dan duurt één afbeelding minuten in plaats van seconden.", + "tags": [ + "ai", + "beeld", + "generatie" + ], + "icon": { + "provider": "selfhst", + "id": "comfyui" + }, + "version": { + "name": "latest" + }, + "categories": [ + "ai" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "comfyui", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8210, + "required": true, + "description": "Standaard 8188; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/crafty/files/compose.yaml b/apps/crafty/files/compose.yaml new file mode 100644 index 0000000..e1fbb67 --- /dev/null +++ b/apps/crafty/files/compose.yaml @@ -0,0 +1,14 @@ +services: + << service_name >>: + image: registry.gitlab.com/crafty-controller/crafty-4:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/crafty/app/config + - << appdata_dir >>/<< service_name >>-servers:/crafty/servers + - << appdata_dir >>/<< service_name >>-backups:/crafty/backups + ports: + - "<< port >>:8443" + - "<< port_spel >>:25565" + restart: unless-stopped diff --git a/apps/crafty/template.json b/apps/crafty/template.json new file mode 100644 index 0000000..5f752d9 --- /dev/null +++ b/apps/crafty/template.json @@ -0,0 +1,68 @@ +{ + "kind": "compose", + "metadata": { + "name": "Crafty Controller", + "description": "Beheerpaneel voor gameservers: meerdere Minecraft-werelden naast elkaar, met back-ups, planning en rechten per gebruiker.", + "tags": [ + "spellen", + "minecraft", + "gameserver" + ], + "icon": { + "provider": "selfhst", + "id": "crafty" + }, + "version": { + "name": "latest" + }, + "categories": [ + "spellen", + "beheer" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "crafty", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8206, + "required": true, + "description": "Standaard 8443; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "port_spel", + "type": "int", + "title": "Speelpoort", + "default": 25566, + "required": true, + "description": "Voor de eerste server; volgende krijgen 25566 en verder" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/ersatztv/files/compose.yaml b/apps/ersatztv/files/compose.yaml new file mode 100644 index 0000000..e608b5f --- /dev/null +++ b/apps/ersatztv/files/compose.yaml @@ -0,0 +1,12 @@ +services: + << service_name >>: + image: jasongdove/ersatztv:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/config + - << data_root >>/media:/data/media:ro + ports: + - "<< port >>:8409" + restart: unless-stopped diff --git a/apps/ersatztv/template.json b/apps/ersatztv/template.json new file mode 100644 index 0000000..92ab01b --- /dev/null +++ b/apps/ersatztv/template.json @@ -0,0 +1,68 @@ +{ + "kind": "compose", + "metadata": { + "name": "ErsatzTV", + "description": "Maakt echte tv-kanalen van je eigen bibliotheek, met een programmagids. Je zapt langs je eigen films en series in plaats van te kiezen.", + "tags": [ + "media", + "iptv", + "streaming" + ], + "icon": { + "provider": "selfhst", + "id": "ersatztv" + }, + "version": { + "name": "latest" + }, + "categories": [ + "media" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "ersatztv", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8208, + "required": true, + "description": "Standaard 8409; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "data_root", + "type": "str", + "title": "Gedeelde datamap", + "default": "/mnt/data", + "required": true, + "description": "Je mediabibliotheek, alleen-lezen gekoppeld", + "help": "Media en downloads horen onder één map te staan, die als /data in elke container komt. Alleen dan kan een voltooide download met een hardlink naar je bibliotheek. Eronder: media/films, media/series, torrents/ en usenet/." + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/evcc/files/compose.yaml b/apps/evcc/files/compose.yaml new file mode 100644 index 0000000..8b92536 --- /dev/null +++ b/apps/evcc/files/compose.yaml @@ -0,0 +1,12 @@ +services: + << service_name >>: + image: evcc/evcc:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/root/.evcc + ports: + - "<< port >>:7070" + network_mode: host + restart: unless-stopped diff --git a/apps/evcc/template.json b/apps/evcc/template.json new file mode 100644 index 0000000..204d94a --- /dev/null +++ b/apps/evcc/template.json @@ -0,0 +1,60 @@ +{ + "kind": "compose", + "metadata": { + "name": "evcc", + "description": "Stemt je zonnepanelen, thuisbatterij en laadpaal op elkaar af. Laadt de auto als de zon schijnt of de stroomprijs laag is. Let op: Draait op het netwerk van de host, want het moet je omvormer en laadpaal in je LAN kunnen vinden. Bereikbaar op poort 7070. De instellingen zet je in evcc.yaml in de appdata-map.", + "tags": [ + "energie", + "zonnepanelen", + "laadpaal", + "auto" + ], + "icon": { + "provider": "selfhst", + "id": "evcc" + }, + "version": { + "name": "latest" + }, + "categories": [ + "domotica", + "gezin" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "evcc", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 7070, + "required": true + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/fittrackee/files/compose.yaml b/apps/fittrackee/files/compose.yaml new file mode 100644 index 0000000..e8f0816 --- /dev/null +++ b/apps/fittrackee/files/compose.yaml @@ -0,0 +1,36 @@ +services: + << service_name >>: + image: fittrackee/fittrackee:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - APP_SECRET_KEY=<< secret_key >> + - DATABASE_URL=postgresql://fittrackee:<< db_password >>@<< service_name >>-db:5432/fittrackee + - UI_URL=<< app_url >> + - APP_SETTINGS=fittrackee.config.ProductionConfig + - REDIS_URL=redis://<< service_name >>-redis:6379 + volumes: + - << appdata_dir >>/<< service_name >>:/usr/src/app/uploads + ports: + - "<< port >>:5000" + depends_on: + - << service_name >>-db + - << service_name >>-redis + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=fittrackee + - POSTGRES_USER=fittrackee + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped + << service_name >>-redis: + image: valkey/valkey:8-alpine + container_name: << service_name >>-redis + command: valkey-server --save 60 1 + volumes: + - << appdata_dir >>/<< service_name >>-redis:/data + restart: unless-stopped diff --git a/apps/fittrackee/template.json b/apps/fittrackee/template.json new file mode 100644 index 0000000..18e2831 --- /dev/null +++ b/apps/fittrackee/template.json @@ -0,0 +1,95 @@ +{ + "kind": "compose", + "metadata": { + "name": "FitTrackee", + "description": "Houdt je hardloop-, fiets- en wandeltochten bij uit gpx-bestanden, met kaarten en statistieken per periode.", + "tags": [ + "sport", + "gezondheid", + "gps" + ], + "icon": { + "provider": "selfhst", + "id": "fittrackee" + }, + "version": { + "name": "latest" + }, + "categories": [ + "gezin" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "fittrackee", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8209, + "required": true, + "description": "Standaard 5000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8209", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "secret_key", + "type": "str", + "title": "Geheime sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/graylog/files/compose.yaml b/apps/graylog/files/compose.yaml new file mode 100644 index 0000000..fb04ecf --- /dev/null +++ b/apps/graylog/files/compose.yaml @@ -0,0 +1,41 @@ +services: + << service_name >>: + image: graylog/graylog:6.1 + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - GRAYLOG_PASSWORD_SECRET=<< password_secret >> + - GRAYLOG_ROOT_PASSWORD_SHA2=<< root_password_sha2 >> + - GRAYLOG_HTTP_EXTERNAL_URI=<< app_url >>/ + - GRAYLOG_MONGODB_URI=mongodb://<< service_name >>-mongo:27017/graylog + - GRAYLOG_ELASTICSEARCH_HOSTS=http://<< service_name >>-opensearch:9200 + volumes: + - << appdata_dir >>/<< service_name >>:/usr/share/graylog/data/data + - << appdata_dir >>/<< service_name >>-journal:/usr/share/graylog/data/journal + ports: + - "<< port >>:9000" + - "<< port_syslog >>:1514" + - "<< port_syslog >>:1514/udp" + - "<< port_gelf >>:12201" + - "<< port_gelf >>:12201/udp" + depends_on: + - << service_name >>-mongo + - << service_name >>-opensearch + restart: unless-stopped + << service_name >>-mongo: + image: mongo:6 + container_name: << service_name >>-mongo + volumes: + - << appdata_dir >>/<< service_name >>-mongo:/data/db + restart: unless-stopped + << service_name >>-opensearch: + image: opensearchproject/opensearch:2 + container_name: << service_name >>-opensearch + environment: + - discovery.type=single-node + - plugins.security.disabled=true + - OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g + - bootstrap.memory_lock=true + volumes: + - << appdata_dir >>/<< service_name >>-opensearch:/usr/share/opensearch/data + restart: unless-stopped diff --git a/apps/graylog/template.json b/apps/graylog/template.json new file mode 100644 index 0000000..68a7582 --- /dev/null +++ b/apps/graylog/template.json @@ -0,0 +1,113 @@ +{ + "kind": "compose", + "metadata": { + "name": "Graylog", + "description": "Verzamelt logs van je hele netwerk, doorzoekt ze en slaat alarm bij patronen die je zelf instelt. Zwaarder dan Loki, maar met meer in huis. Let op: Reken op minstens 4 GB geheugen: OpenSearch alleen al pakt er 1.", + "tags": [ + "logs", + "monitoring", + "beveiliging" + ], + "icon": { + "provider": "selfhst", + "id": "graylog" + }, + "version": { + "name": "latest" + }, + "categories": [ + "monitoring", + "beveiliging" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "graylog", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8214, + "required": true, + "description": "Standaard 9000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "port_syslog", + "type": "int", + "title": "Syslog-poort", + "default": 1514, + "required": true, + "description": "Hier sturen je apparaten hun logs naartoe" + }, + { + "name": "port_gelf", + "type": "int", + "title": "GELF-poort", + "default": 12201, + "required": true, + "description": "Voor logs uit Docker" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8214", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "password_secret", + "type": "str", + "title": "Geheime sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Minimaal 16 tekens; versleutelt de opgeslagen wachtwoorden" + }, + { + "name": "root_password_sha2", + "type": "str", + "title": "SHA-256 van je beheerderswachtwoord", + "default": "", + "required": true, + "secret": true, + "eigen_invoer": true, + "description": "Maak hem met: echo -n 'jouwwachtwoord' | sha256sum" + } + ] + } + ] +} diff --git a/apps/healthchecks/files/compose.yaml b/apps/healthchecks/files/compose.yaml new file mode 100644 index 0000000..b953c67 --- /dev/null +++ b/apps/healthchecks/files/compose.yaml @@ -0,0 +1,19 @@ +services: + << service_name >>: + image: healthchecks/healthchecks:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - SECRET_KEY=<< secret_key >> + - SITE_ROOT=<< app_url >> + - DB=sqlite + - DB_NAME=/data/hc.sqlite + - SUPERUSER_EMAIL=<< admin_email >> + - SUPERUSER_PASSWORD=<< admin_password >> + - ALLOWED_HOSTS=* + - REGISTRATION_OPEN=False + volumes: + - << appdata_dir >>/<< service_name >>:/data + ports: + - "<< port >>:8000" + restart: unless-stopped diff --git a/apps/healthchecks/template.json b/apps/healthchecks/template.json new file mode 100644 index 0000000..66d9e37 --- /dev/null +++ b/apps/healthchecks/template.json @@ -0,0 +1,101 @@ +{ + "kind": "compose", + "metadata": { + "name": "Healthchecks", + "description": "Merkt het als een cronjob níét draait. Elke taak pingt na afloop; blijft die ping uit, dan krijg je bericht.", + "tags": [ + "cron", + "monitoring", + "meldingen" + ], + "icon": { + "provider": "selfhst", + "id": "healthchecks" + }, + "version": { + "name": "latest" + }, + "categories": [ + "monitoring" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "healthchecks", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8203, + "required": true, + "description": "Standaard 8000; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8203", + "required": true + }, + { + "name": "admin_email", + "type": "str", + "title": "E-mailadres beheerder", + "default": "admin@example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "secret_key", + "type": "str", + "title": "Django-sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + }, + { + "name": "admin_password", + "type": "str", + "title": "Beheerderswachtwoord", + "default": "", + "required": true, + "secret": true, + "eigen_invoer": true + } + ] + } + ] +} diff --git a/apps/infisical/files/compose.yaml b/apps/infisical/files/compose.yaml new file mode 100644 index 0000000..7dfc77b --- /dev/null +++ b/apps/infisical/files/compose.yaml @@ -0,0 +1,35 @@ +services: + << service_name >>: + image: infisical/infisical:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - NODE_ENV=production + - ENCRYPTION_KEY=<< encryption_key >> + - AUTH_SECRET=<< auth_secret >> + - DB_CONNECTION_URI=postgres://infisical:<< db_password >>@<< service_name >>-db:5432/infisical + - REDIS_URL=redis://<< service_name >>-redis:6379 + - SITE_URL=<< app_url >> + ports: + - "<< port >>:8080" + depends_on: + - << service_name >>-db + - << service_name >>-redis + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=infisical + - POSTGRES_USER=infisical + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped + << service_name >>-redis: + image: valkey/valkey:8-alpine + container_name: << service_name >>-redis + command: valkey-server --save 60 1 + volumes: + - << appdata_dir >>/<< service_name >>-redis:/data + restart: unless-stopped diff --git a/apps/infisical/template.json b/apps/infisical/template.json new file mode 100644 index 0000000..3b2989a --- /dev/null +++ b/apps/infisical/template.json @@ -0,0 +1,107 @@ +{ + "kind": "compose", + "metadata": { + "name": "Infisical", + "description": "Bewaart de geheimen van je eigen applicaties en deelt ze uit per omgeving. Vaultwarden is voor mensen, dit is voor servers.", + "tags": [ + "geheimen", + "ontwikkeling", + "beveiliging" + ], + "icon": { + "provider": "selfhst", + "id": "infisical" + }, + "version": { + "name": "latest" + }, + "categories": [ + "beveiliging", + "ontwikkeling" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "infisical", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8204, + "required": true, + "description": "Standaard 8080; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "http://localhost:8204", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "encryption_key", + "type": "str", + "title": "Versleutelingssleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "secret_format": "hex", + "secret_bytes": 16 + }, + { + "name": "auth_secret", + "type": "str", + "title": "Sleutel voor de inlogsessies", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/loki/files/compose.yaml b/apps/loki/files/compose.yaml new file mode 100644 index 0000000..bdb830e --- /dev/null +++ b/apps/loki/files/compose.yaml @@ -0,0 +1,12 @@ +services: + << service_name >>: + image: grafana/loki:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/loki + command: -config.file=/etc/loki/local-config.yaml + ports: + - "<< port >>:3100" + restart: unless-stopped diff --git a/apps/loki/template.json b/apps/loki/template.json new file mode 100644 index 0000000..854a782 --- /dev/null +++ b/apps/loki/template.json @@ -0,0 +1,58 @@ +{ + "kind": "compose", + "metadata": { + "name": "Loki", + "description": "Bewaart logregels zodat je ze later nog kunt doorzoeken. Dozzle laat zien wat er nú gebeurt; Loki weet nog wat er vannacht misging.", + "tags": [ + "logs", + "monitoring", + "grafana" + ], + "icon": { + "provider": "selfhst", + "id": "loki" + }, + "version": { + "name": "latest" + }, + "categories": [ + "monitoring" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "loki", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "API-poort", + "default": 3100, + "required": true + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/manyfold/files/compose.yaml b/apps/manyfold/files/compose.yaml new file mode 100644 index 0000000..178a6c4 --- /dev/null +++ b/apps/manyfold/files/compose.yaml @@ -0,0 +1,36 @@ +services: + << service_name >>: + image: ghcr.io/manyfold3d/manyfold:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - SECRET_KEY_BASE=<< secret_key_base >> + - DATABASE_URL=postgresql://manyfold:<< db_password >>@<< service_name >>-db/manyfold + - REDIS_URL=redis://<< service_name >>-redis:6379/1 + - PUID=<< puid >> + - PGID=<< pgid >> + volumes: + - << appdata_dir >>/<< service_name >>:/libraries + ports: + - "<< port >>:3214" + depends_on: + - << service_name >>-db + - << service_name >>-redis + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=manyfold + - POSTGRES_USER=manyfold + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped + << service_name >>-redis: + image: valkey/valkey:8-alpine + container_name: << service_name >>-redis + command: valkey-server --save 60 1 + volumes: + - << appdata_dir >>/<< service_name >>-redis:/data + restart: unless-stopped diff --git a/apps/manyfold/template.json b/apps/manyfold/template.json new file mode 100644 index 0000000..05fc23e --- /dev/null +++ b/apps/manyfold/template.json @@ -0,0 +1,104 @@ +{ + "kind": "compose", + "metadata": { + "name": "Manyfold", + "description": "Bibliotheek voor je 3D-modellen: doorzoekbaar, met voorbeeldweergave, tags en licentie per model.", + "tags": [ + "3d-printen", + "modellen", + "bibliotheek" + ], + "icon": { + "provider": "selfhst", + "id": "manyfold" + }, + "version": { + "name": "latest" + }, + "categories": [ + "3d-printen", + "documenten" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "manyfold", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8207, + "required": true, + "description": "Standaard 3214; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Gebruiker", + "items": [ + { + "name": "puid", + "type": "int", + "title": "Gebruikers-ID (PUID)", + "default": 1000, + "required": true, + "description": "`id -u` op je server" + }, + { + "name": "pgid", + "type": "int", + "title": "Groeps-ID (PGID)", + "default": 1000, + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "secret_key_base", + "type": "str", + "title": "Rails-sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/music-assistant/files/compose.yaml b/apps/music-assistant/files/compose.yaml new file mode 100644 index 0000000..67976b0 --- /dev/null +++ b/apps/music-assistant/files/compose.yaml @@ -0,0 +1,10 @@ +services: + << service_name >>: + image: ghcr.io/music-assistant/server:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/data + network_mode: host + restart: unless-stopped diff --git a/apps/music-assistant/template.json b/apps/music-assistant/template.json new file mode 100644 index 0000000..24a1a88 --- /dev/null +++ b/apps/music-assistant/template.json @@ -0,0 +1,52 @@ +{ + "kind": "compose", + "metadata": { + "name": "Music Assistant", + "description": "Brengt Spotify, je eigen bestanden en je speakers samen onder één afspeellijst. Werkt samen met Home Assistant. Let op: Draait op het netwerk van de host, want anders vindt het je Chromecasts en Sonos-speakers niet. Bereikbaar op poort 8095.", + "tags": [ + "muziek", + "domotica", + "streaming" + ], + "icon": { + "provider": "selfhst", + "id": "music-assistant" + }, + "version": { + "name": "latest" + }, + "categories": [ + "domotica", + "media" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "musicassistant", + "required": true + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/netbox/files/compose.yaml b/apps/netbox/files/compose.yaml new file mode 100644 index 0000000..6221393 --- /dev/null +++ b/apps/netbox/files/compose.yaml @@ -0,0 +1,44 @@ +services: + << service_name >>: + image: netboxcommunity/netbox:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - DB_HOST=<< service_name >>-db + - DB_NAME=netbox + - DB_USER=netbox + - DB_PASSWORD=<< db_password >> + - REDIS_HOST=<< service_name >>-redis + - REDIS_PORT=6379 + - REDIS_CACHE_HOST=<< service_name >>-redis + - REDIS_CACHE_DATABASE=1 + - SECRET_KEY=<< secret_key >> + - SUPERUSER_NAME=<< admin_user >> + - SUPERUSER_EMAIL=<< admin_email >> + - SUPERUSER_PASSWORD=<< admin_password >> + - ALLOWED_HOSTS=* + volumes: + - << appdata_dir >>/<< service_name >>:/opt/netbox/netbox/media + ports: + - "<< port >>:8080" + depends_on: + - << service_name >>-db + - << service_name >>-redis + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=netbox + - POSTGRES_USER=netbox + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped + << service_name >>-redis: + image: valkey/valkey:8-alpine + container_name: << service_name >>-redis + command: valkey-server --save 60 1 + volumes: + - << appdata_dir >>/<< service_name >>-redis:/data + restart: unless-stopped diff --git a/apps/netbox/template.json b/apps/netbox/template.json new file mode 100644 index 0000000..3914a53 --- /dev/null +++ b/apps/netbox/template.json @@ -0,0 +1,115 @@ +{ + "kind": "compose", + "metadata": { + "name": "NetBox", + "description": "Documenteert je netwerk zoals het hoort te zijn: racks, kabels, IP-adressen en VLAN's, als bron van waarheid voor je automatisering.", + "tags": [ + "netwerk", + "documentatie", + "ipam" + ], + "icon": { + "provider": "selfhst", + "id": "netbox" + }, + "version": { + "name": "latest" + }, + "categories": [ + "netwerk", + "zakelijk" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "netbox", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8216, + "required": true, + "description": "Standaard 8080; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "admin_user", + "type": "str", + "title": "Beheerdersnaam", + "default": "admin", + "required": true + }, + { + "name": "admin_email", + "type": "str", + "title": "E-mailadres beheerder", + "default": "admin@example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "secret_key", + "type": "str", + "title": "Django-sleutel", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "secret_format": "hex", + "secret_bytes": 32, + "description": "Minimaal 50 tekens" + }, + { + "name": "admin_password", + "type": "str", + "title": "Beheerderswachtwoord", + "default": "", + "required": true, + "secret": true, + "eigen_invoer": true + } + ] + } + ] +} diff --git a/apps/node-exporter/files/compose.yaml b/apps/node-exporter/files/compose.yaml new file mode 100644 index 0000000..22365e2 --- /dev/null +++ b/apps/node-exporter/files/compose.yaml @@ -0,0 +1,13 @@ +services: + << service_name >>: + image: quay.io/prometheus/node-exporter:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - /:/host:ro,rslave + command: --path.rootfs=/host --collector.filesystem.mount-points-exclude="^/(sys|proc|dev|host|etc)($$|/)" + ports: + - "<< port >>:9100" + pid: host + restart: unless-stopped diff --git a/apps/node-exporter/template.json b/apps/node-exporter/template.json new file mode 100644 index 0000000..4a4e54d --- /dev/null +++ b/apps/node-exporter/template.json @@ -0,0 +1,50 @@ +{ + "kind": "compose", + "metadata": { + "name": "node-exporter", + "description": "Levert de cijfers van de host aan Prometheus: processor, geheugen, schijven, netwerk en temperatuur. Let op: Meet de host, dus koppelt het hele bestandssysteem alleen-lezen mee. Zonder Prometheus ernaast doet dit niets.", + "tags": [ + "monitoring", + "metrics", + "prometheus" + ], + "icon": { + "provider": "selfhst", + "id": "prometheus" + }, + "version": { + "name": "latest" + }, + "categories": [ + "monitoring" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "nodeexporter", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Metrics-poort", + "default": 9100, + "required": true + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/omada/files/compose.yaml b/apps/omada/files/compose.yaml new file mode 100644 index 0000000..b6ba8e7 --- /dev/null +++ b/apps/omada/files/compose.yaml @@ -0,0 +1,19 @@ +services: + << service_name >>: + image: mbentley/omada-controller:latest + container_name: << service_name >> + environment: + - MANAGE_HTTP_PORT=8088 + - MANAGE_HTTPS_PORT=8043 + - PORT_ADOPT_V1=29812 + - PORT_APP_DISCOVERY=27001 + - SHOW_SERVER_LOGS=true + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/opt/tplink/EAPController/data + - << appdata_dir >>/<< service_name >>-logs:/opt/tplink/EAPController/logs + ports: + - "<< port >>:8043" + - "<< port_http >>:8088" + - "<< port_beheer >>:29814" + restart: unless-stopped diff --git a/apps/omada/template.json b/apps/omada/template.json new file mode 100644 index 0000000..90cf8a1 --- /dev/null +++ b/apps/omada/template.json @@ -0,0 +1,74 @@ +{ + "kind": "compose", + "metadata": { + "name": "Omada Controller", + "description": "Beheert TP-Link Omada-accesspoints, switches en routers vanaf één plek, met een netwerkkaart en gastportaal. Let op: Accesspoints vinden de controller via uitzendingen op je LAN. Zit hij in een Docker-bridge, dan moet je ze handmatig aanmelden of de controller een eigen IP-adres geven.", + "tags": [ + "netwerk", + "wifi", + "accesspoints" + ], + "icon": { + "provider": "selfhst", + "id": "omada" + }, + "version": { + "name": "latest" + }, + "categories": [ + "netwerk" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "omada", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8211, + "required": true, + "description": "Standaard 8043; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "port_http", + "type": "int", + "title": "HTTP-poort", + "default": 8212, + "required": true + }, + { + "name": "port_beheer", + "type": "int", + "title": "Beheerpoort", + "default": 29814, + "required": true, + "description": "Waarmee de apparaten zichzelf aanmelden" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/opencloud/files/compose.yaml b/apps/opencloud/files/compose.yaml new file mode 100644 index 0000000..603a56f --- /dev/null +++ b/apps/opencloud/files/compose.yaml @@ -0,0 +1,17 @@ +services: + << service_name >>: + image: opencloudeu/opencloud:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - OC_URL=<< app_url >> + - OC_INSECURE=true + - PROXY_HTTP_ADDR=0.0.0.0:9200 + - IDM_ADMIN_PASSWORD=<< admin_password >> + volumes: + - << appdata_dir >>/<< service_name >>:/etc/opencloud + - << appdata_dir >>/<< service_name >>-data:/var/lib/opencloud + command: sh -c "opencloud init || true && opencloud server" + ports: + - "<< port >>:9200" + restart: unless-stopped diff --git a/apps/opencloud/template.json b/apps/opencloud/template.json new file mode 100644 index 0000000..ad6c7a9 --- /dev/null +++ b/apps/opencloud/template.json @@ -0,0 +1,86 @@ +{ + "kind": "compose", + "metadata": { + "name": "OpenCloud", + "description": "Bestandsopslag met delen en samenwerken, gebouwd op de ownCloud-basis maar volledig opnieuw opgezet in Go.", + "tags": [ + "bestanden", + "sync", + "opslag", + "samenwerken" + ], + "icon": { + "provider": "selfhst", + "id": "opencloud" + }, + "version": { + "name": "latest" + }, + "categories": [ + "opslag", + "productiviteit" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "opencloud", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 9200, + "required": true + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "app_url", + "type": "str", + "title": "Publiek adres", + "default": "https://cloud.example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "admin_password", + "type": "str", + "title": "Beheerderswachtwoord", + "default": "", + "required": true, + "secret": true, + "eigen_invoer": true + } + ] + } + ] +} diff --git a/apps/pgadmin/files/compose.yaml b/apps/pgadmin/files/compose.yaml new file mode 100644 index 0000000..4b07739 --- /dev/null +++ b/apps/pgadmin/files/compose.yaml @@ -0,0 +1,14 @@ +services: + << service_name >>: + image: dpage/pgadmin4:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - PGADMIN_DEFAULT_EMAIL=<< admin_email >> + - PGADMIN_DEFAULT_PASSWORD=<< admin_password >> + - PGADMIN_CONFIG_SERVER_MODE=True + volumes: + - << appdata_dir >>/<< service_name >>:/var/lib/pgadmin + ports: + - "<< port >>:80" + restart: unless-stopped diff --git a/apps/pgadmin/template.json b/apps/pgadmin/template.json new file mode 100644 index 0000000..e6c447d --- /dev/null +++ b/apps/pgadmin/template.json @@ -0,0 +1,85 @@ +{ + "kind": "compose", + "metadata": { + "name": "pgAdmin", + "description": "Beheerprogramma voor PostgreSQL: query's, schema's, gebruikers en back-ups. Uitgebreider dan Adminer, maar alleen voor PostgreSQL.", + "tags": [ + "database", + "postgresql", + "beheer" + ], + "icon": { + "provider": "selfhst", + "id": "pgadmin" + }, + "version": { + "name": "latest" + }, + "categories": [ + "ontwikkeling" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "pgadmin", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8205, + "required": true, + "description": "Standaard 80; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "admin_email", + "type": "str", + "title": "E-mailadres beheerder", + "default": "admin@example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "admin_password", + "type": "str", + "title": "Beheerderswachtwoord", + "default": "", + "required": true, + "secret": true, + "eigen_invoer": true + } + ] + } + ] +} diff --git a/apps/prometheus/files/compose.yaml b/apps/prometheus/files/compose.yaml index 81ac4c7..86c634b 100644 --- a/apps/prometheus/files/compose.yaml +++ b/apps/prometheus/files/compose.yaml @@ -10,7 +10,10 @@ services: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.retention.time=<< retention >> volumes: - - << appdata_dir >>/<< service_name >>/config:/etc/prometheus + # Het configuratiebestand komt uit dit sjabloon; zonder dat weigert + # Prometheus te starten. Eigen alertregels zet je in de rules-map. + - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro + - << appdata_dir >>/<< service_name >>/rules:/etc/prometheus/rules - << service_name >>_data:/prometheus restart: unless-stopped diff --git a/apps/prometheus/files/prometheus.yml b/apps/prometheus/files/prometheus.yml new file mode 100644 index 0000000..8e48de4 --- /dev/null +++ b/apps/prometheus/files/prometheus.yml @@ -0,0 +1,28 @@ +# Startconfiguratie. Prometheus weigert te starten zonder dit bestand — het +# sjabloon koppelde eerder een lege map en verwees naar een prometheus.yml die +# er nooit kwam. +global: + scrape_interval: 30s + evaluation_interval: 30s + +rule_files: + - /etc/prometheus/rules/*.yml + +scrape_configs: + - job_name: prometheus + static_configs: + - targets: ["localhost:9090"] + +<%- if node_exporter %> + # De host zelf: processor, geheugen, schijven, netwerk. + - job_name: node + static_configs: + - targets: ["<< node_exporter >>"] +<%- endif %> + +<%- if cadvisor %> + # Per container: verbruik en herstarts. + - job_name: containers + static_configs: + - targets: ["<< cadvisor >>"] +<%- endif %> diff --git a/apps/prometheus/template.json b/apps/prometheus/template.json index dba9fb8..5102a9b 100644 --- a/apps/prometheus/template.json +++ b/apps/prometheus/template.json @@ -59,6 +59,32 @@ "required": true, "description": "Hoe lang meetwaarden bewaard blijven, bv. 30d of 1y", "advanced": true + }, + { + "name": "node_exporter", + "type": "str", + "title": "node-exporter", + "default": "node-exporter:9100", + "required": false, + "connect": { + "app": "node-exporter", + "scheme": "http", + "port": 9100 + }, + "help": "Waar Prometheus de cijfers van je host ophaalt: processor, geheugen, schijven en netwerk. Laat leeg als je die niet draait; Prometheus meet dan alleen zichzelf." + }, + { + "name": "cadvisor", + "type": "str", + "title": "cAdvisor", + "default": "cadvisor:8080", + "required": false, + "connect": { + "app": "cadvisor", + "scheme": "http", + "port": 8080 + }, + "help": "Waar Prometheus de cijfers per container ophaalt. Laat leeg als je cAdvisor niet draait." } ] } diff --git a/apps/seafile/files/compose.yaml b/apps/seafile/files/compose.yaml new file mode 100644 index 0000000..bd43e90 --- /dev/null +++ b/apps/seafile/files/compose.yaml @@ -0,0 +1,30 @@ +services: + << service_name >>: + image: seafileltd/seafile-mc:latest + container_name: << service_name >> + environment: + - DB_HOST=<< service_name >>-db + - DB_ROOT_PASSWD=<< db_password >> + - SEAFILE_ADMIN_EMAIL=<< admin_email >> + - SEAFILE_ADMIN_PASSWORD=<< admin_password >> + - SEAFILE_SERVER_HOSTNAME=<< hostname >> + - SEAFILE_SERVER_LETSENCRYPT=false + - TIME_ZONE=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/shared + ports: + - "<< port >>:80" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-db: + image: mariadb:11 + container_name: << service_name >>-db + environment: + - MARIADB_DATABASE=seafile + - MARIADB_USER=seafile + - MARIADB_PASSWORD=<< db_password >> + - MARIADB_ROOT_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/mysql + restart: unless-stopped diff --git a/apps/seafile/template.json b/apps/seafile/template.json new file mode 100644 index 0000000..63411ad --- /dev/null +++ b/apps/seafile/template.json @@ -0,0 +1,102 @@ +{ + "kind": "compose", + "metadata": { + "name": "Seafile", + "description": "Bestandssynchronisatie die met grote mappen en veel kleine bestanden beter overweg kan dan Nextcloud, met versiebeheer per bibliotheek.", + "tags": [ + "bestanden", + "sync", + "opslag" + ], + "icon": { + "provider": "selfhst", + "id": "seafile" + }, + "version": { + "name": "latest" + }, + "categories": [ + "opslag" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "seafile", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Webinterface", + "default": 8217, + "required": true, + "description": "Standaard 80; hier verplaatst omdat die poort al bezet is" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Instellingen", + "items": [ + { + "name": "hostname", + "type": "str", + "title": "Publiek domein", + "default": "seafile.example.com", + "required": true + }, + { + "name": "admin_email", + "type": "str", + "title": "E-mailadres beheerder", + "default": "admin@example.com", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + }, + { + "name": "admin_password", + "type": "str", + "title": "Beheerderswachtwoord", + "default": "", + "required": true, + "secret": true, + "eigen_invoer": true + } + ] + } + ] +} diff --git a/apps/unbound/files/compose.yaml b/apps/unbound/files/compose.yaml new file mode 100644 index 0000000..8d85771 --- /dev/null +++ b/apps/unbound/files/compose.yaml @@ -0,0 +1,12 @@ +services: + << service_name >>: + image: mvance/unbound:latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + volumes: + - << appdata_dir >>/<< service_name >>:/opt/unbound/etc/unbound + ports: + - "<< port >>:53" + - "<< port >>:53/udp" + restart: unless-stopped diff --git a/apps/unbound/template.json b/apps/unbound/template.json new file mode 100644 index 0000000..88a517d --- /dev/null +++ b/apps/unbound/template.json @@ -0,0 +1,60 @@ +{ + "kind": "compose", + "metadata": { + "name": "Unbound", + "description": "Zoekt DNS-namen zelf op bij de bron, in plaats van door te vragen aan Google of Cloudflare. Zet hem achter Pi-hole of AdGuard. Let op: Dit vervangt je blokkeerlijst niet — het is de stap erna. Pi-hole bepaalt wát er beantwoord wordt, Unbound waar het antwoord vandaan komt.", + "tags": [ + "dns", + "privacy", + "netwerk" + ], + "icon": { + "provider": "selfhst", + "id": "unbound" + }, + "version": { + "name": "latest" + }, + "categories": [ + "netwerk", + "beveiliging" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "unbound", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "DNS-poort", + "default": 5335, + "required": true, + "description": "Zet dit adres in Pi-hole of AdGuard als upstream" + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + } + ] +} diff --git a/apps/wazuh/files/compose.yaml b/apps/wazuh/files/compose.yaml new file mode 100644 index 0000000..5759872 --- /dev/null +++ b/apps/wazuh/files/compose.yaml @@ -0,0 +1,42 @@ +services: + << service_name >>: + image: wazuh/wazuh-manager:4.9.2 + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - INDEXER_URL=https://<< service_name >>-indexer:9200 + - INDEXER_USERNAME=admin + - INDEXER_PASSWORD=<< indexer_password >> + - API_USERNAME=wazuh-wui + - API_PASSWORD=<< api_password >> + volumes: + - << appdata_dir >>/<< service_name >>:/var/ossec/data + ports: + - "<< port_agent >>:1515" + - "<< port_api >>:55000" + depends_on: + - << service_name >>-indexer + restart: unless-stopped + << service_name >>-indexer: + image: wazuh/wazuh-indexer:4.9.2 + container_name: << service_name >>-indexer + environment: + - OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g + - bootstrap.memory_lock=true + volumes: + - << appdata_dir >>/<< service_name >>-indexer:/var/lib/wazuh-indexer + restart: unless-stopped + << service_name >>-dashboard: + image: wazuh/wazuh-dashboard:4.9.2 + container_name: << service_name >>-dashboard + environment: + - INDEXER_USERNAME=admin + - INDEXER_PASSWORD=<< indexer_password >> + - WAZUH_API_URL=https://<< service_name >> + - API_USERNAME=wazuh-wui + - API_PASSWORD=<< api_password >> + ports: + - "<< port_web >>:5601" + depends_on: + - << service_name >>-indexer + restart: unless-stopped diff --git a/apps/wazuh/template.json b/apps/wazuh/template.json new file mode 100644 index 0000000..a8c6146 --- /dev/null +++ b/apps/wazuh/template.json @@ -0,0 +1,97 @@ +{ + "kind": "compose", + "metadata": { + "name": "Wazuh", + "description": "Beveiligingsbewaking over je hele park: bestandsintegriteit, kwetsbaarheden, inbraakdetectie en naleving, met agents op elke machine. Let op: Wazuh verwacht normaal zelfgemaakte certificaten tussen zijn onderdelen. Deze opzet draait zonder, dus houd hem binnen je eigen netwerk. Reken op 4 GB geheugen of meer.", + "tags": [ + "beveiliging", + "monitoring", + "inbraakdetectie" + ], + "icon": { + "provider": "selfhst", + "id": "wazuh" + }, + "version": { + "name": "latest" + }, + "categories": [ + "beveiliging", + "monitoring" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "wazuh", + "required": true + }, + { + "name": "port_agent", + "type": "int", + "title": "Agentpoort", + "default": 1515, + "required": true, + "description": "Waarmee de agents zich aanmelden" + }, + { + "name": "port_api", + "type": "int", + "title": "API-poort", + "default": 55000, + "required": true + }, + { + "name": "port_web", + "type": "int", + "title": "Webinterface", + "default": 8215, + "required": true + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "indexer_password", + "type": "str", + "title": "Wachtwoord voor de indexer", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + }, + { + "name": "api_password", + "type": "str", + "title": "Wachtwoord voor de API", + "default": "", + "required": true, + "secret": true, + "generate": "auto" + } + ] + } + ] +} diff --git a/apps/zabbix/files/compose.yaml b/apps/zabbix/files/compose.yaml new file mode 100644 index 0000000..7425398 --- /dev/null +++ b/apps/zabbix/files/compose.yaml @@ -0,0 +1,40 @@ +services: + << service_name >>: + image: zabbix/zabbix-server-pgsql:alpine-7.0-latest + container_name: << service_name >> + environment: + - TZ=<< timezone >> + - DB_SERVER_HOST=<< service_name >>-db + - POSTGRES_USER=zabbix + - POSTGRES_PASSWORD=<< db_password >> + - POSTGRES_DB=zabbix + ports: + - "<< port >>:10051" + depends_on: + - << service_name >>-db + restart: unless-stopped + << service_name >>-web: + image: zabbix/zabbix-web-nginx-pgsql:alpine-7.0-latest + container_name: << service_name >>-web + environment: + - ZBX_SERVER_HOST=<< service_name >> + - DB_SERVER_HOST=<< service_name >>-db + - POSTGRES_USER=zabbix + - POSTGRES_PASSWORD=<< db_password >> + - POSTGRES_DB=zabbix + - PHP_TZ=<< timezone >> + ports: + - "<< port_web >>:8080" + depends_on: + - << service_name >> + restart: unless-stopped + << service_name >>-db: + image: postgres:16-alpine + container_name: << service_name >>-db + environment: + - POSTGRES_DB=zabbix + - POSTGRES_USER=zabbix + - POSTGRES_PASSWORD=<< db_password >> + volumes: + - << appdata_dir >>/<< service_name >>-db:/var/lib/postgresql/data + restart: unless-stopped diff --git a/apps/zabbix/template.json b/apps/zabbix/template.json new file mode 100644 index 0000000..dfadc38 --- /dev/null +++ b/apps/zabbix/template.json @@ -0,0 +1,82 @@ +{ + "kind": "compose", + "metadata": { + "name": "Zabbix", + "description": "Bewaakt servers, netwerkapparaten en diensten met agents én zonder, met drempelwaarden per meetwaarde en uitgebreide alarmering. Let op: Het standaardaccount is Admin met wachtwoord zabbix — wijzig dat meteen na de eerste keer inloggen.", + "tags": [ + "monitoring", + "netwerk", + "infrastructuur" + ], + "icon": { + "provider": "selfhst", + "id": "zabbix" + }, + "version": { + "name": "latest" + }, + "categories": [ + "monitoring", + "netwerk" + ] + }, + "variables": [ + { + "title": "Algemeen", + "items": [ + { + "name": "service_name", + "type": "str", + "title": "Servicenaam", + "default": "zabbix", + "required": true + }, + { + "name": "port", + "type": "int", + "title": "Serverpoort", + "default": 10051, + "required": true, + "description": "Waarmee de agents verbinding maken" + }, + { + "name": "port_web", + "type": "int", + "title": "Webinterface", + "default": 8213, + "required": true + }, + { + "name": "appdata_dir", + "type": "str", + "title": "Appdata-map", + "default": "/opt/serverup/appdata", + "required": true, + "description": "Basismap voor de gegevens van deze app" + }, + { + "name": "timezone", + "type": "str", + "title": "Tijdzone", + "default": "Europe/Amsterdam", + "required": true + } + ] + }, + { + "title": "Toegang", + "items": [ + { + "name": "db_password", + "type": "str", + "title": "Databasewachtwoord", + "default": "", + "required": true, + "secret": true, + "generate": "auto", + "description": "Wordt automatisch ingevuld en in .env bewaard" + } + ] + } + ] +} diff --git a/tools/controleer_images.py b/tools/controleer_images.py new file mode 100644 index 0000000..0ad1cb2 --- /dev/null +++ b/tools/controleer_images.py @@ -0,0 +1,77 @@ +"""Controleer of elk image in de catalogus echt bestaat bij zijn registry. + +Aanleiding: `ghcr.io/plexguide/huntarr` stond in de ARR-stack terwijl dat project +offline was gehaald en het image niet meer bestond. Daarna bleken ook Forgejo, +Planka en Baby Buddy naar tags te wijzen die er niet zijn. Zulke fouten merk je +anders pas als iemand op installeren drukt. + + python3 tools/controleer_images.py [--json] + +**Waarom dit langzaam gaat.** Registries knijpen af bij te veel verzoeken achter +elkaar, en een afgeknepen verzoek ziet er precies zo uit als een image dat niet +bestaat. Eén ronde met zestien tegelijk leverde negen valse meldingen op — waarop +je bijna een werkend sjabloon gaat "repareren". Daarom weinig tegelijk, en wat +niet meteen lukt gaat opnieuw met oplopende pauzes. +""" +from __future__ import annotations + +import concurrent.futures as cf +import json +import re +import sys +import time +from pathlib import Path + +WORTEL = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(WORTEL / "server-up" / "core")) + +import registry # noqa: E402 + +TEGELIJK = 4 +POGINGEN = 3 +IMAGE_RE = re.compile(r"^\s*image:\s*(\S+)\s*$", re.M) + + +def images() -> dict[str, list[str]]: + """Elk image in de catalogus, met de apps die het gebruiken.""" + uit: dict[str, list[str]] = {} + for pad in sorted((WORTEL / "apps").glob("*/files/*.yaml")): + for m in IMAGE_RE.finditer(pad.read_text(encoding="utf-8")): + naam = m.group(1) + if "<<" in naam: # nog niet gerenderd + continue + uit.setdefault(naam, []).append(pad.parent.parent.name) + return uit + + +def bestaat(image: str) -> bool: + for poging in range(POGINGEN): + try: + if registry.manifest_digest(image): + return True + except Exception: + pass + if poging < POGINGEN - 1: + time.sleep(2 ** poging) # 1s, 2s + return False + + +def main() -> int: + alles = images() + with cf.ThreadPoolExecutor(TEGELIJK) as ex: + uitslag = dict(zip(alles, ex.map(bestaat, alles))) + + fout = {i: alles[i] for i, ok in uitslag.items() if not ok} + if "--json" in sys.argv: + print(json.dumps({"gecontroleerd": len(alles), "fout": fout}, indent=2)) + else: + print(f"{len(alles)} unieke images gecontroleerd") + for image, apps in sorted(fout.items()): + print(f" ✖ {image} ({', '.join(sorted(set(apps)))})") + if not fout: + print(" alle images bestaan") + return 1 if fout else 0 + + +if __name__ == "__main__": + raise SystemExit(main())