feat(oci): add five application recipes

This commit is contained in:
Jonatan Castro
2026-11-03 00:27:00 +01:00
parent 9940f05adf
commit 0ab40b4eea
20 changed files with 4773 additions and 44 deletions
+14
View File
@@ -184,6 +184,7 @@
"A tunneling daemon by Cloudflare that safely exposes your web servers into the internet.": "Tunelový démon Cloudflare, ktorý bezpečne vystavuje vaše webové servery na internet.",
"A unified email and collaboration server.": "Jednotný server na e-mail a spoluprácu.",
"A unified platform for logs, metrics and traces.": "Jednotná platforma pre logy, metriky a trasovanie.",
"A validating, recursive DNS resolver for your private network.": "Validujúci rekurzívny DNS resolver pre vašu súkromnú sieť.",
"A versatile file conversion tool that supports multiple formats.": "Univerzálny nástroj na konverziu súborov, ktorý podporuje viacero formátov.",
"A visual tool for exploring and documenting database schemas.": "Vizuálny nástroj na skúmanie a dokumentovanie schém databáz.",
"A web GUI client of Project V which supports VMess, VLESS, SS, SSR, Trojan, Tuic and Juicity protocols": "Webový GUI klient projektu V, ktorý podporuje protokoly VMess, VLESS, SS, SSR, Trojan, Tuic a Juicity",
@@ -1393,6 +1394,7 @@
"Controller + NVMe": "Controller + NVMe",
"Controller + NVMe assignment will be written now and become active after host reboot.": "Priradenie Controller + NVMe sa teraz zapíše a aktivuje sa po reštarte servera.",
"Controller + NVMe passthrough requires machine type q35. Skipping controller assignment.": "Priame priradenie Controller + NVMe vyžaduje typ stroja q35. Priradenie controllera sa preskočí.",
"Controller path on the Proxmox host; prefer /dev/serial/by-id/...": "Cesta ku kontroléru na hostiteľovi Proxmox; uprednostnite /dev/serial/by-id/...",
"Controller/NVMe Conflict Policy": "Riešenie konfliktu Controller/NVMe",
"Controller/NVMe already present in VM config": "Controller/NVMe už je v nastavení VM",
"Controller/NVMe already present in VM config (%s)": "Radič/NVMe už je v nastaveniach VM (%s)",
@@ -3339,6 +3341,7 @@
"Initial Stirling-PDF account": "Počiatočný účet Stirling-PDF",
"Initial Tandoor administrator": "Počiatočný správca Tandoor",
"Initial Umami administrator": "Počiatočný správca Umami",
"Initial Z-Wave JS UI admin password": "Počiatočné heslo správcu Z-Wave JS UI",
"Initial administrator account": "Úvodný účet správcu",
"Initial administrator created:": "Pôvodný správca vytvorený:",
"Initial administrator user": "Počiatočné používateľské meno správcu",
@@ -3740,6 +3743,7 @@
"Jupyter Lab (token only)": "Jupyter Lab (iba token)",
"JupyterLab": "JupyterLab",
"JupyterLab access token": "Prístupový token JupyterLab",
"KDE's twin-panel file manager in a browser-based desktop.": "Dvojpanelový správca súborov KDE v pracovnej ploche dostupnej cez webový prehliadač.",
"KDF:": "KDF:",
"KROMA": "KROMA",
"KVM MSR options added to /etc/modprobe.d/kvm.conf": "Možnosti KVM MSR boli pridané do /etc/modprobe.d/kvm.conf",
@@ -3787,12 +3791,14 @@
"KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad – multiplatformový balík s otvoreným zdrojovým kódom na návrh elektroniky.",
"Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai nemá predvolený účet. Do kontajnera vstúpite príkazom: pct enter {main_vmid}",
"Kimai is a professional grade time-tracking application, free and open-source.": "Kimai je profesionálna bezplatná aplikácia na sledovanie pracovného času s otvoreným zdrojovým kódom.",
"KitchenOwl": "KitchenOwl",
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa poskytuje podrobnú kontrolu nad mediálnymi knižnicami. Umožňuje prispôsobiť metadáta, kolekcie, grafické prekrytia a ďalšie prvky.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa číta nastavenia z /config/config.yml, kontajner však obsahuje iba /config/config.yml.template. Skopírujte šablónu do config.yml, vyplňte požadované pripojenia Plex a TMDb a kontajner reštartujte.",
"Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga je mediálny server na komiksy, mangu, grafické romány, časopisy a elektronické knihy.",
"Kopia Web UI": "Webové rozhranie Kopia",
"Kopia Web UI login": "Prihlásenie do webového rozhrania Kopia",
"Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita je profesionálny bezplatný maliarsky program s otvoreným zdrojovým kódom. Vytvárajú ho umelci, ktorí chcú sprístupniť umelecké nástroje každému.",
"Krusader Web UI": "Webové rozhranie Krusaderu",
"LAN access to the kept containers (stack configuration incomplete):": "Prístup z LAN k ponechaným kontajnerom (nastavenie zostavy nie je dokončené):",
"LAN address applied to the application URLs": "LAN adresa použitá na URL aplikácie",
"LLDAP": "LLDAP",
@@ -4020,6 +4026,7 @@
"Manage OCI stack": "Spravovať aplikáciu s viacerými kontajnermi",
"Manage PBS encryption keyfile": "Spravovať šifrovací keyfile pre PBS",
"Manage Secure Gateway": "Spravovať Secure Gateway",
"Manage a Z-Wave network from a web dashboard and WebSocket gateway.": "Spravujte sieť Z-Wave cez webový panel a bránu WebSocket.",
"Manage and inspect VM disk images": "Spravovať a kontrolovať diskové obrazy VM",
"Manage custom backup paths": "Spravovať vlastné cesty zálohy",
"Manage custom paths (add / remove your folders)": "Spravovať vlastné cesty (pridať / odstrániť priečinky)",
@@ -4899,6 +4906,7 @@
"Open the page of an application in the menu": "Otvoriť stránku aplikácie v ponuke",
"Open the web address and create the first account: the daemon registers by itself and starts the first scan.": "Otvorte webovú adresu a vytvorte prvé konto: daemon sa zaregistruje sám a spustí prvé skenovanie.",
"Open the web address and create the password of the akadmin user in the first-run setup.": "Otvorte webovú adresu a v prvotnom nastavení vytvorte heslo používateľa akadmin.",
"Open the web address and follow KitchenOwl’s onboarding to create the first account; no default account is configured.": "Otvorte webovú adresu a podľa pokynov KitchenOwl vytvorte prvý účet; predvolený účet nie je nastavený.",
"Open-source AI-powered coding assistant": "Programátorský asistent využívajúci AI s otvoreným zdrojovým kódom",
"Open-source UI for building and debugging multi-agent and RAG applications": "Rozhranie s otvoreným zdrojovým kódom na tvorbu a ladenie aplikácií s viacerými agentmi a RAG",
"Open-source in-memory data store.": "Úložisko údajov v pamäti s otvoreným zdrojovým kódom.",
@@ -5039,6 +5047,7 @@
"Pass /dev/video12 to the LXC": "Sprístupniť /dev/video12 kontajneru LXC",
"Pass a host device to the LXC": "Sprístupniť zariadenie hostiteľa kontajneru LXC",
"Pass the UPS to the LXC": "Sprístupniť UPS kontajneru LXC",
"Pass through a Z-Wave USB or serial controller": "Pripojiť USB alebo sériový radič Z-Wave",
"Passing NVIDIA does not enable acceleration inside the application. The host needs NVIDIA Container Toolkit and a compatible image. Continue?": "Prejdenie NVIDIA neumožní zrýchlenie v aplikácii. Hostiteľ potrebuje NVIDIA Container Toolkit a kompatibilný obrázok. Pokračovať?",
"Passkey-based identity provider for self-hosted apps.": "Poskytovateľ identity založený na passkeys pre samohostované aplikácie.",
"Passphrase used to unlock the imported keyfile (leave blank if the keyfile is unencrypted / kdf=none):": "Fráza na odomknutie importovaného keyfile (nechajte prázdne, ak keyfile nie je šifrovaný / kdf=none):",
@@ -6261,6 +6270,7 @@
"Self-hosted file sync and sharing.": "Vlastná služba na synchronizáciu a zdieľanie súborov.",
"Self-hosted file toolkit for images, video, audio, PDFs, and files": "Súprava nástrojov na vlastnom serveri na prácu s obrázkami, videom, zvukom, PDF a ďalšími súbormi",
"Self-hosted forms and workflows.": "Formuláre a pracovné postupy prevádzkované na vlastnom serveri.",
"Self-hosted grocery list and recipe manager": "Správca nákupných zoznamov a receptov, ktorý si môžete prevádzkovať sami.",
"Self-hosted household plant-care tracker with reminders, photos, and multiple user accounts.": "Prehľad starostlivosti o izbové rastliny prevádzkovaný na vlastnom serveri s pripomienkami, fotografiami a viacerými používateľskými účtami.",
"Self-hosted image-hosting service with authenticated uploads, image processing, public links, and local storage.": "Služba na ukladanie a zdieľanie obrázkov na vlastnom serveri s nahrávaním pre overených používateľov, spracovaním obrázkov, verejnými odkazmi a lokálnym úložiskom.",
"Self-hosted internet archiving solution": "Riešenie na archiváciu internetového obsahu na vlastnom serveri",
@@ -6523,6 +6533,7 @@
"Specific subnet (enter manually)": "Konkrétna podsieť (zadať ručne)",
"Speedtest Tracker web interface": "Webové rozhranie Speedtest Tracker",
"Speedtest-tracker is a self-hosted internet performance tracking application that runs speedtest checks against Ookla's Speedtest service.": "Speedtest Tracker je aplikácia na vlastnom serveri na sledovanie výkonu internetového pripojenia pomocou meraní služby Ookla Speedtest.",
"Spoolman web interface": "Webové rozhranie Spoolmanu",
"Spotube is an open source, cross-platform Spotify client compatible across multiple platforms utilizing Spotify's data API and YouTube, Piped.video or JioSaavn as an audio source, eliminating the need for Spotify Premium": "Spotube je multiplatformový klient Spotify s otvoreným zdrojovým kódom. Využíva dátové API Spotify a zvuk získava z YouTube, Piped.video alebo JioSaavn, takže nevyžaduje Spotify Premium.",
"Sshwifty": "Sshwifty",
"Sshwifty web access (password only)": "Webový prístup do Sshwifty (iba heslom)",
@@ -7624,6 +7635,7 @@
"Total routes": "Počet trás",
"Total size:": "Celková veľkosť:",
"Toxiproxy": "Toxiproxy",
"Track 3D printer filament spools and usage.": "Sledujte cievky filamentu pre 3D tlačiareň a ich spotrebu.",
"Track movies, television, anime and games you want to enjoy.": "Sledujte filmy, seriály, anime a hry, ktoré si chcete pozrieť alebo zahrať.",
"Track subscriptions and recurring expenses.": "Sledovanie predplatných a opakovaných výdavkov.",
"Track your music listening history on your own server.": "Sledujte históriu počúvania hudby na vlastnom serveri.",
@@ -8315,6 +8327,8 @@
"Your machine learning Env work with Jupyter Lab": "Prostredie strojového učenia s JupyterLab",
"Your next YouTube media manager": "Váš ďalší manažér médií YouTube",
"Your_spotify is a self-hosted application that tracks what you listen and offers you a dashboard to explore statistics about it! It's composed of a web server which polls the Spotify API every now and then and a web application on which you can explore your statistics.": "Your_spotify je aplikácia na vlastnom serveri, ktorá sleduje počúvanie hudby a zobrazuje štatistiky. Tvorí ju server pravidelne zisťujúci údaje cez Spotify API a webové rozhranie na ich prezeranie.",
"Z-Wave JS UI": "Z-Wave JS UI",
"Z-Wave JS UI admin login": "Prihlásenie správcu Z-Wave JS UI",
"ZFS ARC config removed (kernel defaults will apply on reboot)": "Nastavenie ZFS ARC bolo odstránené (predvolené hodnoty kernelu sa použijú po reštarte)",
"ZFS ARC maximum configured:": "Maximálna veľkosť ZFS ARC je nastavená na:",
"ZFS ARC optimization completed": "Optimalizácia ZFS ARC je dokončená",
+1 -1
View File
@@ -2,7 +2,7 @@
<!-- Generated from oci/catalog/verification.json by .github/scripts/oci_validation.py. Do not edit by hand. -->
52 of 533 applications in the OCI catalog have been tested for real: 49 in the ProxMenux lab and 3 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
52 of 538 applications in the OCI catalog have been tested for real: 49 in the ProxMenux lab and 3 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
Each test describes the scenario that was run and names the image it ran on. It does not cover every possible configuration of the application, and a newer image has not been tested until someone reports it.
+383
View File
@@ -0,0 +1,383 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-kitchenowl",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "KitchenOwl"
},
"tagline": {
"en_US": "Self-hosted grocery list and recipe manager"
},
"description": {
"en_US": "Manage shared shopping lists, recipes and meal plans in one self-hosted app."
},
"category": "productivity",
"category_label": "Productivity & Workflows",
"author": "Tom Bursch",
"developer": "Tom Bursch",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kitchenowl.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": 80,
"path": "/"
},
"website": "https://kitchenowl.org/",
"documentation": "https://docs.kitchenowl.org/self-hosting/",
"repository": "https://github.com/TomBursch/kitchenowl",
"tips": [
"This recipe follows KitchenOwl’s standard official Compose deployment: a web frontend and backend in separate OCI LXCs.",
"The upstream sample uses PLEASE_CHANGE_ME for JWT_SECRET_KEY; this recipe generates a unique signing key at install time."
],
"mini_changelog": [],
"display_version": null,
"updated_at": "2026-11-01"
},
"source": {
"provider": "tombursch",
"repository": "https://github.com/TomBursch/kitchenowl",
"revision": "60a012ea5cae829878f78a46a585a7ee530b069a",
"image_repository_url": "https://hub.docker.com/r/tombursch/kitchenowl-web",
"readme_pushed_at": "2026-11-01T23:22:22Z",
"compose_sha256": "f620124c2a4943d4f2465ee304677bc9ed172a088f0fe43f5db7cbc8f6424263",
"generated_at": "2026-11-02T20:13:05+00:00",
"default_branch": "main",
"readme_raw_url": "https://raw.githubusercontent.com/TomBursch/kitchenowl/main/docker-compose.yml"
},
"container_contract": {
"service_name": "front",
"container_name": "front",
"image": {
"reference": "tombursch/kitchenowl-web:latest",
"registry": "docker.io",
"repository": "tombursch/kitchenowl-web",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [],
"volumes": [],
"ports": [
{
"container_port": 80,
"published_example": 80,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [
{
"name": "back",
"image": "tombursch/kitchenowl-backend:latest"
}
],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "services:\n back:\n environment:\n JWT_SECRET_KEY: ${GENERATED_JWT_SECRET_KEY}\n image: tombursch/kitchenowl-backend:latest\n restart: unless-stopped\n volumes:\n - read_only: false\n source: kitchenowl_data\n target: /data\n front:\n depends_on:\n - back\n image: tombursch/kitchenowl-web:latest\n ports:\n - 80:80\n restart: unless-stopped\n volumes: []\nvolumes:\n kitchenowl_data: null\n"
},
"compose_stack": {
"project_name": "kitchenowl",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "front",
"service_count": 2,
"services": [
{
"name": "back",
"image": "tombursch/kitchenowl-backend:latest",
"is_main": false,
"role": "dependency",
"vmid_offset": 1,
"depends_on": [],
"frontend_network": false,
"private_network": true,
"compose": {
"environment": {
"JWT_SECRET_KEY": "${GENERATED_JWT_SECRET_KEY}"
},
"image": "tombursch/kitchenowl-backend:latest",
"restart": "unless-stopped",
"volumes": [
{
"source": "kitchenowl_data",
"target": "/data",
"read_only": false
}
],
"healthcheck": {
"test": [
"CMD-SHELL",
"uwsgi_curl localhost:5000 /api/health/8M4F88S8ooi4sMbLBfkkV7ctWwgibW6V || exit 1"
],
"interval": "60s",
"timeout": "3s",
"retries": 3
}
}
},
{
"name": "front",
"image": "tombursch/kitchenowl-web:latest",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [
"back"
],
"frontend_network": true,
"private_network": true,
"compose": {
"depends_on": [
"back"
],
"image": "tombursch/kitchenowl-web:latest",
"ports": [
"80:80"
],
"restart": "unless-stopped",
"volumes": [],
"healthcheck": {
"test": [
"CMD-SHELL",
"curl -f http://localhost/ || exit 1"
],
"interval": "30s",
"timeout": "3s",
"retries": 3
}
}
}
],
"top_level": {
"volumes": {
"kitchenowl_data": null
}
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": true,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "back-volume-0",
"service": "back",
"container_path": "/data",
"mode": "managed-volume",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
"reserve_vmids_atomically": 2,
"start_order": [
"back",
"front"
],
"stop_order": [
"front",
"back"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": [
{
"id": "generated-jwt-secret-key",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "back",
"environment_variable": "JWT_SECRET_KEY"
}
]
}
]
}
},
"first_run": {
"endpoints": [
{
"label": "KitchenOwl",
"scheme": "http",
"port": 80,
"path": "/",
"source": "upstream-documentation"
}
],
"credentials": []
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 1024,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": false,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"adaptations": [
{
"id": "generated-jwt-signing-key",
"upstream_behavior": "The official Compose example uses the placeholder PLEASE_CHANGE_ME for JWT_SECRET_KEY and instructs operators to replace it.",
"native_lxc_behavior": "ProxMenux generates a unique cryptographically random JWT signing key and supplies it to the backend service.",
"reason": "Avoid deploying the documented placeholder or placing a signing secret in the public recipe.",
"behavioral_impact": "The generated key is not an app login credential; KitchenOwl has no catalog-provided default account.",
"validation": "pending-runtime"
},
{
"id": "image-healthchecks-preserved",
"upstream_behavior": "The web and backend OCI images declare Docker healthchecks in image metadata.",
"native_lxc_behavior": "The current image healthchecks are recorded in the stack so ProxMenux can check backend readiness and frontend availability during first start.",
"reason": "The Compose file inherits these checks from its images instead of declaring them itself.",
"behavioral_impact": "Checks gate stack startup; Docker does not run as a separate health-monitoring daemon inside the LXCs.",
"validation": "pending-runtime"
}
],
"custom_compose_stack": true,
"custom_compose_interpolated": false,
"stack_image_entrypoints": {},
"installer_profile": {
"stack_driver": "generic-multi-lxc-stack",
"volume_preparations": [
{
"container_path": "/data",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
]
},
"stack_completion_notes": [
"Open the web address and follow KitchenOwl’s onboarding to create the first account; no default account is configured."
]
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"stop_signal",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "The official two-image Compose stack is mapped to native Proxmox OCI LXCs with private service networking. Lab checks confirmed installation, web/API behavior, persistence, backup and recovery, modification, managed storage, and host-bind storage. Advanced custom settings have not been validated."
},
"validation": {
"schema": "passed-at-generation",
"clean_install": "passed-default-official-two-service-stack-and-first-start",
"advanced_install": "pending-custom-install-summary-validation",
"service_health": "passed-web-api-and-created-user-household-list-item",
"restart_persistence": "passed-managed-and-host-bind-data",
"backup_restore": "passed-manager-recover-new-vmids-stack-network-and-data",
"update_preserves_data": "no-update-available-at-check",
"modification": "passed-added-managed-volume-without-image-pull-or-container-recreate",
"volumes": "passed-managed-storage-and-host-bind-with-owner-and-data-check",
"removal": "passed-manager-removal-temporary-data-cleaned-after-retention-check"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": null,
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false,
"dependency_lifecycle": {
"implementation": "proxmox-hookscript",
"trigger": "main-lxc-pre-start",
"waits_for_dependency_healthchecks": true,
"stops_dependencies_with_main": false
}
}
}
+490
View File
@@ -0,0 +1,490 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-krusader",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Krusader",
"es_ES": "Krusader"
},
"tagline": {
"en_US": "KDE's twin-panel file manager in a browser-based desktop.",
"es_ES": "El gestor de archivos de dos paneles de KDE, disponible desde el navegador."
},
"description": {
"en_US": "Open Krusader in a browser and manage files from a directory you explicitly mount at /storage. The maintained jlesage image provides the graphical desktop; ProxMenux runs it as one native OCI LXC.",
"es_ES": "Abre Krusader en el navegador y gestiona los archivos de una carpeta que montes expresamente en /storage. La imagen mantenida por jlesage incluye el escritorio gráfico; ProxMenux la ejecuta como un LXC OCI nativo."
},
"category": "desktop",
"category_label": "Browsers & Web Desktops",
"author": "jlesage",
"developer": "jlesage",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/krusader.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "https",
"port": 5800,
"path": "/"
},
"website": "https://krusader.org/",
"documentation": "https://github.com/jlesage/docker-krusader",
"repository": "https://github.com/jlesage/docker-krusader",
"tips": [
"The web login uses HTTPS with an automatically generated self-signed certificate and password. Accept the certificate warning on the first visit or configure a trusted certificate.",
"Krusader can read, change and delete files in the directory mounted at /storage. Mount only the directory you intend to manage; the rest of the Proxmox host is not exposed.",
"The VNC port is not published. Use the browser interface on port 5800."
],
"mini_changelog": [],
"display_version": null,
"updated_at": null
},
"source": {
"provider": "jlesage",
"repository": "https://github.com/jlesage/docker-krusader",
"revision": "855c8140e1bb65b2a2526d82db44c808f23eb49f",
"image_repository_url": "https://hub.docker.com/r/jlesage/krusader",
"readme_pushed_at": "2026-09-22T15:18:17Z",
"compose_sha256": "9dd16cc30aa19afe7e148598c9cfb0c65c31606d67f3f22254c33a4d05212c7f",
"generated_at": "2026-11-02T20:05:40+00:00",
"default_branch": "master",
"readme_raw_url": "https://raw.githubusercontent.com/jlesage/docker-krusader/855c8140e1bb65b2a2526d82db44c808f23eb49f/README.md"
},
"container_contract": {
"service_name": "krusader",
"container_name": "krusader",
"image": {
"reference": "jlesage/krusader:latest",
"registry": "docker.io",
"repository": "jlesage/krusader",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [
{
"name": "USER_ID",
"example": "1000",
"required": true,
"sensitive": false,
"source": "upstream-image-default"
},
{
"name": "GROUP_ID",
"example": "1000",
"required": true,
"sensitive": false,
"source": "upstream-image-default"
},
{
"name": "TZ",
"example": "Europe/Madrid",
"required": true,
"sensitive": false,
"source": "ProxMenux-local-timezone"
},
{
"name": "VNC_LOCALHOST_ONLY",
"example": "1",
"required": true,
"sensitive": false,
"source": "upstream-image-security-setting"
},
{
"name": "WEB_AUTHENTICATION",
"example": "1",
"required": true,
"sensitive": false,
"source": "upstream-image-security-setting"
},
{
"name": "WEB_AUTHENTICATION_USERNAME",
"example": "admin",
"required": true,
"sensitive": false,
"source": "upstream-image-security-setting"
},
{
"name": "WEB_AUTHENTICATION_PASSWORD",
"example": "${GENERATED_WEB_PASSWORD}",
"required": true,
"sensitive": true,
"source": "ProxMenux-generated-credential"
},
{
"name": "SECURE_CONNECTION",
"example": "1",
"required": true,
"sensitive": false,
"source": "upstream-image-security-setting"
}
],
"volumes": [
{
"id": "krusader-config",
"container_path": "/config",
"compose_source_example": "krusader-config",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 4
}
},
{
"id": "krusader-storage",
"container_path": "/storage",
"compose_source_example": "/mnt/oci-shared/krusader/storage",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "host-bind",
"managed_volume": {
"backup": true,
"default_size_gb": 8
}
}
],
"ports": [
{
"container_port": 5800,
"published_example": 5800,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "name: krusader\nservices:\n krusader:\n image: jlesage/krusader:latest\n container_name: krusader\n restart: unless-stopped\n environment:\n USER_ID: '1000'\n GROUP_ID: '1000'\n TZ: Europe/Madrid\n VNC_LOCALHOST_ONLY: '1'\n WEB_AUTHENTICATION: '1'\n WEB_AUTHENTICATION_USERNAME: admin\n WEB_AUTHENTICATION_PASSWORD: ${GENERATED_WEB_PASSWORD}\n SECURE_CONNECTION: '1'\n ports:\n - 5800:5800/tcp\n volumes:\n - krusader-config:/config\n - /mnt/oci-shared/krusader/storage:/storage\nvolumes:\n krusader-config: {}\n"
},
"compose_stack": {
"project_name": "krusader",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "krusader",
"service_count": 1,
"services": [
{
"name": "krusader",
"image": "jlesage/krusader:latest",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [],
"frontend_network": true,
"private_network": false,
"compose": {
"environment": {
"GROUP_ID": "1000",
"SECURE_CONNECTION": "1",
"TZ": "Europe/Madrid",
"USER_ID": "1000",
"VNC_LOCALHOST_ONLY": "1",
"WEB_AUTHENTICATION": "1",
"WEB_AUTHENTICATION_PASSWORD": "${GENERATED_GENERATED_WEB_PASSWORD}",
"WEB_AUTHENTICATION_USERNAME": "admin"
},
"image": "jlesage/krusader:latest",
"ports": [
"5800:5800"
],
"volumes": [
"./config:/config",
"./storage:/storage"
]
}
}
],
"top_level": {
"name": "krusader"
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": false,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "krusader-volume-0",
"service": "krusader",
"container_path": "/config",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "krusader-volume-1",
"service": "krusader",
"container_path": "/storage",
"mode": "user-selectable",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": "Host directory for Krusader:/storage",
"default": "host-bind",
"installation_choice": [
"managed-volume",
"host-bind"
]
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
"krusader"
],
"stop_order": [
"krusader"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": [
{
"id": "generated-web-password",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "krusader",
"environment_variable": "WEB_AUTHENTICATION_PASSWORD"
}
]
}
]
}
},
"first_run": {
"endpoints": [
{
"label": "Krusader Web UI",
"scheme": "https",
"port": 5800,
"path": "/",
"source": "upstream-jlesage-secure-connection-and-web-authentication"
}
],
"credentials": [
{
"label": "Krusader Web UI",
"type": "configured-or-installer-generated",
"username": "admin",
"password": null,
"username_environment": "WEB_AUTHENTICATION_USERNAME",
"password_environment": "WEB_AUTHENTICATION_PASSWORD",
"change_required": false,
"source": "ProxMenux generated password and upstream image environment",
"retrieval": null
}
]
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 2048,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": false,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"adaptations": [
{
"id": "web-authentication-https",
"upstream_behavior": "The jlesage image can protect its browser GUI with WEB_AUTHENTICATION and requires SECURE_CONNECTION for authenticated web access.",
"native_lxc_behavior": "Enable authenticated HTTPS on port 5800, generate a unique password at install and keep VNC bound to localhost.",
"reason": "Krusader can modify files exposed under /storage, so the browser endpoint must not be unauthenticated.",
"behavioral_impact": "The image generates a self-signed TLS certificate; a browser may ask to trust it on first access.",
"validation": "pending"
},
{
"id": "scoped-storage-mount",
"upstream_behavior": "Docker Compose mounts the selected host directory at /storage.",
"native_lxc_behavior": "The same selected directory is mounted in the dedicated OCI LXC; no other host paths are exposed.",
"reason": "The file manager must only see data explicitly selected by the user.",
"behavioral_impact": "Write access permits changing or deleting files in that one directory.",
"validation": "pending"
},
{
"id": "dedicated-lxc-network",
"upstream_behavior": "Docker publishes web port 5800 on the Docker host.",
"native_lxc_behavior": "The native LXC listens on its own IP at port 5800 without Docker NAT.",
"reason": "The LXC has a dedicated network namespace and address.",
"behavioral_impact": "The port is reached at the LXC address rather than through a host-published port.",
"validation": "pending"
}
],
"installer_profile": {
"volume_preparations": [
{
"container_path": "/config",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"uid": 1000,
"gid": 1000,
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/storage",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"uid": 1000,
"gid": 1000,
"only_when_mount_type": "managed-volume"
}
],
"startup_healthcheck": {
"scheme": "https",
"port": 5800,
"path": "/",
"timeout_seconds": 240,
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": false
},
"generated_sensitive_environment": {
"WEB_AUTHENTICATION_PASSWORD": {
"strategy": "token-hex",
"bytes": 16,
"prompt": false
}
},
"volume_owner": {
"uid": 1000,
"gid": 1000
}
}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"stop_signal",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
},
"validation": {
"schema": "passed-at-generation",
"clean_install": "passed-first-start",
"service_health": "passed-login-page-and-authenticated-session-http-200",
"restart_persistence": "passed-container-restart-and-host-bind-write-check",
"backup_restore": "pending",
"update_preserves_data": "pending",
"image_resolution": "passed-skopeo-327mb-11-layers-integrity",
"credentials": "passed-generated-password-login-and-session-cookie",
"advanced_install": "pending-custom-install-summary-validation",
"managed_volume": "passed-managed-config-volume",
"host_bind": "passed-storage-bind-owner-and-app-user-write",
"browser_visual": "pending-self-signed-certificate-review",
"modification": "pending",
"removal": "passed-manager-remove-ct-disks-record-and-empty-test-path-cleaned",
"web_authentication": "passed-generated-credential-post-and-session-cookie"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": "amd64",
"resolved_digest": "sha256:ecb77bb73b1d3c66720eefb07805f70289349b449d1ddaa15d120c399bd89cc4",
"image_version_label": "26.09.1",
"image_created": "2026-09-22T15:22:56.230858151Z"
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false
}
}
+477
View File
@@ -0,0 +1,477 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-spoolman",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Spoolman"
},
"tagline": {
"en_US": "Track 3D printer filament spools and usage."
},
"description": {
"en_US": "A self-hosted filament inventory for 3D printers. Manage spools, materials, manufacturers, locations and labels in the web UI, and integrate with Moonraker, OctoPrint and other printer tools. The official image uses SQLite by default; no separate database container is required."
},
"category": "tools",
"category_label": "Tools",
"author": "Donkie",
"developer": "Donkie",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/spoolman.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": 8000,
"path": "/"
},
"website": "https://github.com/Donkie/Spoolman",
"documentation": "https://github.com/Donkie/Spoolman/wiki/Installation",
"repository": "https://github.com/Donkie/Spoolman",
"tips": [
"Spoolman has no built-in authentication. Keep it on a trusted network; do not expose it directly to the internet.",
"The official image uses SQLite by default and stores the database, automatic backups, logs and cache in the persistent data directory."
],
"mini_changelog": [],
"display_version": null,
"updated_at": null
},
"source": {
"provider": "Donkie",
"repository": "https://github.com/Donkie/Spoolman",
"revision": "ff8efd517b76a4b2783f29c89e9b9fbcd8cd167b",
"image_repository_url": "https://github.com/Donkie/Spoolman/pkgs/container/spoolman",
"readme_pushed_at": "2026-10-31T09:39:19Z",
"compose_sha256": "d9b115c4c37589f9813e54f926fdd22e7a10a44a6e56160f837b1247638fac83",
"generated_at": "2026-11-02T20:02:46+00:00",
"default_branch": "master",
"readme_raw_url": "https://github.com/Donkie/Spoolman/wiki/Installation"
},
"container_contract": {
"service_name": "spoolman",
"container_name": "spoolman",
"image": {
"reference": "ghcr.io/donkie/spoolman:latest",
"registry": "ghcr.io",
"repository": "ghcr.io/donkie/spoolman",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [
{
"name": "PUID",
"example": "1000",
"required": true,
"sensitive": false,
"source": "upstream-image-default",
"prompt_user": false
},
{
"name": "PGID",
"example": "1000",
"required": true,
"sensitive": false,
"source": "upstream-image-default",
"prompt_user": false
},
{
"name": "TZ",
"example": "Etc/UTC",
"required": true,
"sensitive": false,
"source": "upstream-compose",
"prompt_user": false
}
],
"volumes": [
{
"id": "volume-0",
"container_path": "/home/app/.local/share/spoolman",
"compose_source_example": "./data",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 4
}
}
],
"ports": [
{
"container_port": 8000,
"published_example": 7912,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "services:\n spoolman:\n image: ghcr.io/donkie/spoolman:latest\n container_name: spoolman\n restart: unless-stopped\n ports:\n - \"7912:8000/tcp\"\n volumes:\n - ./data:/home/app/.local/share/spoolman\n environment:\n PUID: \"1000\"\n PGID: \"1000\"\n TZ: Etc/UTC\n"
},
"compose_stack": {
"project_name": "spoolman",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "spoolman",
"service_count": 1,
"services": [
{
"name": "spoolman",
"image": "ghcr.io/donkie/spoolman:latest",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [],
"frontend_network": true,
"private_network": false,
"compose": {
"image": "ghcr.io/donkie/spoolman:latest",
"container_name": "spoolman",
"restart": "unless-stopped",
"ports": [
"7912:8000/tcp"
],
"volumes": [
"./data:/home/app/.local/share/spoolman"
],
"environment": {
"PUID": "1000",
"PGID": "1000",
"TZ": "Etc/UTC"
}
}
}
],
"top_level": {
"name": "spoolman"
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": false,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "spoolman-volume-0",
"service": "spoolman",
"container_path": "/home/app/.local/share/spoolman",
"mode": "managed-volume",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
"spoolman"
],
"stop_order": [
"spoolman"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": []
}
},
"first_run": {
"endpoints": [
{
"label": "Spoolman web interface",
"scheme": "http",
"port": 8000,
"path": "/",
"source": "upstream-image"
}
],
"credentials": []
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 1024,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": false,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"adaptations": [
{
"id": "imported-compose-source",
"upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
"native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
"reason": "Catalog import must not imply runtime compatibility.",
"behavioral_impact": "No automatic installation before review.",
"validation": "pending-per-application"
},
{
"id": "rolling-latest-image",
"upstream_behavior": "A discovered Compose may pin a release tag or digest.",
"native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
"reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
"behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
"validation": "pending-per-application"
},
{
"id": "dedicated-lxc-network",
"upstream_behavior": "Docker publishes selected container ports on the Docker host.",
"native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
"reason": "A native LXC has its own address and does not require Docker port NAT.",
"behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
"validation": "pending-per-application"
},
{
"id": "compose-shm-size",
"upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
"native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
"reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
"behavioral_impact": "None expected.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-command",
"upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
"native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
"reason": "Proxmox stores the effective OCI process as one entrypoint string.",
"behavioral_impact": "None expected.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-privileged-mode",
"upstream_behavior": "Compose selects whether the container runs in privileged mode.",
"native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
"reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
"behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
"validation": "native-equivalent"
},
{
"id": "compose-process-runtime",
"upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
"native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
"reason": "The OCI process must start with the same identity, command and working directory without Docker.",
"behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-healthcheck",
"upstream_behavior": "Docker periodically executes the declared container healthcheck.",
"native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
"reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
"behavioral_impact": "The check runs during installation rather than continuously after installation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-cpu-priority",
"upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
"native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
"reason": "Both settings express relative CPU priority on different scales.",
"behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-resource-limits",
"upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
"native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
"reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
"behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-network-identity",
"upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
"native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
"reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
"behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-network-mode",
"upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
"native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
"reason": "The LXC is the application host and already has its own address and port namespace.",
"behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-capabilities-and-sysctls",
"upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
"native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
"reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
"behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
"validation": "not-requested-by-compose"
},
{
"id": "docker-engine-metadata",
"upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
"native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
"reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
"behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-device-passthrough",
"upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
"native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
"reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
"behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-host-ipc",
"upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
"native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
"reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
"behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
"validation": "not-requested-by-compose"
}
],
"installer_profile": {
"startup_healthcheck": {
"scheme": "http",
"port": 8000,
"path": "/",
"timeout_seconds": 180,
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": true
},
"volume_preparations": [
{
"container_path": "/home/app/.local/share/spoolman",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
],
"volume_owner": {
"uid": 1000,
"gid": 1000
}
}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
},
"validation": {
"schema": "passed-at-generation",
"clean_install": "passed-default-official-image-skopeo-and-first-start-http",
"advanced_install": "pending-custom-install-summary-validation",
"service_health": "passed-web-api-crud",
"restart_persistence": "passed-managed-and-host-bind-database-and-records",
"backup_restore": "passed-pbs-backup-and-manager-recover-record-preserved",
"update_preserves_data": "no-update-available-at-check",
"modification": "passed-cpu-ram-up-and-down-without-image-pull-or-recreate",
"volumes": "passed-managed-storage-and-host-bind-crud-owner-and-data-check",
"removal": "passed-manager-removal-host-path-retained-then-temporary-data-cleaned"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": null,
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false
}
}
+292
View File
@@ -0,0 +1,292 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-unbound",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Unbound"
},
"tagline": {
"en_US": "A validating, recursive DNS resolver for your private network."
},
"description": {
"en_US": "Run the community-maintained klutchell image of Unbound as a DNS resolver on its own Proxmox LXC. It listens on TCP and UDP port 53 and can load optional custom configuration files."
},
"category": "adblock",
"category_label": "Adblock & DNS",
"author": "Kyle Harding",
"developer": "Kyle Harding (klutchell)",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/unbound.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64",
"armhf"
],
"launch": {
"scheme": "http",
"port": null,
"path": "/"
},
"website": "https://unbound.net/",
"documentation": "https://github.com/klutchell/unbound-docker#readme",
"repository": "https://github.com/klutchell/unbound-docker",
"tips": [
"Unbound has no web interface. Use the LXC IP as a DNS server on TCP and UDP port 53.",
"The default image config listens on all interfaces and accepts clients from private IPv4 ranges. Keep it behind your trusted LAN firewall; do not expose recursive DNS publicly.",
"The original GitHub repository has moved to klutchell/unbound-docker; the published Docker Hub image remains klutchell/unbound:main. The image is an unofficial community image, not published by NLnet Labs.",
"Optional custom .conf files go under /etc/unbound/custom.conf.d and must be readable by UID/GID 101:102 or world-readable."
],
"mini_changelog": [],
"display_version": null,
"updated_at": null
},
"source": {
"provider": "klutchell",
"repository": "https://github.com/klutchell/unbound-docker",
"revision": "1efbea960bf9462b6fb6dd71aa702ad32d6820d7",
"default_branch": "main",
"image_repository_url": "https://hub.docker.com/r/klutchell/unbound",
"readme_pushed_at": "2026-11-02T14:31:22Z",
"compose_sha256": "faf73a93c1f7d4d43f1eb61ba2e62a4418bd501f0c827b080ea9897964e75848",
"generated_at": "2026-11-02T21:21:54+00:00",
"readme_raw_url": "https://raw.githubusercontent.com/klutchell/unbound-docker/main/README.md"
},
"container_contract": {
"service_name": "unbound",
"container_name": "unbound",
"image": {
"reference": "klutchell/unbound:main",
"registry": "docker.io",
"repository": "klutchell/unbound",
"tag": "main",
"digest": null,
"pull_policy": "resolve-selected-tag-to-architecture-digest-at-install"
},
"environment": [],
"volumes": [
{
"id": "volume-0",
"container_path": "/etc/unbound/custom.conf.d",
"compose_source_example": "./config",
"read_only": false,
"required": false,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "host-bind",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
{
"container_port": 53,
"published_example": 53,
"protocol": "udp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
},
{
"container_port": 53,
"published_example": 53,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "name: unbound\nservices:\n unbound:\n image: klutchell/unbound:main\n container_name: unbound\n restart: unless-stopped\n ports:\n - 53:53/udp\n - 53:53/tcp\n volumes:\n - ./config:/etc/unbound/custom.conf.d\n"
},
"compose_stack": {
"project_name": "unbound",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "unbound",
"service_count": 1,
"services": [
{
"name": "unbound",
"image": "klutchell/unbound:main",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [],
"frontend_network": true,
"private_network": false,
"compose": {
"image": "klutchell/unbound:main",
"container_name": "unbound",
"restart": "unless-stopped",
"ports": [
"53:53/udp",
"53:53/tcp"
],
"volumes": [
"./config:/etc/unbound/custom.conf.d"
]
}
}
],
"top_level": {
"name": "unbound"
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": false,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "unbound-volume-0",
"service": "unbound",
"container_path": "/etc/unbound/custom.conf.d",
"mode": "user-selectable",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": "Host directory containing optional custom Unbound .conf files",
"default": "host-bind",
"installation_choice": [
"managed-volume",
"host-bind"
]
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
"unbound"
],
"stop_order": [
"unbound"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
]
}
},
"first_run": {
"endpoints": [],
"credentials": []
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 512,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": false,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 120
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"adaptations": [],
"installer_profile": {
"volume_preparations": [
{
"container_path": "/etc/unbound/custom.conf.d",
"owner_strategy": "mapped-root",
"remove_lost_found": true,
"only_when_mount_type": "managed-volume"
}
],
"startup_healthcheck": {
"type": "running",
"timeout_seconds": 90,
"stability_seconds": 20
}
},
"catalog": {
"replaces_discovered_ids": []
}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"container_name",
"image",
"ports",
"restart",
"volumes"
],
"untranslated_blockers": [],
"policy": "This single-image Compose contract maps to one unprivileged Proxmox LXC. The optional custom-config directory can use a Proxmox-managed volume or a host directory; the image defaults work with an empty directory."
},
"validation": {
"schema": "passed-at-generation",
"image_resolution": "passed-skopeo-16-layers",
"clean_install": "passed-default-install-and-first-start-healthcheck",
"advanced_install": "pending",
"service_health": "passed-recursion-and-custom-local-data-over-udp-and-tcp",
"restart_persistence": "passed-custom-zone-after-pct-reboot",
"backup_restore": "pending",
"update_preserves_data": "pending-update-check-not-run",
"modification": "pending-manager-ui-unresponsive-no-changes-applied",
"managed_volume": "passed-config-volume-1g",
"host_bind": "pending",
"removal": "passed-manager-remove-ct-disks-and-record-cleaned"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": null,
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false
}
}
+302
View File
@@ -0,0 +1,302 @@
{
"schema_version": "0.3.0",
"kind": "proxmenux.oci-template",
"id": "image-zwave-js-ui",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Z-Wave JS UI"
},
"tagline": {
"en_US": "Manage a Z-Wave network from a web dashboard and WebSocket gateway."
},
"description": {
"en_US": "A self-hosted Z-Wave control panel and MQTT gateway. Its web interface can start without a Z-Wave controller; device control requires a compatible USB or serial controller and network security keys."
},
"category": "smarthome",
"author": "Z-Wave JS",
"developer": "Z-Wave JS",
"icon": "https://raw.githubusercontent.com/zwave-js/zwave-js-ui/c38e0d2cbbb826bf2bab2db596a3452e59ce0e8e/public/apple-touch-icon-180x180.png",
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": 8091,
"path": "/"
},
"website": "https://zwave-js.github.io/zwave-js-ui/",
"documentation": "https://zwave-js.github.io/zwave-js-ui/#/getting-started/docker",
"repository": "https://github.com/zwave-js/zwave-js-ui",
"tips": [
"The web UI can start without a Z-Wave controller. To control devices, pass through a compatible USB/serial controller and select its stable /dev/serial/by-id/... path in Settings → Z-Wave → Serial Port.",
"Configure and securely back up the S0/S2 network keys in Settings → Z-Wave before pairing security-enabled devices.",
"Port 3000 is the Z-Wave JS WebSocket server for integrations such as Home Assistant; it listens only after a Z-Wave client is configured. Upstream documents it as unauthenticated, so expose it only to trusted integrations and networks.",
"ProxMenux enables the web login and generates the initial admin password. Configure trusted HTTPS before remote access over an untrusted network."
],
"mini_changelog": [],
"display_version": "11.25.2",
"updated_at": "2026-11-02",
"category_label": "IoT & Smart Home"
},
"source": {
"provider": "Z-Wave JS",
"repository": "https://github.com/zwave-js/zwave-js-ui",
"default_branch": "master",
"revision": "c38e0d2cbbb826bf2bab2db596a3452e59ce0e8e",
"readme_raw_url": "https://raw.githubusercontent.com/zwave-js/zwave-js-ui/c38e0d2cbbb826bf2bab2db596a3452e59ce0e8e/README.md",
"image_repository_url": "https://hub.docker.com/r/zwavejs/zwave-js-ui",
"compose_sha256": "ed371d9e9805db5d2abd356b54e3ff5fdb677d637857313f6aac3f498f480f87",
"generated_at": "2026-11-02T20:10:33+00:00"
},
"container_contract": {
"service_name": "zwave-js-ui",
"container_name": "zwave-js-ui",
"image": {
"reference": "docker.io/zwavejs/zwave-js-ui:latest",
"registry": "docker.io",
"repository": "zwavejs/zwave-js-ui",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-selected-tag-to-architecture-digest-at-install"
},
"environment": [
{
"name": "SESSION_SECRET",
"example": "${GENERATED_SESSION_SECRET}",
"required": true,
"sensitive": true,
"source": "ProxMenux-generated; supported by upstream image"
},
{
"name": "DEFAULT_PASSWORD",
"example": "${GENERATED_ADMIN_PASSWORD}",
"prompt": "Initial Z-Wave JS UI admin password",
"required": true,
"sensitive": true,
"source": "Upstream image; generated by ProxMenux"
}
],
"volumes": [
{
"id": "zwave-config",
"container_path": "/usr/src/app/store",
"compose_source_example": "zwave-config",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 4
}
}
],
"ports": [
{
"container_port": 8091,
"published_example": 8091,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
},
{
"container_port": 3000,
"published_example": 3000,
"protocol": "tcp",
"required": false,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "always",
"stop_grace_period": null,
"original_compose": "version: '3.7'\nservices:\n zwave-js-ui:\n container_name: zwave-js-ui\n image: zwavejs/zwave-js-ui:latest\n restart: always\n tty: true\n stop_signal: SIGINT\n environment:\n SESSION_SECRET: ${GENERATED_SESSION_SECRET}\n DEFAULT_PASSWORD: ${GENERATED_ADMIN_PASSWORD}\n networks:\n - zwave\n devices:\n - /dev/serial/by-id/insert_stick_reference_here:/dev/zwave\n volumes:\n - zwave-config:/usr/src/app/store\n ports:\n - '8091:8091'\n - '3000:3000'\nnetworks:\n zwave:\nvolumes:\n zwave-config:\n name: zwave-config\n"
},
"first_run": {
"endpoints": [
{
"label": "Z-Wave JS UI",
"scheme": "http",
"port": 8091,
"path": "/",
"source": "official-docker-documentation"
}
],
"credentials": [
{
"label": "Z-Wave JS UI admin login",
"type": "configured-or-installer-generated",
"username": "admin",
"password": null,
"password_environment": "DEFAULT_PASSWORD",
"change_required": false,
"source": "Upstream DEFAULT_PASSWORD, generated by ProxMenux",
"retrieval": null
}
]
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 1024,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": true,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 60
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "apply-compose-override"
},
"adaptations": [
{
"id": "dedicated-lxc-network",
"upstream_behavior": "Docker publishes ports on a Docker host address and attaches the service to its Compose network.",
"native_lxc_behavior": "The OCI process listens on the dedicated LXC address; ProxMenux maps the single-service network to the LXC network namespace.",
"reason": "A one-service native OCI LXC has its own network namespace and address.",
"behavioral_impact": "Use the LXC address directly; no Docker port NAT or separate bridge is created.",
"validation": "pending-per-application"
},
{
"id": "generated-login-and-session-secret",
"upstream_behavior": "The image defaults to user admin and password zwave; web authentication is disabled by default, and the Compose example uses a sample session secret.",
"native_lxc_behavior": "ProxMenux generates a unique initial admin password and session secret, and seeds gateway.authEnabled=true before first boot.",
"reason": "Avoid shipping a shared password, sample session key or unauthenticated web UI.",
"behavioral_impact": "The installer must display the generated admin password; change it after first login.",
"validation": "pending-per-application"
},
{
"id": "stop-signal",
"upstream_behavior": "Compose requests SIGINT when stopping the container.",
"native_lxc_behavior": "ProxMenux uses the same signal for the OCI process shutdown lifecycle.",
"reason": "Preserve the upstream graceful-shutdown behavior.",
"behavioral_impact": "None expected.",
"validation": "pending-per-application"
}
],
"installer_profile": {
"generated_sensitive_environment": {
"SESSION_SECRET": {
"strategy": "token-hex",
"bytes": 32,
"prompt": false
},
"DEFAULT_PASSWORD": {
"strategy": "token-hex",
"bytes": 20,
"prompt": true
}
},
"generated_files": [
{
"container_path": "/usr/src/app/store/settings.json",
"mode": "0600",
"owner": "mapped-root",
"content": "{\"gateway\":{\"authEnabled\":true}}\n",
"only_if_missing": true
}
],
"volume_preparations": [
{
"container_path": "/usr/src/app/store",
"remove_lost_found": true,
"owner_strategy": "mapped-root",
"only_when_mount_type": "managed-volume"
}
],
"optional_devices": [
{
"id": "zwave-serial-controller",
"kind": "character-device",
"purpose": "serial",
"enable_prompt": "Pass through a Z-Wave USB or serial controller",
"enabled_default": false,
"required_by_compose": false,
"path_prompt": "Controller path on the Proxmox host; prefer /dev/serial/by-id/...",
"host_path_default": "/dev/serial/by-id/insert_stick_reference_here",
"container_path_strategy": "same-as-host",
"mode": "preserve-host",
"deny_write": false,
"gid_strategy": "host-device-gid"
}
],
"startup_healthcheck": {
"scheme": "http",
"port": 8091,
"path": "/",
"timeout_seconds": 180,
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": false
}
}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"container_name",
"devices",
"environment",
"image",
"networks",
"ports",
"restart",
"stop_signal",
"tty",
"volumes"
],
"untranslated_blockers": [],
"policy": "The single-image recipe has passed installation, UI/login, restart, backup and recovery, modification, and engine checks. Host-bind storage has not been checked. Radio control remains untested because no compatible Z-Wave coordinator was attached during the lab test."
},
"validation": {
"schema": "passed-static-validation",
"image_resolution": "passed-skopeo-download-and-layer-verification",
"clean_install": "passed-default-install-and-health-check",
"advanced_install": "pending-custom-install-summary-validation",
"web_response": "passed-ui-and-generated-login",
"credentials": "passed-generated-login-before-and-after-restore",
"restart_persistence": "passed-restart-with-store-preserved",
"backup_restore": "passed-proxmox-backup-and-manager-recover-new-vmid",
"update_preserves_data": "no-update-available-at-check",
"modification": "passed-cpu-ram-up-down-without-recreation",
"managed_volume": "passed-store-volume-and-recovery",
"host_bind": "pending",
"hardware_radio": "not-tested-no-z-wave-coordinator-on-host",
"removal": "passed-remove-original-and-restored-ct-and-local-backup-cleanup"
},
"lifecycle": {
"update_strategy": "replace-rootfs-from-new-oci-image-preserve-managed-volumes",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": "11.25.2",
"image_created": null
},
"change_detection": "compare-resolved-architecture-digest",
"automatic_unattended_updates": false
}
}
+5 -1
View File
@@ -432,6 +432,10 @@
"metabase": "monitoring",
"kopia": "backup",
"restic-rest-server": "backup",
"netalertx": "monitoring"
"netalertx": "monitoring",
"kitchenowl": "productivity",
"unbound": "adblock",
"spoolman": "tools",
"zwave-js-ui": "smarthome"
}
}
+383
View File
@@ -0,0 +1,383 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-kitchenowl",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "KitchenOwl"
},
"tagline": {
"en_US": "Self-hosted grocery list and recipe manager"
},
"description": {
"en_US": "Manage shared shopping lists, recipes and meal plans in one self-hosted app."
},
"category": "productivity",
"category_label": "Productivity & Workflows",
"author": "Tom Bursch",
"developer": "Tom Bursch",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kitchenowl.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": 80,
"path": "/"
},
"website": "https://kitchenowl.org/",
"documentation": "https://docs.kitchenowl.org/self-hosting/",
"repository": "https://github.com/TomBursch/kitchenowl",
"tips": [
"This recipe follows KitchenOwl’s standard official Compose deployment: a web frontend and backend in separate OCI LXCs.",
"The upstream sample uses PLEASE_CHANGE_ME for JWT_SECRET_KEY; this recipe generates a unique signing key at install time."
],
"mini_changelog": [],
"display_version": null,
"updated_at": "2026-11-01"
},
"source": {
"provider": "tombursch",
"repository": "https://github.com/TomBursch/kitchenowl",
"revision": "60a012ea5cae829878f78a46a585a7ee530b069a",
"image_repository_url": "https://hub.docker.com/r/tombursch/kitchenowl-web",
"readme_pushed_at": "2026-11-01T23:22:22Z",
"compose_sha256": "f620124c2a4943d4f2465ee304677bc9ed172a088f0fe43f5db7cbc8f6424263",
"generated_at": "2026-11-02T20:13:05+00:00",
"default_branch": "main",
"readme_raw_url": "https://raw.githubusercontent.com/TomBursch/kitchenowl/main/docker-compose.yml"
},
"container_contract": {
"service_name": "front",
"container_name": "front",
"image": {
"reference": "tombursch/kitchenowl-web:latest",
"registry": "docker.io",
"repository": "tombursch/kitchenowl-web",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [],
"volumes": [],
"ports": [
{
"container_port": 80,
"published_example": 80,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [
{
"name": "back",
"image": "tombursch/kitchenowl-backend:latest"
}
],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "services:\n back:\n environment:\n JWT_SECRET_KEY: ${GENERATED_JWT_SECRET_KEY}\n image: tombursch/kitchenowl-backend:latest\n restart: unless-stopped\n volumes:\n - read_only: false\n source: kitchenowl_data\n target: /data\n front:\n depends_on:\n - back\n image: tombursch/kitchenowl-web:latest\n ports:\n - 80:80\n restart: unless-stopped\n volumes: []\nvolumes:\n kitchenowl_data: null\n"
},
"compose_stack": {
"project_name": "kitchenowl",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "front",
"service_count": 2,
"services": [
{
"name": "back",
"image": "tombursch/kitchenowl-backend:latest",
"is_main": false,
"role": "dependency",
"vmid_offset": 1,
"depends_on": [],
"frontend_network": false,
"private_network": true,
"compose": {
"environment": {
"JWT_SECRET_KEY": "${GENERATED_JWT_SECRET_KEY}"
},
"image": "tombursch/kitchenowl-backend:latest",
"restart": "unless-stopped",
"volumes": [
{
"source": "kitchenowl_data",
"target": "/data",
"read_only": false
}
],
"healthcheck": {
"test": [
"CMD-SHELL",
"uwsgi_curl localhost:5000 /api/health/8M4F88S8ooi4sMbLBfkkV7ctWwgibW6V || exit 1"
],
"interval": "60s",
"timeout": "3s",
"retries": 3
}
}
},
{
"name": "front",
"image": "tombursch/kitchenowl-web:latest",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [
"back"
],
"frontend_network": true,
"private_network": true,
"compose": {
"depends_on": [
"back"
],
"image": "tombursch/kitchenowl-web:latest",
"ports": [
"80:80"
],
"restart": "unless-stopped",
"volumes": [],
"healthcheck": {
"test": [
"CMD-SHELL",
"curl -f http://localhost/ || exit 1"
],
"interval": "30s",
"timeout": "3s",
"retries": 3
}
}
}
],
"top_level": {
"volumes": {
"kitchenowl_data": null
}
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": true,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "back-volume-0",
"service": "back",
"container_path": "/data",
"mode": "managed-volume",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
"reserve_vmids_atomically": 2,
"start_order": [
"back",
"front"
],
"stop_order": [
"front",
"back"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": [
{
"id": "generated-jwt-secret-key",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "back",
"environment_variable": "JWT_SECRET_KEY"
}
]
}
]
}
},
"first_run": {
"endpoints": [
{
"label": "KitchenOwl",
"scheme": "http",
"port": 80,
"path": "/",
"source": "upstream-documentation"
}
],
"credentials": []
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 1024,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": false,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"adaptations": [
{
"id": "generated-jwt-signing-key",
"upstream_behavior": "The official Compose example uses the placeholder PLEASE_CHANGE_ME for JWT_SECRET_KEY and instructs operators to replace it.",
"native_lxc_behavior": "ProxMenux generates a unique cryptographically random JWT signing key and supplies it to the backend service.",
"reason": "Avoid deploying the documented placeholder or placing a signing secret in the public recipe.",
"behavioral_impact": "The generated key is not an app login credential; KitchenOwl has no catalog-provided default account.",
"validation": "pending-runtime"
},
{
"id": "image-healthchecks-preserved",
"upstream_behavior": "The web and backend OCI images declare Docker healthchecks in image metadata.",
"native_lxc_behavior": "The current image healthchecks are recorded in the stack so ProxMenux can check backend readiness and frontend availability during first start.",
"reason": "The Compose file inherits these checks from its images instead of declaring them itself.",
"behavioral_impact": "Checks gate stack startup; Docker does not run as a separate health-monitoring daemon inside the LXCs.",
"validation": "pending-runtime"
}
],
"custom_compose_stack": true,
"custom_compose_interpolated": false,
"stack_image_entrypoints": {},
"installer_profile": {
"stack_driver": "generic-multi-lxc-stack",
"volume_preparations": [
{
"container_path": "/data",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
]
},
"stack_completion_notes": [
"Open the web address and follow KitchenOwl’s onboarding to create the first account; no default account is configured."
]
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"stop_signal",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "The official two-image Compose stack is mapped to native Proxmox OCI LXCs with private service networking. Lab checks confirmed installation, web/API behavior, persistence, backup and recovery, modification, managed storage, and host-bind storage. Advanced custom settings have not been validated."
},
"validation": {
"schema": "passed-at-generation",
"clean_install": "passed-default-official-two-service-stack-and-first-start",
"advanced_install": "pending-custom-install-summary-validation",
"service_health": "passed-web-api-and-created-user-household-list-item",
"restart_persistence": "passed-managed-and-host-bind-data",
"backup_restore": "passed-manager-recover-new-vmids-stack-network-and-data",
"update_preserves_data": "no-update-available-at-check",
"modification": "passed-added-managed-volume-without-image-pull-or-container-recreate",
"volumes": "passed-managed-storage-and-host-bind-with-owner-and-data-check",
"removal": "passed-manager-removal-temporary-data-cleaned-after-retention-check"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": null,
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false,
"dependency_lifecycle": {
"implementation": "proxmox-hookscript",
"trigger": "main-lxc-pre-start",
"waits_for_dependency_healthchecks": true,
"stops_dependencies_with_main": false
}
}
}
+490
View File
@@ -0,0 +1,490 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-krusader",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Krusader",
"es_ES": "Krusader"
},
"tagline": {
"en_US": "KDE's twin-panel file manager in a browser-based desktop.",
"es_ES": "El gestor de archivos de dos paneles de KDE, disponible desde el navegador."
},
"description": {
"en_US": "Open Krusader in a browser and manage files from a directory you explicitly mount at /storage. The maintained jlesage image provides the graphical desktop; ProxMenux runs it as one native OCI LXC.",
"es_ES": "Abre Krusader en el navegador y gestiona los archivos de una carpeta que montes expresamente en /storage. La imagen mantenida por jlesage incluye el escritorio gráfico; ProxMenux la ejecuta como un LXC OCI nativo."
},
"category": "desktop",
"category_label": "Browsers & Web Desktops",
"author": "jlesage",
"developer": "jlesage",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/krusader.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "https",
"port": 5800,
"path": "/"
},
"website": "https://krusader.org/",
"documentation": "https://github.com/jlesage/docker-krusader",
"repository": "https://github.com/jlesage/docker-krusader",
"tips": [
"The web login uses HTTPS with an automatically generated self-signed certificate and password. Accept the certificate warning on the first visit or configure a trusted certificate.",
"Krusader can read, change and delete files in the directory mounted at /storage. Mount only the directory you intend to manage; the rest of the Proxmox host is not exposed.",
"The VNC port is not published. Use the browser interface on port 5800."
],
"mini_changelog": [],
"display_version": null,
"updated_at": null
},
"source": {
"provider": "jlesage",
"repository": "https://github.com/jlesage/docker-krusader",
"revision": "855c8140e1bb65b2a2526d82db44c808f23eb49f",
"image_repository_url": "https://hub.docker.com/r/jlesage/krusader",
"readme_pushed_at": "2026-09-22T15:18:17Z",
"compose_sha256": "9dd16cc30aa19afe7e148598c9cfb0c65c31606d67f3f22254c33a4d05212c7f",
"generated_at": "2026-11-02T20:05:40+00:00",
"default_branch": "master",
"readme_raw_url": "https://raw.githubusercontent.com/jlesage/docker-krusader/855c8140e1bb65b2a2526d82db44c808f23eb49f/README.md"
},
"container_contract": {
"service_name": "krusader",
"container_name": "krusader",
"image": {
"reference": "jlesage/krusader:latest",
"registry": "docker.io",
"repository": "jlesage/krusader",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [
{
"name": "USER_ID",
"example": "1000",
"required": true,
"sensitive": false,
"source": "upstream-image-default"
},
{
"name": "GROUP_ID",
"example": "1000",
"required": true,
"sensitive": false,
"source": "upstream-image-default"
},
{
"name": "TZ",
"example": "Europe/Madrid",
"required": true,
"sensitive": false,
"source": "ProxMenux-local-timezone"
},
{
"name": "VNC_LOCALHOST_ONLY",
"example": "1",
"required": true,
"sensitive": false,
"source": "upstream-image-security-setting"
},
{
"name": "WEB_AUTHENTICATION",
"example": "1",
"required": true,
"sensitive": false,
"source": "upstream-image-security-setting"
},
{
"name": "WEB_AUTHENTICATION_USERNAME",
"example": "admin",
"required": true,
"sensitive": false,
"source": "upstream-image-security-setting"
},
{
"name": "WEB_AUTHENTICATION_PASSWORD",
"example": "${GENERATED_WEB_PASSWORD}",
"required": true,
"sensitive": true,
"source": "ProxMenux-generated-credential"
},
{
"name": "SECURE_CONNECTION",
"example": "1",
"required": true,
"sensitive": false,
"source": "upstream-image-security-setting"
}
],
"volumes": [
{
"id": "krusader-config",
"container_path": "/config",
"compose_source_example": "krusader-config",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 4
}
},
{
"id": "krusader-storage",
"container_path": "/storage",
"compose_source_example": "/mnt/oci-shared/krusader/storage",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "host-bind",
"managed_volume": {
"backup": true,
"default_size_gb": 8
}
}
],
"ports": [
{
"container_port": 5800,
"published_example": 5800,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "name: krusader\nservices:\n krusader:\n image: jlesage/krusader:latest\n container_name: krusader\n restart: unless-stopped\n environment:\n USER_ID: '1000'\n GROUP_ID: '1000'\n TZ: Europe/Madrid\n VNC_LOCALHOST_ONLY: '1'\n WEB_AUTHENTICATION: '1'\n WEB_AUTHENTICATION_USERNAME: admin\n WEB_AUTHENTICATION_PASSWORD: ${GENERATED_WEB_PASSWORD}\n SECURE_CONNECTION: '1'\n ports:\n - 5800:5800/tcp\n volumes:\n - krusader-config:/config\n - /mnt/oci-shared/krusader/storage:/storage\nvolumes:\n krusader-config: {}\n"
},
"compose_stack": {
"project_name": "krusader",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "krusader",
"service_count": 1,
"services": [
{
"name": "krusader",
"image": "jlesage/krusader:latest",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [],
"frontend_network": true,
"private_network": false,
"compose": {
"environment": {
"GROUP_ID": "1000",
"SECURE_CONNECTION": "1",
"TZ": "Europe/Madrid",
"USER_ID": "1000",
"VNC_LOCALHOST_ONLY": "1",
"WEB_AUTHENTICATION": "1",
"WEB_AUTHENTICATION_PASSWORD": "${GENERATED_GENERATED_WEB_PASSWORD}",
"WEB_AUTHENTICATION_USERNAME": "admin"
},
"image": "jlesage/krusader:latest",
"ports": [
"5800:5800"
],
"volumes": [
"./config:/config",
"./storage:/storage"
]
}
}
],
"top_level": {
"name": "krusader"
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": false,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "krusader-volume-0",
"service": "krusader",
"container_path": "/config",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "krusader-volume-1",
"service": "krusader",
"container_path": "/storage",
"mode": "user-selectable",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": "Host directory for Krusader:/storage",
"default": "host-bind",
"installation_choice": [
"managed-volume",
"host-bind"
]
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
"krusader"
],
"stop_order": [
"krusader"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": [
{
"id": "generated-web-password",
"strategy": "generate-cryptographically-random-at-install",
"bindings": [
{
"service": "krusader",
"environment_variable": "WEB_AUTHENTICATION_PASSWORD"
}
]
}
]
}
},
"first_run": {
"endpoints": [
{
"label": "Krusader Web UI",
"scheme": "https",
"port": 5800,
"path": "/",
"source": "upstream-jlesage-secure-connection-and-web-authentication"
}
],
"credentials": [
{
"label": "Krusader Web UI",
"type": "configured-or-installer-generated",
"username": "admin",
"password": null,
"username_environment": "WEB_AUTHENTICATION_USERNAME",
"password_environment": "WEB_AUTHENTICATION_PASSWORD",
"change_required": false,
"source": "ProxMenux generated password and upstream image environment",
"retrieval": null
}
]
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 2048,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": false,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"adaptations": [
{
"id": "web-authentication-https",
"upstream_behavior": "The jlesage image can protect its browser GUI with WEB_AUTHENTICATION and requires SECURE_CONNECTION for authenticated web access.",
"native_lxc_behavior": "Enable authenticated HTTPS on port 5800, generate a unique password at install and keep VNC bound to localhost.",
"reason": "Krusader can modify files exposed under /storage, so the browser endpoint must not be unauthenticated.",
"behavioral_impact": "The image generates a self-signed TLS certificate; a browser may ask to trust it on first access.",
"validation": "pending"
},
{
"id": "scoped-storage-mount",
"upstream_behavior": "Docker Compose mounts the selected host directory at /storage.",
"native_lxc_behavior": "The same selected directory is mounted in the dedicated OCI LXC; no other host paths are exposed.",
"reason": "The file manager must only see data explicitly selected by the user.",
"behavioral_impact": "Write access permits changing or deleting files in that one directory.",
"validation": "pending"
},
{
"id": "dedicated-lxc-network",
"upstream_behavior": "Docker publishes web port 5800 on the Docker host.",
"native_lxc_behavior": "The native LXC listens on its own IP at port 5800 without Docker NAT.",
"reason": "The LXC has a dedicated network namespace and address.",
"behavioral_impact": "The port is reached at the LXC address rather than through a host-published port.",
"validation": "pending"
}
],
"installer_profile": {
"volume_preparations": [
{
"container_path": "/config",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"uid": 1000,
"gid": 1000,
"only_when_mount_type": "managed-volume"
},
{
"container_path": "/storage",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"uid": 1000,
"gid": 1000,
"only_when_mount_type": "managed-volume"
}
],
"startup_healthcheck": {
"scheme": "https",
"port": 5800,
"path": "/",
"timeout_seconds": 240,
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": false
},
"generated_sensitive_environment": {
"WEB_AUTHENTICATION_PASSWORD": {
"strategy": "token-hex",
"bytes": 16,
"prompt": false
}
},
"volume_owner": {
"uid": 1000,
"gid": 1000
}
}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"stop_signal",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
},
"validation": {
"schema": "passed-at-generation",
"clean_install": "passed-first-start",
"service_health": "passed-login-page-and-authenticated-session-http-200",
"restart_persistence": "passed-container-restart-and-host-bind-write-check",
"backup_restore": "pending",
"update_preserves_data": "pending",
"image_resolution": "passed-skopeo-327mb-11-layers-integrity",
"credentials": "passed-generated-password-login-and-session-cookie",
"advanced_install": "pending-custom-install-summary-validation",
"managed_volume": "passed-managed-config-volume",
"host_bind": "passed-storage-bind-owner-and-app-user-write",
"browser_visual": "pending-self-signed-certificate-review",
"modification": "pending",
"removal": "passed-manager-remove-ct-disks-record-and-empty-test-path-cleaned",
"web_authentication": "passed-generated-credential-post-and-session-cookie"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": "amd64",
"resolved_digest": "sha256:ecb77bb73b1d3c66720eefb07805f70289349b449d1ddaa15d120c399bd89cc4",
"image_version_label": "26.09.1",
"image_created": "2026-09-22T15:22:56.230858151Z"
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false
}
}
+477
View File
@@ -0,0 +1,477 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-spoolman",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Spoolman"
},
"tagline": {
"en_US": "Track 3D printer filament spools and usage."
},
"description": {
"en_US": "A self-hosted filament inventory for 3D printers. Manage spools, materials, manufacturers, locations and labels in the web UI, and integrate with Moonraker, OctoPrint and other printer tools. The official image uses SQLite by default; no separate database container is required."
},
"category": "tools",
"category_label": "Tools",
"author": "Donkie",
"developer": "Donkie",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/spoolman.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": 8000,
"path": "/"
},
"website": "https://github.com/Donkie/Spoolman",
"documentation": "https://github.com/Donkie/Spoolman/wiki/Installation",
"repository": "https://github.com/Donkie/Spoolman",
"tips": [
"Spoolman has no built-in authentication. Keep it on a trusted network; do not expose it directly to the internet.",
"The official image uses SQLite by default and stores the database, automatic backups, logs and cache in the persistent data directory."
],
"mini_changelog": [],
"display_version": null,
"updated_at": null
},
"source": {
"provider": "Donkie",
"repository": "https://github.com/Donkie/Spoolman",
"revision": "ff8efd517b76a4b2783f29c89e9b9fbcd8cd167b",
"image_repository_url": "https://github.com/Donkie/Spoolman/pkgs/container/spoolman",
"readme_pushed_at": "2026-10-31T09:39:19Z",
"compose_sha256": "d9b115c4c37589f9813e54f926fdd22e7a10a44a6e56160f837b1247638fac83",
"generated_at": "2026-11-02T20:02:46+00:00",
"default_branch": "master",
"readme_raw_url": "https://github.com/Donkie/Spoolman/wiki/Installation"
},
"container_contract": {
"service_name": "spoolman",
"container_name": "spoolman",
"image": {
"reference": "ghcr.io/donkie/spoolman:latest",
"registry": "ghcr.io",
"repository": "ghcr.io/donkie/spoolman",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
},
"environment": [
{
"name": "PUID",
"example": "1000",
"required": true,
"sensitive": false,
"source": "upstream-image-default",
"prompt_user": false
},
{
"name": "PGID",
"example": "1000",
"required": true,
"sensitive": false,
"source": "upstream-image-default",
"prompt_user": false
},
{
"name": "TZ",
"example": "Etc/UTC",
"required": true,
"sensitive": false,
"source": "upstream-compose",
"prompt_user": false
}
],
"volumes": [
{
"id": "volume-0",
"container_path": "/home/app/.local/share/spoolman",
"compose_source_example": "./data",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 4
}
}
],
"ports": [
{
"container_port": 8000,
"published_example": 7912,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "services:\n spoolman:\n image: ghcr.io/donkie/spoolman:latest\n container_name: spoolman\n restart: unless-stopped\n ports:\n - \"7912:8000/tcp\"\n volumes:\n - ./data:/home/app/.local/share/spoolman\n environment:\n PUID: \"1000\"\n PGID: \"1000\"\n TZ: Etc/UTC\n"
},
"compose_stack": {
"project_name": "spoolman",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "spoolman",
"service_count": 1,
"services": [
{
"name": "spoolman",
"image": "ghcr.io/donkie/spoolman:latest",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [],
"frontend_network": true,
"private_network": false,
"compose": {
"image": "ghcr.io/donkie/spoolman:latest",
"container_name": "spoolman",
"restart": "unless-stopped",
"ports": [
"7912:8000/tcp"
],
"volumes": [
"./data:/home/app/.local/share/spoolman"
],
"environment": {
"PUID": "1000",
"PGID": "1000",
"TZ": "Etc/UTC"
}
}
}
],
"top_level": {
"name": "spoolman"
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": false,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "spoolman-volume-0",
"service": "spoolman",
"container_path": "/home/app/.local/share/spoolman",
"mode": "managed-volume",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
"spoolman"
],
"stop_order": [
"spoolman"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": []
}
},
"first_run": {
"endpoints": [
{
"label": "Spoolman web interface",
"scheme": "http",
"port": 8000,
"path": "/",
"source": "upstream-image"
}
],
"credentials": []
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 1024,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": false,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"adaptations": [
{
"id": "imported-compose-source",
"upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
"native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
"reason": "Catalog import must not imply runtime compatibility.",
"behavioral_impact": "No automatic installation before review.",
"validation": "pending-per-application"
},
{
"id": "rolling-latest-image",
"upstream_behavior": "A discovered Compose may pin a release tag or digest.",
"native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
"reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
"behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
"validation": "pending-per-application"
},
{
"id": "dedicated-lxc-network",
"upstream_behavior": "Docker publishes selected container ports on the Docker host.",
"native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
"reason": "A native LXC has its own address and does not require Docker port NAT.",
"behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
"validation": "pending-per-application"
},
{
"id": "compose-shm-size",
"upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
"native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
"reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
"behavioral_impact": "None expected.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-command",
"upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
"native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
"reason": "Proxmox stores the effective OCI process as one entrypoint string.",
"behavioral_impact": "None expected.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-privileged-mode",
"upstream_behavior": "Compose selects whether the container runs in privileged mode.",
"native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
"reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
"behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
"validation": "native-equivalent"
},
{
"id": "compose-process-runtime",
"upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
"native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
"reason": "The OCI process must start with the same identity, command and working directory without Docker.",
"behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-healthcheck",
"upstream_behavior": "Docker periodically executes the declared container healthcheck.",
"native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
"reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
"behavioral_impact": "The check runs during installation rather than continuously after installation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-cpu-priority",
"upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
"native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
"reason": "Both settings express relative CPU priority on different scales.",
"behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-resource-limits",
"upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
"native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
"reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
"behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-network-identity",
"upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
"native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
"reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
"behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-network-mode",
"upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
"native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
"reason": "The LXC is the application host and already has its own address and port namespace.",
"behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-capabilities-and-sysctls",
"upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
"native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
"reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
"behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
"validation": "not-requested-by-compose"
},
{
"id": "docker-engine-metadata",
"upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
"native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
"reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
"behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-device-passthrough",
"upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
"native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
"reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
"behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
"validation": "not-requested-by-compose"
},
{
"id": "compose-host-ipc",
"upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
"native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
"reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
"behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
"validation": "not-requested-by-compose"
}
],
"installer_profile": {
"startup_healthcheck": {
"scheme": "http",
"port": 8000,
"path": "/",
"timeout_seconds": 180,
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": true
},
"volume_preparations": [
{
"container_path": "/home/app/.local/share/spoolman",
"remove_lost_found": true,
"owner_strategy": "mapped-application-user",
"only_when_mount_type": "managed-volume"
}
],
"volume_owner": {
"uid": 1000,
"gid": 1000
}
}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"cap_add",
"command",
"container_name",
"cpu_shares",
"deploy",
"devices",
"entrypoint",
"environment",
"extra_hosts",
"group_add",
"healthcheck",
"hostname",
"image",
"init",
"ipc",
"labels",
"logging",
"mac_address",
"mem_limit",
"network_mode",
"networks",
"ports",
"privileged",
"restart",
"runtime",
"security_opt",
"shm_size",
"stdin_open",
"stop_grace_period",
"sysctls",
"tty",
"ulimits",
"user",
"volumes",
"working_dir"
],
"untranslated_blockers": [],
"policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
},
"validation": {
"schema": "passed-at-generation",
"clean_install": "passed-default-official-image-skopeo-and-first-start-http",
"advanced_install": "pending-custom-install-summary-validation",
"service_health": "passed-web-api-crud",
"restart_persistence": "passed-managed-and-host-bind-database-and-records",
"backup_restore": "passed-pbs-backup-and-manager-recover-record-preserved",
"update_preserves_data": "no-update-available-at-check",
"modification": "passed-cpu-ram-up-and-down-without-image-pull-or-recreate",
"volumes": "passed-managed-storage-and-host-bind-crud-owner-and-data-check",
"removal": "passed-manager-removal-host-path-retained-then-temporary-data-cleaned"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": null,
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false
}
}
+292
View File
@@ -0,0 +1,292 @@
{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-unbound",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Unbound"
},
"tagline": {
"en_US": "A validating, recursive DNS resolver for your private network."
},
"description": {
"en_US": "Run the community-maintained klutchell image of Unbound as a DNS resolver on its own Proxmox LXC. It listens on TCP and UDP port 53 and can load optional custom configuration files."
},
"category": "adblock",
"category_label": "Adblock & DNS",
"author": "Kyle Harding",
"developer": "Kyle Harding (klutchell)",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/unbound.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64",
"armhf"
],
"launch": {
"scheme": "http",
"port": null,
"path": "/"
},
"website": "https://unbound.net/",
"documentation": "https://github.com/klutchell/unbound-docker#readme",
"repository": "https://github.com/klutchell/unbound-docker",
"tips": [
"Unbound has no web interface. Use the LXC IP as a DNS server on TCP and UDP port 53.",
"The default image config listens on all interfaces and accepts clients from private IPv4 ranges. Keep it behind your trusted LAN firewall; do not expose recursive DNS publicly.",
"The original GitHub repository has moved to klutchell/unbound-docker; the published Docker Hub image remains klutchell/unbound:main. The image is an unofficial community image, not published by NLnet Labs.",
"Optional custom .conf files go under /etc/unbound/custom.conf.d and must be readable by UID/GID 101:102 or world-readable."
],
"mini_changelog": [],
"display_version": null,
"updated_at": null
},
"source": {
"provider": "klutchell",
"repository": "https://github.com/klutchell/unbound-docker",
"revision": "1efbea960bf9462b6fb6dd71aa702ad32d6820d7",
"default_branch": "main",
"image_repository_url": "https://hub.docker.com/r/klutchell/unbound",
"readme_pushed_at": "2026-11-02T14:31:22Z",
"compose_sha256": "faf73a93c1f7d4d43f1eb61ba2e62a4418bd501f0c827b080ea9897964e75848",
"generated_at": "2026-11-02T21:21:54+00:00",
"readme_raw_url": "https://raw.githubusercontent.com/klutchell/unbound-docker/main/README.md"
},
"container_contract": {
"service_name": "unbound",
"container_name": "unbound",
"image": {
"reference": "klutchell/unbound:main",
"registry": "docker.io",
"repository": "klutchell/unbound",
"tag": "main",
"digest": null,
"pull_policy": "resolve-selected-tag-to-architecture-digest-at-install"
},
"environment": [],
"volumes": [
{
"id": "volume-0",
"container_path": "/etc/unbound/custom.conf.d",
"compose_source_example": "./config",
"read_only": false,
"required": false,
"installation_choice": [
"managed-volume",
"host-bind"
],
"default": "host-bind",
"managed_volume": {
"backup": true,
"default_size_gb": 1
}
}
],
"ports": [
{
"container_port": 53,
"published_example": 53,
"protocol": "udp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
},
{
"container_port": 53,
"published_example": 53,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "name: unbound\nservices:\n unbound:\n image: klutchell/unbound:main\n container_name: unbound\n restart: unless-stopped\n ports:\n - 53:53/udp\n - 53:53/tcp\n volumes:\n - ./config:/etc/unbound/custom.conf.d\n"
},
"compose_stack": {
"project_name": "unbound",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "unbound",
"service_count": 1,
"services": [
{
"name": "unbound",
"image": "klutchell/unbound:main",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [],
"frontend_network": true,
"private_network": false,
"compose": {
"image": "klutchell/unbound:main",
"container_name": "unbound",
"restart": "unless-stopped",
"ports": [
"53:53/udp",
"53:53/tcp"
],
"volumes": [
"./config:/etc/unbound/custom.conf.d"
]
}
}
],
"top_level": {
"name": "unbound"
},
"networking": {
"frontend": "selected-proxmox-bridge",
"private_required": false,
"private_creation": "automatic-create-if-missing",
"private_address_allocation": "automatic-static-address-per-service",
"service_discovery": "private-addresses-with-compose-service-host-aliases",
"dependency_external_access": "disabled-unless-service-publishes-ports",
"prompt_user_for_private_network": false
},
"storage": [
{
"id": "unbound-volume-0",
"service": "unbound",
"container_path": "/etc/unbound/custom.conf.d",
"mode": "user-selectable",
"user_selectable": true,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": "Host directory containing optional custom Unbound .conf files",
"default": "host-bind",
"installation_choice": [
"managed-volume",
"host-bind"
]
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
"unbound"
],
"stop_order": [
"unbound"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
]
}
},
"first_run": {
"endpoints": [],
"credentials": []
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 512,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": false,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 120
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"adaptations": [],
"installer_profile": {
"volume_preparations": [
{
"container_path": "/etc/unbound/custom.conf.d",
"owner_strategy": "mapped-root",
"remove_lost_found": true,
"only_when_mount_type": "managed-volume"
}
],
"startup_healthcheck": {
"type": "running",
"timeout_seconds": 90,
"stability_seconds": 20
}
},
"catalog": {
"replaces_discovered_ids": []
}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"container_name",
"image",
"ports",
"restart",
"volumes"
],
"untranslated_blockers": [],
"policy": "This single-image Compose contract maps to one unprivileged Proxmox LXC. The optional custom-config directory can use a Proxmox-managed volume or a host directory; the image defaults work with an empty directory."
},
"validation": {
"schema": "passed-at-generation",
"image_resolution": "passed-skopeo-16-layers",
"clean_install": "passed-default-install-and-first-start-healthcheck",
"advanced_install": "pending",
"service_health": "passed-recursion-and-custom-local-data-over-udp-and-tcp",
"restart_persistence": "passed-custom-zone-after-pct-reboot",
"backup_restore": "pending",
"update_preserves_data": "pending-update-check-not-run",
"modification": "pending-manager-ui-unresponsive-no-changes-applied",
"managed_volume": "passed-config-volume-1g",
"host_bind": "pending",
"removal": "passed-manager-remove-ct-disks-and-record-cleaned"
},
"lifecycle": {
"update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": null,
"image_created": null
},
"change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
"automatic_unattended_updates": false
}
}
+302
View File
@@ -0,0 +1,302 @@
{
"schema_version": "0.3.0",
"kind": "proxmenux.oci-template",
"id": "image-zwave-js-ui",
"status": "generated-unvalidated",
"catalog_ui": {
"title": {
"en_US": "Z-Wave JS UI"
},
"tagline": {
"en_US": "Manage a Z-Wave network from a web dashboard and WebSocket gateway."
},
"description": {
"en_US": "A self-hosted Z-Wave control panel and MQTT gateway. Its web interface can start without a Z-Wave controller; device control requires a compatible USB or serial controller and network security keys."
},
"category": "smarthome",
"author": "Z-Wave JS",
"developer": "Z-Wave JS",
"icon": "https://raw.githubusercontent.com/zwave-js/zwave-js-ui/c38e0d2cbbb826bf2bab2db596a3452e59ce0e8e/public/apple-touch-icon-180x180.png",
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": 8091,
"path": "/"
},
"website": "https://zwave-js.github.io/zwave-js-ui/",
"documentation": "https://zwave-js.github.io/zwave-js-ui/#/getting-started/docker",
"repository": "https://github.com/zwave-js/zwave-js-ui",
"tips": [
"The web UI can start without a Z-Wave controller. To control devices, pass through a compatible USB/serial controller and select its stable /dev/serial/by-id/... path in Settings → Z-Wave → Serial Port.",
"Configure and securely back up the S0/S2 network keys in Settings → Z-Wave before pairing security-enabled devices.",
"Port 3000 is the Z-Wave JS WebSocket server for integrations such as Home Assistant; it listens only after a Z-Wave client is configured. Upstream documents it as unauthenticated, so expose it only to trusted integrations and networks.",
"ProxMenux enables the web login and generates the initial admin password. Configure trusted HTTPS before remote access over an untrusted network."
],
"mini_changelog": [],
"display_version": "11.25.2",
"updated_at": "2026-11-02",
"category_label": "IoT & Smart Home"
},
"source": {
"provider": "Z-Wave JS",
"repository": "https://github.com/zwave-js/zwave-js-ui",
"default_branch": "master",
"revision": "c38e0d2cbbb826bf2bab2db596a3452e59ce0e8e",
"readme_raw_url": "https://raw.githubusercontent.com/zwave-js/zwave-js-ui/c38e0d2cbbb826bf2bab2db596a3452e59ce0e8e/README.md",
"image_repository_url": "https://hub.docker.com/r/zwavejs/zwave-js-ui",
"compose_sha256": "ed371d9e9805db5d2abd356b54e3ff5fdb677d637857313f6aac3f498f480f87",
"generated_at": "2026-11-02T20:10:33+00:00"
},
"container_contract": {
"service_name": "zwave-js-ui",
"container_name": "zwave-js-ui",
"image": {
"reference": "docker.io/zwavejs/zwave-js-ui:latest",
"registry": "docker.io",
"repository": "zwavejs/zwave-js-ui",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-selected-tag-to-architecture-digest-at-install"
},
"environment": [
{
"name": "SESSION_SECRET",
"example": "${GENERATED_SESSION_SECRET}",
"required": true,
"sensitive": true,
"source": "ProxMenux-generated; supported by upstream image"
},
{
"name": "DEFAULT_PASSWORD",
"example": "${GENERATED_ADMIN_PASSWORD}",
"prompt": "Initial Z-Wave JS UI admin password",
"required": true,
"sensitive": true,
"source": "Upstream image; generated by ProxMenux"
}
],
"volumes": [
{
"id": "zwave-config",
"container_path": "/usr/src/app/store",
"compose_source_example": "zwave-config",
"read_only": false,
"required": true,
"installation_choice": [
"managed-volume"
],
"default": "managed-volume",
"managed_volume": {
"backup": true,
"default_size_gb": 4
}
}
],
"ports": [
{
"container_port": 8091,
"published_example": 8091,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
},
{
"container_port": 3000,
"published_example": 3000,
"protocol": "tcp",
"required": false,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "always",
"stop_grace_period": null,
"original_compose": "version: '3.7'\nservices:\n zwave-js-ui:\n container_name: zwave-js-ui\n image: zwavejs/zwave-js-ui:latest\n restart: always\n tty: true\n stop_signal: SIGINT\n environment:\n SESSION_SECRET: ${GENERATED_SESSION_SECRET}\n DEFAULT_PASSWORD: ${GENERATED_ADMIN_PASSWORD}\n networks:\n - zwave\n devices:\n - /dev/serial/by-id/insert_stick_reference_here:/dev/zwave\n volumes:\n - zwave-config:/usr/src/app/store\n ports:\n - '8091:8091'\n - '3000:3000'\nnetworks:\n zwave:\nvolumes:\n zwave-config:\n name: zwave-config\n"
},
"first_run": {
"endpoints": [
{
"label": "Z-Wave JS UI",
"scheme": "http",
"port": 8091,
"path": "/",
"source": "official-docker-documentation"
}
],
"credentials": [
{
"label": "Z-Wave JS UI admin login",
"type": "configured-or-installer-generated",
"username": "admin",
"password": null,
"password_environment": "DEFAULT_PASSWORD",
"change_required": false,
"source": "Upstream DEFAULT_PASSWORD, generated by ProxMenux",
"retrieval": null
}
]
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"defaults": {
"unprivileged": true,
"ostype": "auto-from-image",
"cores": 2,
"memory_mb": 1024,
"swap_mb": 512,
"rootfs_size_gb": 8,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": true,
"features": [
"nesting=1"
],
"shutdown_timeout_seconds": 60
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image",
"cmd": "import-from-oci-image",
"environment": "import-image-env-then-apply-compose-overrides",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "apply-compose-override"
},
"adaptations": [
{
"id": "dedicated-lxc-network",
"upstream_behavior": "Docker publishes ports on a Docker host address and attaches the service to its Compose network.",
"native_lxc_behavior": "The OCI process listens on the dedicated LXC address; ProxMenux maps the single-service network to the LXC network namespace.",
"reason": "A one-service native OCI LXC has its own network namespace and address.",
"behavioral_impact": "Use the LXC address directly; no Docker port NAT or separate bridge is created.",
"validation": "pending-per-application"
},
{
"id": "generated-login-and-session-secret",
"upstream_behavior": "The image defaults to user admin and password zwave; web authentication is disabled by default, and the Compose example uses a sample session secret.",
"native_lxc_behavior": "ProxMenux generates a unique initial admin password and session secret, and seeds gateway.authEnabled=true before first boot.",
"reason": "Avoid shipping a shared password, sample session key or unauthenticated web UI.",
"behavioral_impact": "The installer must display the generated admin password; change it after first login.",
"validation": "pending-per-application"
},
{
"id": "stop-signal",
"upstream_behavior": "Compose requests SIGINT when stopping the container.",
"native_lxc_behavior": "ProxMenux uses the same signal for the OCI process shutdown lifecycle.",
"reason": "Preserve the upstream graceful-shutdown behavior.",
"behavioral_impact": "None expected.",
"validation": "pending-per-application"
}
],
"installer_profile": {
"generated_sensitive_environment": {
"SESSION_SECRET": {
"strategy": "token-hex",
"bytes": 32,
"prompt": false
},
"DEFAULT_PASSWORD": {
"strategy": "token-hex",
"bytes": 20,
"prompt": true
}
},
"generated_files": [
{
"container_path": "/usr/src/app/store/settings.json",
"mode": "0600",
"owner": "mapped-root",
"content": "{\"gateway\":{\"authEnabled\":true}}\n",
"only_if_missing": true
}
],
"volume_preparations": [
{
"container_path": "/usr/src/app/store",
"remove_lost_found": true,
"owner_strategy": "mapped-root",
"only_when_mount_type": "managed-volume"
}
],
"optional_devices": [
{
"id": "zwave-serial-controller",
"kind": "character-device",
"purpose": "serial",
"enable_prompt": "Pass through a Z-Wave USB or serial controller",
"enabled_default": false,
"required_by_compose": false,
"path_prompt": "Controller path on the Proxmox host; prefer /dev/serial/by-id/...",
"host_path_default": "/dev/serial/by-id/insert_stick_reference_here",
"container_path_strategy": "same-as-host",
"mode": "preserve-host",
"deny_write": false,
"gid_strategy": "host-device-gid"
}
],
"startup_healthcheck": {
"scheme": "http",
"port": 8091,
"path": "/",
"timeout_seconds": 180,
"request_timeout_seconds": 10,
"stability_seconds": 4,
"verify_tls": false
}
}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": false,
"supported_compose_keys": [
"container_name",
"devices",
"environment",
"image",
"networks",
"ports",
"restart",
"stop_signal",
"tty",
"volumes"
],
"untranslated_blockers": [],
"policy": "The single-image recipe has passed installation, UI/login, restart, backup and recovery, modification, and engine checks. Host-bind storage has not been checked. Radio control remains untested because no compatible Z-Wave coordinator was attached during the lab test."
},
"validation": {
"schema": "passed-static-validation",
"image_resolution": "passed-skopeo-download-and-layer-verification",
"clean_install": "passed-default-install-and-health-check",
"advanced_install": "pending-custom-install-summary-validation",
"web_response": "passed-ui-and-generated-login",
"credentials": "passed-generated-login-before-and-after-restore",
"restart_persistence": "passed-restart-with-store-preserved",
"backup_restore": "passed-proxmox-backup-and-manager-recover-new-vmid",
"update_preserves_data": "no-update-available-at-check",
"modification": "passed-cpu-ram-up-down-without-recreation",
"managed_volume": "passed-store-volume-and-recovery",
"host_bind": "pending",
"hardware_radio": "not-tested-no-z-wave-coordinator-on-host",
"removal": "passed-remove-original-and-restored-ct-and-local-backup-cleanup"
},
"lifecycle": {
"update_strategy": "replace-rootfs-from-new-oci-image-preserve-managed-volumes",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": "11.25.2",
"image_created": null
},
"change_detection": "compare-resolved-architecture-digest",
"automatic_unattended_updates": false
}
}
+211 -42
View File
@@ -2,7 +2,7 @@
"schema_version": "0.2.0",
"kind": "proxmenux.oci-catalog-index",
"provider": "multiple-container-images",
"generated_at": "2026-10-30T18:13:36+00:00",
"generated_at": "2026-11-02T21:25:05+00:00",
"applications": [
{
"id": "2fauth",
@@ -77,7 +77,7 @@
"template_family": "curated-profile",
"title": "Activepieces",
"repository": "https://github.com/activepieces/activepieces",
"description": "Build and run automation flows from a self-hosted visual interface. This single-container profile uses Activepieces\u2019 documented personal mode with embedded storage and an in-memory queue.",
"description": "Build and run automation flows from a self-hosted visual interface. This single-container profile uses Activepieces’ documented personal mode with embedded storage and an in-memory queue.",
"website": "https://www.activepieces.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/activepieces.webp",
"architectures": [
@@ -280,9 +280,9 @@
"provider": "getalby",
"template_family": "imported-compose",
"variant": null,
"title": "Alby Hub \u2728",
"title": "Alby Hub ✨",
"repository": "https://ghcr.io/getalby/hub",
"description": "Alby Hub is an open-source, self-custodial Bitcoin Lightning wallet, with the easiest-to-use Lightning Network node for everyone.\nWhether you're an individual, creator, or developer, Alby Hub is your centre for seamless Bitcoin payments.\nEffortlessly connect to a variety of apps like the Alby Browser Extension or Alby Go mobile app, create sub-wallets for family and friends, and take full control of your funds\u2014all within an intuitive interface and developer-ready APIs.\n\n**USEFUL LINKS**\n- [Source Repository](https://github.com/getAlby/hub)\n- [Support](https://support.getalby.com/)\n- [Marketing Site](https://albyhub.com/)\n- [Community of users and developers](https://discord.getalby.com)\n- [Feedback Board, feature requests, bug reports[(https://feedback.getalby.com)\n",
"description": "Alby Hub is an open-source, self-custodial Bitcoin Lightning wallet, with the easiest-to-use Lightning Network node for everyone.\nWhether you're an individual, creator, or developer, Alby Hub is your centre for seamless Bitcoin payments.\nEffortlessly connect to a variety of apps like the Alby Browser Extension or Alby Go mobile app, create sub-wallets for family and friends, and take full control of your funds—all within an intuitive interface and developer-ready APIs.\n\n**USEFUL LINKS**\n- [Source Repository](https://github.com/getAlby/hub)\n- [Support](https://support.getalby.com/)\n- [Marketing Site](https://albyhub.com/)\n- [Community of users and developers](https://discord.getalby.com)\n- [Feedback Board, feature requests, bug reports[(https://feedback.getalby.com)\n",
"website": "https://albyhub.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/alby-hub.webp",
"architectures": [
@@ -1056,7 +1056,7 @@
"variant": null,
"title": "BentoPDF",
"repository": "https://ghcr.io/alam00000/bentopdf-simple",
"description": "**Your PDFs never leave your device.** BentoPDF is a privacy-first PDF toolkit that runs entirely in your browser. Every merge, split, conversion, and edit happens locally on your machine \u2014 no file is ever uploaded to a server. More than 50 tools are bundled into one clean, fast, ad-free interface.\n\n**One app for every PDF job.** Organize and edit your documents (merge, split, reorder, rotate, crop, watermark, page numbers, redaction, annotations and forms), convert to and from PDF (images, Word, Excel, PowerPoint, EPUB, Markdown and more), run OCR, and secure your files with compression, encryption, digital signatures, and metadata cleanup.\n\n**Made for your private cloud.** Self-hosting BentoPDF on a private cloud device like self-hosted server gives your whole household or team a single, ad-free PDF workshop on hardware you control \u2014 fast over the local network, with your documents staying private by design.\n",
"description": "**Your PDFs never leave your device.** BentoPDF is a privacy-first PDF toolkit that runs entirely in your browser. Every merge, split, conversion, and edit happens locally on your machine — no file is ever uploaded to a server. More than 50 tools are bundled into one clean, fast, ad-free interface.\n\n**One app for every PDF job.** Organize and edit your documents (merge, split, reorder, rotate, crop, watermark, page numbers, redaction, annotations and forms), convert to and from PDF (images, Word, Excel, PowerPoint, EPUB, Markdown and more), run OCR, and secure your files with compression, encryption, digital signatures, and metadata cleanup.\n\n**Made for your private cloud.** Self-hosting BentoPDF on a private cloud device like self-hosted server gives your whole household or team a single, ad-free PDF workshop on hardware you control — fast over the local network, with your documents staying private by design.\n",
"website": "https://bentopdf.com",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/bentopdf.webp",
"architectures": [
@@ -2067,7 +2067,7 @@
"variant": null,
"title": "Cloudflared",
"repository": "https://hub.docker.com/r/wisdomsky/cloudflared-web",
"description": "Cloudflare Tunnel offers an easy way to expose web servers securely to the internet, without opening up firewall ports and configuring ACLs. Cloudflare Tunnel also ensures requests route through Cloudflare before reaching the web server, so you can be sure attack traffic is stopped with Cloudflare\u2019s WAF and Unmetered DDoS mitigation, and authenticated with Access if you\u2019ve enabled those features for your account.\n\nThe software provides a seamless way to securely expose web servers to the internet without configuring firewall ports or access control lists (ACLs). All requests are routed through Cloudflare before reaching your web server, leveraging Cloudflare\u2019s Web Application Firewall (WAF) and unmetered DDoS mitigation to block attack traffic, with optional authentication via Cloudflare Access if enabled. With its intuitive Web interface and efficient tunnel management, this tool is the perfect solution for securely deploying web services.\n\n**Discover How to Connect self-hosted server to Cloudflare Tunnel**\nIntegrating self-hosted server with Cloudflare Tunnel allows you to securely expose local services to the internet without opening firewall ports, enabling seamless remote access. Below are two practical resources to guide you through the setup process:\n1. [**Cloudflare Official Tutorial**](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/): \n This tutorial provides detailed steps for creating and managing a Cloudflare Tunnel.\n2. [**Phiptech Practical Guide**](https://phiptech.com/how-to-setup-cloudflare-tunnel-and-expose-your-local-service-or-application/): \n This guide offers a concise, step-by-step walkthrough for setting up Cloudflare Tunnel on local devices like self-hosted server, with practical examples to help users easily expose services to the public internet.\n",
"description": "Cloudflare Tunnel offers an easy way to expose web servers securely to the internet, without opening up firewall ports and configuring ACLs. Cloudflare Tunnel also ensures requests route through Cloudflare before reaching the web server, so you can be sure attack traffic is stopped with Cloudflare’s WAF and Unmetered DDoS mitigation, and authenticated with Access if you’ve enabled those features for your account.\n\nThe software provides a seamless way to securely expose web servers to the internet without configuring firewall ports or access control lists (ACLs). All requests are routed through Cloudflare before reaching your web server, leveraging Cloudflare’s Web Application Firewall (WAF) and unmetered DDoS mitigation to block attack traffic, with optional authentication via Cloudflare Access if enabled. With its intuitive Web interface and efficient tunnel management, this tool is the perfect solution for securely deploying web services.\n\n**Discover How to Connect self-hosted server to Cloudflare Tunnel**\nIntegrating self-hosted server with Cloudflare Tunnel allows you to securely expose local services to the internet without opening firewall ports, enabling seamless remote access. Below are two practical resources to guide you through the setup process:\n1. [**Cloudflare Official Tutorial**](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/): \n This tutorial provides detailed steps for creating and managing a Cloudflare Tunnel.\n2. [**Phiptech Practical Guide**](https://phiptech.com/how-to-setup-cloudflare-tunnel-and-expose-your-local-service-or-application/): \n This guide offers a concise, step-by-step walkthrough for setting up Cloudflare Tunnel on local devices like self-hosted server, with practical examples to help users easily expose services to the public internet.\n",
"website": "https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel/",
"icon": "https://cdn.jsdelivr.net/gh/IceWhaleTech/CasaOS-AppStore@main/Apps/Cloudflared/icon.png",
"architectures": [
@@ -2339,7 +2339,7 @@
"title": "Cops",
"repository_name": "docker-cops",
"repository": "https://github.com/linuxserver/docker-cops",
"description": "Cops by S\u00e9bastien Lucas, now maintained by MikesPub, stands for Calibre OPDS (and HTML) Php Server.",
"description": "Cops by Sébastien Lucas, now maintained by MikesPub, stands for Calibre OPDS (and HTML) Php Server.",
"website": "https://github.com/mikespub-org/seblucas-cops",
"icon": "https://cdn.jsdelivr.net/gh/linuxserver/docker-templates@master/linuxserver.io/img/cops-icon.png",
"architectures": [
@@ -2941,7 +2941,7 @@
"template_family": "curated-profile",
"title": "Dgraph",
"repository": "https://github.com/dgraph-io/dgraph",
"description": "Run Dgraph\u2019s single-container standalone distribution, which starts its required local Dgraph components together.",
"description": "Run Dgraph’s single-container standalone distribution, which starts its required local Dgraph components together.",
"website": "https://github.com/dgraph-io/dgraph",
"icon": "https://cdn.jsdelivr.net/npm/simple-icons@15.2.0/icons/dgraph.svg",
"architectures": [
@@ -3184,7 +3184,7 @@
"variant": null,
"title": "Docmost",
"repository": "https://hub.docker.com/r/docmost/docmost",
"description": "Docmost is a self-hosted collaborative wiki and documentation tool designed for real-time collaboration, allowing multiple users to edit the same page simultaneously without conflicts. Its intuitive interface is ideal for teams managing knowledge bases, project documentation, or wikis, offering an efficient knowledge creation and sharing experience.\n\nThe tool's core features include real-time collaborative editing and space organization. It supports multiple users editing pages in real time for seamless collaboration and organizes pages into 'spaces' for teams, projects, or departments, each with independent permission settings. A rich text editor with Markdown shortcuts simplifies content creation. Built-in Draw.io, Excalidraw, and Mermaid tools provide robust diagramming capabilities.\n\nIt offers permissions management, assigning access via user groups for content security. Pages can be publicly shared via links for external access. Comments enhance communication and feedback, while page history tracks changes. Features like nested navigation, quick search, file attachments, and Markdown/HTML import/export are supported. The tool\u2019s collaboration and flexibility deliver a modern documentation solution.\n\n**Key Features:**\n- Real-time collaborative editing for multiple users\n- Spaces for organizing pages by team, project or department\n- Permissions management with user group access control\n- Rich text editor with Markdown shortcuts\n- Built-in Draw.io, Excalidraw, Mermaid diagramming tools\n- Public page sharing via links\n- Page comments for communication and feedback\n- Page history, nested navigation, search, and file attachments\n\n**Learn More:**\n- [Docmost Official Website](https://docmost.com/)\n- [Docmost GitHub](https://github.com/docmost/docmost)\n",
"description": "Docmost is a self-hosted collaborative wiki and documentation tool designed for real-time collaboration, allowing multiple users to edit the same page simultaneously without conflicts. Its intuitive interface is ideal for teams managing knowledge bases, project documentation, or wikis, offering an efficient knowledge creation and sharing experience.\n\nThe tool's core features include real-time collaborative editing and space organization. It supports multiple users editing pages in real time for seamless collaboration and organizes pages into 'spaces' for teams, projects, or departments, each with independent permission settings. A rich text editor with Markdown shortcuts simplifies content creation. Built-in Draw.io, Excalidraw, and Mermaid tools provide robust diagramming capabilities.\n\nIt offers permissions management, assigning access via user groups for content security. Pages can be publicly shared via links for external access. Comments enhance communication and feedback, while page history tracks changes. Features like nested navigation, quick search, file attachments, and Markdown/HTML import/export are supported. The tool’s collaboration and flexibility deliver a modern documentation solution.\n\n**Key Features:**\n- Real-time collaborative editing for multiple users\n- Spaces for organizing pages by team, project or department\n- Permissions management with user group access control\n- Rich text editor with Markdown shortcuts\n- Built-in Draw.io, Excalidraw, Mermaid diagramming tools\n- Public page sharing via links\n- Page comments for communication and feedback\n- Page history, nested navigation, search, and file attachments\n\n**Learn More:**\n- [Docmost Official Website](https://docmost.com/)\n- [Docmost GitHub](https://github.com/docmost/docmost)\n",
"website": "https://docmost.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/docmost.webp",
"architectures": [
@@ -4242,7 +4242,7 @@
"template_family": "curated-profile",
"title": "FalkorDB",
"repository": "https://github.com/FalkorDB/FalkorDB",
"description": "Explore and query property graphs using FalkorDB\u2019s Redis-compatible server and bundled browser UI in one image.",
"description": "Explore and query property graphs using FalkorDB’s Redis-compatible server and bundled browser UI in one image.",
"website": "https://github.com/FalkorDB/FalkorDB",
"icon": "https://github.com/FalkorDB.png",
"architectures": [
@@ -4470,7 +4470,7 @@
"template_family": "curated-profile",
"title": "FileFlows",
"repository": "https://hub.docker.com/r/revenz/fileflows",
"description": "Save storage space with efficient file processing.\n\nFileFlows lets you monitor and process any file type with custom flows. Videos, audio, images, archives, comics, eBooks\u2014you name it!\n",
"description": "Save storage space with efficient file processing.\n\nFileFlows lets you monitor and process any file type with custom flows. Videos, audio, images, archives, comics, eBooks—you name it!\n",
"website": "https://fileflows.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/fileflows.webp",
"architectures": [
@@ -4602,7 +4602,7 @@
"variant": null,
"title": "Firefly",
"repository": "https://hub.docker.com/r/uusec/firefly",
"description": "Firefly is a simple and easy to install WireGuard server software, which can be widely used in scenarios such as remote networking, remote work, and expose a local server behind a NAT or firewall to the internet. \ud83c\udfaf Features \ud83d\udfe2 Provide a simple and easy-to-use web management UI \ud83d\udfe3 Supports access to all WireGuard clients \ud83d\udfe1 No need for system installation of WireGuard components \ud83d\udfe0 Single file, no additional library dependencies \ud83d\udd34 Automatically apply for free SSL certificate",
"description": "Firefly is a simple and easy to install WireGuard server software, which can be widely used in scenarios such as remote networking, remote work, and expose a local server behind a NAT or firewall to the internet. 🎯 Features 🟢 Provide a simple and easy-to-use web management UI 🟣 Supports access to all WireGuard clients 🟡 No need for system installation of WireGuard components 🟠 Single file, no additional library dependencies 🔴 Automatically apply for free SSL certificate",
"website": "https://qq.uusec.com/",
"icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@main/webp/firefly.webp",
"architectures": [
@@ -6547,7 +6547,7 @@
"template_family": "curated-profile",
"title": "Huginn",
"repository": "https://github.com/huginn/huginn",
"description": "A self-hosted automation platform whose agents can watch websites and services, transform events, and trigger actions. Uses Huginn\u2019s all-in-one image.",
"description": "A self-hosted automation platform whose agents can watch websites and services, transform events, and trigger actions. Uses Huginn’s all-in-one image.",
"website": "https://github.com/huginn/huginn",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/huginn.webp",
"architectures": [
@@ -6748,7 +6748,7 @@
"variant": null,
"title": "Index-TTS",
"repository": "https://hub.docker.com/r/icewhaletech/index-tts",
"description": "IndexTTS2 is an advanced zero-shot text-to-speech model that innovatively achieves complete decoupling of emotional expression and speaker identity. The model supports precise speech duration control and multimodal emotion control, capable of maintaining the target timbre while accurately reproducing the specified emotional intonation.\n\nThe model employs a three-stage training paradigm and introduces GPT latent representations, ensuring excellent speech clarity and stability even under high emotional expression. Through the Qwen-based soft instruction mechanism, users can easily control the emotional characteristics of generated speech using natural language descriptions.\n\nIn multi-dataset evaluations, IndexTTS2 surpasses existing zero-shot TTS models in key metrics such as word error rate, speaker similarity, and emotion fidelity, providing industry-leading speech synthesis quality.\n\n**Key Features:**\n- Zero-shot TTS capability to replicate any timbre without training\n- Independent control of emotion and timbre with multimodal emotion input\n- Precise duration control with explicit token count specification for perfect audio-video synchronization\n- Natural language-based emotion control to guide speech generation through text descriptions\n\n**Additional Notes:**\n- Please ensure available memory > 12 GB, otherwise the application may not run properly\n- This application runs on CPU by default. This mode has low computational efficiency and will cause extremely high resource consumption and potential system instability. For optimal performance and stability, it is strongly recommended to use NVIDIA GPU to run this application\n- If you need to use NVIDIA GPU, please select \"Custom Install\" and enable the GPU option (supported in self-hosted server 1.5.0 and above)\n- For NVIDIA GPU usage, \u2265 8 GB VRAM is required (recommended for optimal performance)\n- For NVIDIA GPU usage, NVIDIA CUDA Toolkit version \u2265 12.8 is required\n\n**Learn More:**\n- [IndexTTS GitHub](https://github.com/index-tts/index-tts)\n",
"description": "IndexTTS2 is an advanced zero-shot text-to-speech model that innovatively achieves complete decoupling of emotional expression and speaker identity. The model supports precise speech duration control and multimodal emotion control, capable of maintaining the target timbre while accurately reproducing the specified emotional intonation.\n\nThe model employs a three-stage training paradigm and introduces GPT latent representations, ensuring excellent speech clarity and stability even under high emotional expression. Through the Qwen-based soft instruction mechanism, users can easily control the emotional characteristics of generated speech using natural language descriptions.\n\nIn multi-dataset evaluations, IndexTTS2 surpasses existing zero-shot TTS models in key metrics such as word error rate, speaker similarity, and emotion fidelity, providing industry-leading speech synthesis quality.\n\n**Key Features:**\n- Zero-shot TTS capability to replicate any timbre without training\n- Independent control of emotion and timbre with multimodal emotion input\n- Precise duration control with explicit token count specification for perfect audio-video synchronization\n- Natural language-based emotion control to guide speech generation through text descriptions\n\n**Additional Notes:**\n- Please ensure available memory > 12 GB, otherwise the application may not run properly\n- This application runs on CPU by default. This mode has low computational efficiency and will cause extremely high resource consumption and potential system instability. For optimal performance and stability, it is strongly recommended to use NVIDIA GPU to run this application\n- If you need to use NVIDIA GPU, please select \"Custom Install\" and enable the GPU option (supported in self-hosted server 1.5.0 and above)\n- For NVIDIA GPU usage, ≥ 8 GB VRAM is required (recommended for optimal performance)\n- For NVIDIA GPU usage, NVIDIA CUDA Toolkit version ≥ 12.8 is required\n\n**Learn More:**\n- [IndexTTS GitHub](https://github.com/index-tts/index-tts)\n",
"website": "",
"icon": "https://cdn.jsdelivr.net/gh/IceWhaleTech/CasaOS-AppStore@main/Apps/Index-TTS/icon.png",
"architectures": [
@@ -6908,7 +6908,7 @@
"title": "Intellij Idea",
"repository_name": "docker-intellij-idea",
"repository": "https://github.com/linuxserver/docker-intellij-idea",
"description": "IntelliJ IDEA helps you write code faster with tools that eliminate tedious tasks and let you focus on what matters \u2013 building great software.",
"description": "IntelliJ IDEA helps you write code faster with tools that eliminate tedious tasks and let you focus on what matters – building great software.",
"website": "https://www.jetbrains.com/idea/",
"icon": "https://cdn.jsdelivr.net/gh/linuxserver/docker-templates@master/linuxserver.io/img/intellij-idea-logo.png",
"architectures": [
@@ -7068,7 +7068,7 @@
"template_family": "curated-profile",
"title": "Jaeger",
"repository": "https://github.com/jaegertracing/jaeger",
"description": "Run Jaeger\u2019s all-in-one image for collecting OTLP traces and viewing them in the web UI. This profile uses the built-in in-memory storage.",
"description": "Run Jaeger’s all-in-one image for collecting OTLP traces and viewing them in the web UI. This profile uses the built-in in-memory storage.",
"website": "https://github.com/jaegertracing/jaeger",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/jaeger.webp",
"architectures": [
@@ -7396,7 +7396,7 @@
"title": "Kali Linux",
"repository_name": "docker-kali-linux",
"repository": "https://github.com/linuxserver/docker-kali-linux",
"description": "Kali-linux - is an Advanced Penetration Testing Linux distribution used for Penetration Testing, Ethical Hacking and network security assessments. KALI LINUX \u2122 is a trademark of OffSec.",
"description": "Kali-linux - is an Advanced Penetration Testing Linux distribution used for Penetration Testing, Ethical Hacking and network security assessments. KALI LINUX ™ is a trademark of OffSec.",
"website": "https://github.com/linuxserver/docker-kali-linux",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kali-linux.webp",
"architectures": [
@@ -7721,6 +7721,39 @@
"hidden": false,
"template_id": "linuxserver-kimai"
},
{
"id": "kitchenowl",
"provider": "tombursch",
"template_family": "curated-profile",
"title": "KitchenOwl",
"repository": "https://github.com/TomBursch/kitchenowl",
"description": "Manage shared shopping lists, recipes and meal plans in one self-hosted app.",
"website": "https://kitchenowl.org/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kitchenowl.webp",
"architectures": [
"amd64",
"arm64"
],
"updated_at": "2026-11-01",
"main_image": "tombursch/kitchenowl-web:latest",
"category": "productivity",
"category_label": "Productivity & Workflows",
"replaces_discovered_ids": [],
"curated_path": "catalog/curated/kitchenowl.json",
"template_id": "image-kitchenowl",
"template": "apps/kitchenowl.json",
"template_status": "generated-unvalidated",
"hidden": false,
"automatic_install_candidate": true,
"untranslated_blockers": [],
"requires_privileged_lxc": false,
"optional_privileged_lxc": false,
"requires_host_pid_namespace": false,
"requires_relaxed_confinement": false,
"optional_relaxed_confinement": false,
"requires_security_confirmation": false,
"multi_container": true
},
{
"id": "kiwix-serve",
"provider": "Kiwix",
@@ -7917,6 +7950,39 @@
"hidden": false,
"template_id": "image-kroma"
},
{
"id": "krusader",
"provider": "jlesage",
"template_family": "curated-profile",
"title": "Krusader",
"repository": "https://github.com/jlesage/docker-krusader",
"description": "Open Krusader in a browser and manage files from a directory you explicitly mount at /storage. The maintained jlesage image provides the graphical desktop; ProxMenux runs it as one native OCI LXC.",
"website": "https://krusader.org/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/krusader.webp",
"architectures": [
"amd64",
"arm64"
],
"updated_at": null,
"main_image": "jlesage/krusader:latest",
"category": "desktop",
"category_label": "Browsers & Web Desktops",
"replaces_discovered_ids": [],
"curated_path": "catalog/curated/krusader.json",
"template_id": "image-krusader",
"template": "apps/krusader.json",
"template_status": "generated-unvalidated",
"hidden": false,
"automatic_install_candidate": true,
"untranslated_blockers": [],
"requires_privileged_lxc": false,
"optional_privileged_lxc": false,
"requires_host_pid_namespace": false,
"requires_relaxed_confinement": false,
"optional_relaxed_confinement": false,
"requires_security_confirmation": false,
"multi_container": false
},
{
"id": "label-studio",
"source_app_id": "label-studio",
@@ -8026,7 +8092,7 @@
"title": "Ldap Auth",
"repository_name": "docker-ldap-auth",
"repository": "https://github.com/linuxserver/docker-ldap-auth",
"description": "Ldap-auth software is for authenticating users who request protected resources from servers proxied by nginx. It includes a daemon (ldap-auth) that communicates with an authentication server, and a webserver daemon that generates an authentication cookie based on the user\u2019s credentials. The daemons are written in Python for use with a Lightweight Directory Access Protocol (LDAP) authentication server (OpenLDAP or Microsoft Windows Active Directory 2003 and 2012).",
"description": "Ldap-auth software is for authenticating users who request protected resources from servers proxied by nginx. It includes a daemon (ldap-auth) that communicates with an authentication server, and a webserver daemon that generates an authentication cookie based on the user’s credentials. The daemons are written in Python for use with a Lightweight Directory Access Protocol (LDAP) authentication server (OpenLDAP or Microsoft Windows Active Directory 2003 and 2012).",
"website": "https://github.com/nginxinc/nginx-ldap-auth",
"icon": "https://cdn.jsdelivr.net/gh/linuxserver/docker-templates@master/linuxserver.io/img/ldap-auth-logo.png",
"architectures": [
@@ -9484,7 +9550,7 @@
"variant": null,
"title": "Memos",
"repository": "https://hub.docker.com/r/neosmemo/memos",
"description": "Memos is a lightweight, open-source, self-hosted note-taking application that offers a secure and streamlined solution for users prioritizing privacy and data control. All notes are stored on the user\u2019s own server, eliminating risks associated with third-party cloud services. Its minimalist Web interface supports Markdown syntax and tag-based organization, enabling effortless capture of ideas, personal knowledge management, or small-scale team collaboration. The open-source design ensures transparency, long-term maintainability, and no subscription costs, making it ideal for users seeking data ownership and cost efficiency.\n\nDesigned for simplicity and efficiency, Memos caters to a variety of use cases. Whether jotting down daily thoughts, organizing study notes, or sharing task memos in small teams, Memos delivers a seamless experience through its intuitive tag system and Markdown formatting. Accessible via any web browser, it requires no proprietary clients or complex setup, allowing users to manage notes anytime, anywhere.\n\nWhether used for long-term personal knowledge archiving or as a lightweight tool for team collaboration, Memos offers a secure, flexible, and user-friendly solution, empowering users to maintain full control over their data while enjoying a streamlined note-taking experience.\n",
"description": "Memos is a lightweight, open-source, self-hosted note-taking application that offers a secure and streamlined solution for users prioritizing privacy and data control. All notes are stored on the user’s own server, eliminating risks associated with third-party cloud services. Its minimalist Web interface supports Markdown syntax and tag-based organization, enabling effortless capture of ideas, personal knowledge management, or small-scale team collaboration. The open-source design ensures transparency, long-term maintainability, and no subscription costs, making it ideal for users seeking data ownership and cost efficiency.\n\nDesigned for simplicity and efficiency, Memos caters to a variety of use cases. Whether jotting down daily thoughts, organizing study notes, or sharing task memos in small teams, Memos delivers a seamless experience through its intuitive tag system and Markdown formatting. Accessible via any web browser, it requires no proprietary clients or complex setup, allowing users to manage notes anytime, anywhere.\n\nWhether used for long-term personal knowledge archiving or as a lightweight tool for team collaboration, Memos offers a secure, flexible, and user-friendly solution, empowering users to maintain full control over their data while enjoying a streamlined note-taking experience.\n",
"website": "https://usememos.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/memos.webp",
"architectures": [
@@ -9917,7 +9983,7 @@
"variant": null,
"title": "Monica",
"repository": "https://hub.docker.com/_/monica",
"description": "Monica is a self-hosted personal relationship management tool that helps users document and organize interactions with family and friends via an intuitive Web interface, creating a personalized contact database. It is ideal for users balancing work and life, ensuring key personal connections are never missed.\n\nThe tool's core features include comprehensive contact management and smart reminders. It supports creating detailed contact profiles, logging personal details, relationships (e.g., family, friends), and how contacts were met. Users can set automatic reminders for birthdays, anniversaries, and other key dates, while tracking conversations, activities, and gift ideas. A diary feature records daily moods and significant moments, maintaining a clear life record.\n\nIt offers task and debt management to track to-dos or financial interactions. Users can upload photos and documents, favorite contacts, and organize relationships with labels for streamlined data management. Full control over local data ensures privacy. The tool\u2019s intuitive operation and high flexibility deliver a modern relationship management solution.\n\n**Key Features:**\n- Comprehensive contact management with detailed profiles\n- Automatic reminders for birthdays, anniversaries, and key dates\n- Track conversations, activities, and gift ideas\n- Diary feature to record daily moods and moments\n- Task and debt management for to-dos and finances\n- Upload photos and documents, favorite contacts\n- Organize contacts with labels\n- Local data storage for privacy assurance\n\n**Learn More:**\n- [Monica Official Website](https://www.monicahq.com)\n- [Monica GitHub Repository](https://github.com/monicahq/monica)\n",
"description": "Monica is a self-hosted personal relationship management tool that helps users document and organize interactions with family and friends via an intuitive Web interface, creating a personalized contact database. It is ideal for users balancing work and life, ensuring key personal connections are never missed.\n\nThe tool's core features include comprehensive contact management and smart reminders. It supports creating detailed contact profiles, logging personal details, relationships (e.g., family, friends), and how contacts were met. Users can set automatic reminders for birthdays, anniversaries, and other key dates, while tracking conversations, activities, and gift ideas. A diary feature records daily moods and significant moments, maintaining a clear life record.\n\nIt offers task and debt management to track to-dos or financial interactions. Users can upload photos and documents, favorite contacts, and organize relationships with labels for streamlined data management. Full control over local data ensures privacy. The tool’s intuitive operation and high flexibility deliver a modern relationship management solution.\n\n**Key Features:**\n- Comprehensive contact management with detailed profiles\n- Automatic reminders for birthdays, anniversaries, and key dates\n- Track conversations, activities, and gift ideas\n- Diary feature to record daily moods and moments\n- Task and debt management for to-dos and finances\n- Upload photos and documents, favorite contacts\n- Organize contacts with labels\n- Local data storage for privacy assurance\n\n**Learn More:**\n- [Monica Official Website](https://www.monicahq.com)\n- [Monica GitHub Repository](https://github.com/monicahq/monica)\n",
"website": "https://www.monicahq.com",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/monica.webp",
"architectures": [
@@ -10278,7 +10344,7 @@
"variant": null,
"title": "n8n",
"repository": "https://hub.docker.com/r/n8nio/n8n",
"description": "n8n is a powerful open-source workflow automation and conversational AI platform that blends the flexibility of coding with the simplicity of no-code development, empowering users to create efficient and secure automation workflows. It seamlessly connects any app with an API, leveraging native AI capabilities (like LangChain-based AI agent workflows) to process custom data, ideal for personal task management, team collaboration, or enterprise-grade automation. Its vibrant community offers over 400 integrations and 900+ ready-to-use templates, enabling users to deploy automations quickly.\n\nThe platform supports highly customizable workflow design, allowing users to write JavaScript/Python, add npm packages, or use an intuitive visual interface to manage data, catering to both simple tasks and complex processes. Enterprise-grade features like advanced permissions and air-gapped deployments ensure security, while multilingual support makes it accessible globally. n8n delivers a versatile and user-friendly automation solution.\n\nDiscover n8n\u2019s Automation Scenarios\nn8n\u2019s community resources provide extensive support and inspiration, helping users explore its scenario-based value and easily build automation workflows. Below are two key resources showcasing n8n\u2019s capabilities across various use cases:\n\n1. [n8n Official Community Forum](https://community.n8n.io/): \nThe forum is a hub for user collaboration and learning, offering resources from beginner guides to advanced workflow designs. Shared use cases include automating social media posts or real-time data syncing, such as using n8n to pull data from Google Sheets and send Slack notifications, boosting team collaboration and data efficiency.\n\n2. [n8n Official Template Library](https://n8n.io/workflows/): \nThe template library offers over 900 ready-to-use workflows for scenarios like marketing automation, data analytics, and customer support. For example, a template can link Shopify to Mailchimp, automatically adding new customers to mailing lists and sending welcome emails, making automation accessible to non-technical users. AI-driven workflows, like handling customer queries with LangChain, highlight n8n\u2019s strength in intelligent interactions.\n",
"description": "n8n is a powerful open-source workflow automation and conversational AI platform that blends the flexibility of coding with the simplicity of no-code development, empowering users to create efficient and secure automation workflows. It seamlessly connects any app with an API, leveraging native AI capabilities (like LangChain-based AI agent workflows) to process custom data, ideal for personal task management, team collaboration, or enterprise-grade automation. Its vibrant community offers over 400 integrations and 900+ ready-to-use templates, enabling users to deploy automations quickly.\n\nThe platform supports highly customizable workflow design, allowing users to write JavaScript/Python, add npm packages, or use an intuitive visual interface to manage data, catering to both simple tasks and complex processes. Enterprise-grade features like advanced permissions and air-gapped deployments ensure security, while multilingual support makes it accessible globally. n8n delivers a versatile and user-friendly automation solution.\n\nDiscover n8n’s Automation Scenarios\nn8n’s community resources provide extensive support and inspiration, helping users explore its scenario-based value and easily build automation workflows. Below are two key resources showcasing n8n’s capabilities across various use cases:\n\n1. [n8n Official Community Forum](https://community.n8n.io/): \nThe forum is a hub for user collaboration and learning, offering resources from beginner guides to advanced workflow designs. Shared use cases include automating social media posts or real-time data syncing, such as using n8n to pull data from Google Sheets and send Slack notifications, boosting team collaboration and data efficiency.\n\n2. [n8n Official Template Library](https://n8n.io/workflows/): \nThe template library offers over 900 ready-to-use workflows for scenarios like marketing automation, data analytics, and customer support. For example, a template can link Shopify to Mailchimp, automatically adding new customers to mailing lists and sending welcome emails, making automation accessible to non-technical users. AI-driven workflows, like handling customer queries with LangChain, highlight n8n’s strength in intelligent interactions.\n",
"website": "https://n8n.io",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/n8n.webp",
"architectures": [
@@ -10414,7 +10480,7 @@
"variant": null,
"title": "NetBird",
"repository": "https://hub.docker.com/r/netbirdio/netbird",
"description": "NetBird combines a WireGuard\u00ae-based overlay network with Zero Trust Network Access, providing a unified open source platform for reliable and secure connectivity.\n\n- Secure Remote Access \u2013 Enable least privilege network access in a few clicks\n- Zero-Config Deployment \u2013 Replace legacy VPNs with a peer-to-peer WireGuard\u00ae-based network\n- Seamless SSO with MFA \u2013 Secure your network access with session-based SSO & MFA\n- Dynamic Posture Checks \u2013 Grant access only to devices meeting your security rules\n- Centralized Network Management \u2013 Control your private network from a single place\n- Detailed Activity Logging \u2013 Identify who did what, and when in your network\n\n**Learn More:**\n- [NetBird Website](https://netbird.io)\n- [Documentation](https://docs.netbird.io)\n- [Self-Hosting Guide](https://docs.netbird.io/selfhosted/selfhosted-quickstart)\n- [NetBird GitHub](https://github.com/netbirdio/netbird)\n- [Slack Community](https://docs.netbird.io/slack-url)\n",
"description": "NetBird combines a WireGuard®-based overlay network with Zero Trust Network Access, providing a unified open source platform for reliable and secure connectivity.\n\n- Secure Remote Access – Enable least privilege network access in a few clicks\n- Zero-Config Deployment – Replace legacy VPNs with a peer-to-peer WireGuard®-based network\n- Seamless SSO with MFA – Secure your network access with session-based SSO & MFA\n- Dynamic Posture Checks – Grant access only to devices meeting your security rules\n- Centralized Network Management – Control your private network from a single place\n- Detailed Activity Logging – Identify who did what, and when in your network\n\n**Learn More:**\n- [NetBird Website](https://netbird.io)\n- [Documentation](https://docs.netbird.io)\n- [Self-Hosting Guide](https://docs.netbird.io/selfhosted/selfhosted-quickstart)\n- [NetBird GitHub](https://github.com/netbirdio/netbird)\n- [Slack Community](https://docs.netbird.io/slack-url)\n",
"website": "https://netbird.io",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/netbird.webp",
"architectures": [
@@ -10751,7 +10817,7 @@
"template_family": "curated-profile",
"title": "NocoDB",
"repository": "https://github.com/nocodb/nocodb",
"description": "Create and manage databases through a spreadsheet-style web interface. This single-image profile uses NocoDB\u2019s SQLite quickstart mode.",
"description": "Create and manage databases through a spreadsheet-style web interface. This single-image profile uses NocoDB’s SQLite quickstart mode.",
"website": "https://nocodb.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/nocodb.webp",
"architectures": [
@@ -11307,7 +11373,7 @@
"template_family": "curated-profile",
"title": "Open WebUI",
"repository": "https://ghcr.io/open-webui/open-webui",
"description": "Open WebUI is a feature-rich, user-friendly self-hosted AI platform designed for fully offline operation, supporting multiple large language model runners and API integration. Its intuitive Web interface provides powerful AI deployment capabilities, ideal for developers, researchers, and AI enthusiasts building localized intelligent applications.\n\nThe tool's core features include local model execution and Retrieval Augmented Generation (RAG). It supports Ollama and OpenAI-compatible APIs (e.g., LMStudio, GroqCloud), enabling seamless model switching and multi-model conversations. RAG enhances chat experiences through local document loading or web search integration (e.g., SearXNG, Google PSE). Granular permissions and role-based access control (RBAC) ensure security with customized user role management.\n\nIt supports Markdown and LaTeX for enriched interactions and offers hands-free voice and video call features for dynamic communication. Image generation integration (e.g., DALL-E, ComfyUI) enriches visual content, and a model builder enables creating and importing custom models via the interface. The Pipelines plugin framework supports Python plugins, extending functionality like function calling and real-time translation. The tool\u2019s offline privacy and flexibility deliver a modern AI interaction solution.\n\n**Key Features:**\n- Local execution of Ollama and OpenAI-compatible APIs with multi-model conversations\n- RAG support with local document and web search integration (e.g., SearXNG, Google PSE)\n- Granular permissions and role-based access control (RBAC)\n- Simultaneous interaction with multiple models, leveraging their strengths\n- Image generation integration with DALL-E, ComfyUI, and more\n- Full Markdown and LaTeX support\n- Hands-free voice and video call functionality\n- Pipelines plugin framework for custom Python functionality\n\n**Learn More:**\n- [Open WebUI Official Website](https://openwebui.com)\n- [Open WebUI Documentation](https://docs.openwebui.com)\n- [Open WebUI GitHub Repository](https://github.com/open-webui/open-webui)\n",
"description": "Open WebUI is a feature-rich, user-friendly self-hosted AI platform designed for fully offline operation, supporting multiple large language model runners and API integration. Its intuitive Web interface provides powerful AI deployment capabilities, ideal for developers, researchers, and AI enthusiasts building localized intelligent applications.\n\nThe tool's core features include local model execution and Retrieval Augmented Generation (RAG). It supports Ollama and OpenAI-compatible APIs (e.g., LMStudio, GroqCloud), enabling seamless model switching and multi-model conversations. RAG enhances chat experiences through local document loading or web search integration (e.g., SearXNG, Google PSE). Granular permissions and role-based access control (RBAC) ensure security with customized user role management.\n\nIt supports Markdown and LaTeX for enriched interactions and offers hands-free voice and video call features for dynamic communication. Image generation integration (e.g., DALL-E, ComfyUI) enriches visual content, and a model builder enables creating and importing custom models via the interface. The Pipelines plugin framework supports Python plugins, extending functionality like function calling and real-time translation. The tool’s offline privacy and flexibility deliver a modern AI interaction solution.\n\n**Key Features:**\n- Local execution of Ollama and OpenAI-compatible APIs with multi-model conversations\n- RAG support with local document and web search integration (e.g., SearXNG, Google PSE)\n- Granular permissions and role-based access control (RBAC)\n- Simultaneous interaction with multiple models, leveraging their strengths\n- Image generation integration with DALL-E, ComfyUI, and more\n- Full Markdown and LaTeX support\n- Hands-free voice and video call functionality\n- Pipelines plugin framework for custom Python functionality\n\n**Learn More:**\n- [Open WebUI Official Website](https://openwebui.com)\n- [Open WebUI Documentation](https://docs.openwebui.com)\n- [Open WebUI GitHub Repository](https://github.com/open-webui/open-webui)\n",
"website": "https://openwebui.com",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/open-webui.webp",
"architectures": [
@@ -11338,7 +11404,7 @@
"template_family": "curated-profile",
"title": "Open WebUI CUDA",
"repository": "https://ghcr.io/open-webui/open-webui",
"description": "Open WebUI is a feature-rich, user-friendly self-hosted AI platform designed for fully offline operation, supporting multiple large language model runners and API integration. Its intuitive Web interface provides powerful AI deployment capabilities, ideal for developers, researchers, and AI enthusiasts building localized intelligent applications.\n\nThe tool's core features include local model execution and Retrieval Augmented Generation (RAG). It supports Ollama and OpenAI-compatible APIs (e.g., LMStudio, GroqCloud), enabling seamless model switching and multi-model conversations. RAG enhances chat experiences through local document loading or web search integration (e.g., SearXNG, Google PSE). Granular permissions and role-based access control (RBAC) ensure security with customized user role management.\n\nIt supports Markdown and LaTeX for enriched interactions and offers hands-free voice and video call features for dynamic communication. Image generation integration (e.g., DALL-E, ComfyUI) enriches visual content, and a model builder enables creating and importing custom models via the interface. The Pipelines plugin framework supports Python plugins, extending functionality like function calling and real-time translation. The tool\u2019s offline privacy and flexibility deliver a modern AI interaction solution.\n\n**Key Features:**\n- Local execution of Ollama and OpenAI-compatible APIs with multi-model conversations\n- RAG support with local document and web search integration (e.g., SearXNG, Google PSE)\n- Granular permissions and role-based access control (RBAC)\n- Simultaneous interaction with multiple models, leveraging their strengths\n- Image generation integration with DALL-E, ComfyUI, and more\n- Full Markdown and LaTeX support\n- Hands-free voice and video call functionality\n- Pipelines plugin framework for custom Python functionality\n\n**Learn More:**\n- [Open WebUI Official Website](https://openwebui.com)\n- [Open WebUI Documentation](https://docs.openwebui.com)\n- [Open WebUI GitHub Repository](https://github.com/open-webui/open-webui)\n",
"description": "Open WebUI is a feature-rich, user-friendly self-hosted AI platform designed for fully offline operation, supporting multiple large language model runners and API integration. Its intuitive Web interface provides powerful AI deployment capabilities, ideal for developers, researchers, and AI enthusiasts building localized intelligent applications.\n\nThe tool's core features include local model execution and Retrieval Augmented Generation (RAG). It supports Ollama and OpenAI-compatible APIs (e.g., LMStudio, GroqCloud), enabling seamless model switching and multi-model conversations. RAG enhances chat experiences through local document loading or web search integration (e.g., SearXNG, Google PSE). Granular permissions and role-based access control (RBAC) ensure security with customized user role management.\n\nIt supports Markdown and LaTeX for enriched interactions and offers hands-free voice and video call features for dynamic communication. Image generation integration (e.g., DALL-E, ComfyUI) enriches visual content, and a model builder enables creating and importing custom models via the interface. The Pipelines plugin framework supports Python plugins, extending functionality like function calling and real-time translation. The tool’s offline privacy and flexibility deliver a modern AI interaction solution.\n\n**Key Features:**\n- Local execution of Ollama and OpenAI-compatible APIs with multi-model conversations\n- RAG support with local document and web search integration (e.g., SearXNG, Google PSE)\n- Granular permissions and role-based access control (RBAC)\n- Simultaneous interaction with multiple models, leveraging their strengths\n- Image generation integration with DALL-E, ComfyUI, and more\n- Full Markdown and LaTeX support\n- Hands-free voice and video call functionality\n- Pipelines plugin framework for custom Python functionality\n\n**Learn More:**\n- [Open WebUI Official Website](https://openwebui.com)\n- [Open WebUI Documentation](https://docs.openwebui.com)\n- [Open WebUI GitHub Repository](https://github.com/open-webui/open-webui)\n",
"website": "https://openwebui.com",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/open-webui.webp",
"architectures": [
@@ -11369,7 +11435,7 @@
"template_family": "curated-profile",
"title": "Open WebUI + Ollama",
"repository": "https://ghcr.io/open-webui/open-webui",
"description": "Open WebUI is a feature-rich, user-friendly self-hosted AI platform designed for fully offline operation, supporting multiple large language model runners and API integration. Its intuitive Web interface provides powerful AI deployment capabilities, ideal for developers, researchers, and AI enthusiasts building localized intelligent applications.\n\nThe tool's core features include local model execution and Retrieval Augmented Generation (RAG). It supports Ollama and OpenAI-compatible APIs (e.g., LMStudio, GroqCloud), enabling seamless model switching and multi-model conversations. RAG enhances chat experiences through local document loading or web search integration (e.g., SearXNG, Google PSE). Granular permissions and role-based access control (RBAC) ensure security with customized user role management.\n\nIt supports Markdown and LaTeX for enriched interactions and offers hands-free voice and video call features for dynamic communication. Image generation integration (e.g., DALL-E, ComfyUI) enriches visual content, and a model builder enables creating and importing custom models via the interface. The Pipelines plugin framework supports Python plugins, extending functionality like function calling and real-time translation. The tool\u2019s offline privacy and flexibility deliver a modern AI interaction solution.\n\n**Key Features:**\n- Local execution of Ollama and OpenAI-compatible APIs with multi-model conversations\n- RAG support with local document and web search integration (e.g., SearXNG, Google PSE)\n- Granular permissions and role-based access control (RBAC)\n- Simultaneous interaction with multiple models, leveraging their strengths\n- Image generation integration with DALL-E, ComfyUI, and more\n- Full Markdown and LaTeX support\n- Hands-free voice and video call functionality\n- Pipelines plugin framework for custom Python functionality\n\n**Learn More:**\n- [Open WebUI Official Website](https://openwebui.com)\n- [Open WebUI Documentation](https://docs.openwebui.com)\n- [Open WebUI GitHub Repository](https://github.com/open-webui/open-webui)\n",
"description": "Open WebUI is a feature-rich, user-friendly self-hosted AI platform designed for fully offline operation, supporting multiple large language model runners and API integration. Its intuitive Web interface provides powerful AI deployment capabilities, ideal for developers, researchers, and AI enthusiasts building localized intelligent applications.\n\nThe tool's core features include local model execution and Retrieval Augmented Generation (RAG). It supports Ollama and OpenAI-compatible APIs (e.g., LMStudio, GroqCloud), enabling seamless model switching and multi-model conversations. RAG enhances chat experiences through local document loading or web search integration (e.g., SearXNG, Google PSE). Granular permissions and role-based access control (RBAC) ensure security with customized user role management.\n\nIt supports Markdown and LaTeX for enriched interactions and offers hands-free voice and video call features for dynamic communication. Image generation integration (e.g., DALL-E, ComfyUI) enriches visual content, and a model builder enables creating and importing custom models via the interface. The Pipelines plugin framework supports Python plugins, extending functionality like function calling and real-time translation. The tool’s offline privacy and flexibility deliver a modern AI interaction solution.\n\n**Key Features:**\n- Local execution of Ollama and OpenAI-compatible APIs with multi-model conversations\n- RAG support with local document and web search integration (e.g., SearXNG, Google PSE)\n- Granular permissions and role-based access control (RBAC)\n- Simultaneous interaction with multiple models, leveraging their strengths\n- Image generation integration with DALL-E, ComfyUI, and more\n- Full Markdown and LaTeX support\n- Hands-free voice and video call functionality\n- Pipelines plugin framework for custom Python functionality\n\n**Learn More:**\n- [Open WebUI Official Website](https://openwebui.com)\n- [Open WebUI Documentation](https://docs.openwebui.com)\n- [Open WebUI GitHub Repository](https://github.com/open-webui/open-webui)\n",
"website": "https://openwebui.com",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/open-webui.webp",
"architectures": [
@@ -11472,7 +11538,7 @@
"variant": null,
"title": "OpenHAB",
"repository": "https://hub.docker.com/r/openhab/openhab",
"description": "The open Home Automation Bus (openHAB, pronounced \u02c8\u0259\u028ap\u0259n\u02c8h\u00e6b) is an open source, technology agnostic home automation platform which runs as the center of your smart home! Its ability to integrate a multitude of other devices and systems. openHAB includes other home automation systems, (smart) devices and other technologies into a single solution. To provide a uniform user interface and a common approach to automation rules across the entire system, regardless of the number of manufacturers and sub-systems involved. Giving you the most flexible tool available to make almost any home automation wish come true; if you can think it, odds are that you can implement it with openHAB.",
"description": "The open Home Automation Bus (openHAB, pronounced ˈəʊpənˈhæb) is an open source, technology agnostic home automation platform which runs as the center of your smart home! Its ability to integrate a multitude of other devices and systems. openHAB includes other home automation systems, (smart) devices and other technologies into a single solution. To provide a uniform user interface and a common approach to automation rules across the entire system, regardless of the number of manufacturers and sub-systems involved. Giving you the most flexible tool available to make almost any home automation wish come true; if you can think it, odds are that you can implement it with openHAB.",
"website": "https://www.openhab.org",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/openhab.webp",
"architectures": [
@@ -13061,7 +13127,7 @@
"variant": null,
"title": "Portainer",
"repository": "https://hub.docker.com/r/portainer/portainer-ce",
"description": "Portainer Community Edition (CE) is a lightweight container management tool offering an intuitive Web interface to simplify building, managing, and monitoring containerized applications. With over 500,000 active users, it is widely appreciated for its ease of use and robust functionality, ideal for individual developers, home lab users, and small teams.\n\nThe tool's core features include multi-platform support, resource management, and real-time monitoring. It supports managing various container platforms, covering containers, images, volumes, and networks. Users can quickly create, deploy, and manage containers via a \u201csmart\u201d graphical interface or comprehensive API, without needing deep command-line expertise. It provides real-time container status monitoring, log viewing, and configuration management, ensuring efficient control over application operations.\n\nIts design philosophy is to \u201csimplify container complexity\u201d with an intuitive interface and default settings that lower the technical barrier. Users can manage containerized applications without complex configurations, saving time and boosting efficiency. It is completely free, with data stored locally, ensuring full user control. Community support via GitHub Discussions and Slack, along with rich documentation and regular updates, enhances the user experience, making it suitable for learning container technology or managing small projects.\n\n**Key Features:**\n- Intuitive Web interface for simplified containerized app management\n- Supports multiple container platforms for unified resource management\n- Real-time monitoring of container status and logs\n- Rapid container deployment and management via API\n- Community support with extensive documentation and assistance\n\n**Learn More:**\n- [Portainer Official Website](https://www.portainer.io/)\n- [Portainer GitHub Repository](https://github.com/portainer/portainer)\n",
"description": "Portainer Community Edition (CE) is a lightweight container management tool offering an intuitive Web interface to simplify building, managing, and monitoring containerized applications. With over 500,000 active users, it is widely appreciated for its ease of use and robust functionality, ideal for individual developers, home lab users, and small teams.\n\nThe tool's core features include multi-platform support, resource management, and real-time monitoring. It supports managing various container platforms, covering containers, images, volumes, and networks. Users can quickly create, deploy, and manage containers via a “smart” graphical interface or comprehensive API, without needing deep command-line expertise. It provides real-time container status monitoring, log viewing, and configuration management, ensuring efficient control over application operations.\n\nIts design philosophy is to “simplify container complexity” with an intuitive interface and default settings that lower the technical barrier. Users can manage containerized applications without complex configurations, saving time and boosting efficiency. It is completely free, with data stored locally, ensuring full user control. Community support via GitHub Discussions and Slack, along with rich documentation and regular updates, enhances the user experience, making it suitable for learning container technology or managing small projects.\n\n**Key Features:**\n- Intuitive Web interface for simplified containerized app management\n- Supports multiple container platforms for unified resource management\n- Real-time monitoring of container status and logs\n- Rapid container deployment and management via API\n- Community support with extensive documentation and assistance\n\n**Learn More:**\n- [Portainer Official Website](https://www.portainer.io/)\n- [Portainer GitHub Repository](https://github.com/portainer/portainer)\n",
"website": "https://www.portainer.io/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/portainer.webp",
"architectures": [
@@ -13518,7 +13584,7 @@
"variant": null,
"title": "qBittorrent",
"repository": "https://ghcr.io/hotio/qbittorrent",
"description": "**Elevate Your Home Media Experience**. qBittorrent transforms your home media setup with a polished interface reminiscent of \u00b5Torrent, minus the ads. Unlike traditional download tools, it offers a streamlined, efficient, and ad-free experience. This makes it a perfect fit for those seeking a hassle-free way to manage and enjoy their media collections at home.\n\n**Feature-Rich and User-Friendly**. With qBittorrent, you gain access to a well-integrated and extensible search engine, allowing simultaneous searches across multiple torrent sites, and category-specific searches for books, music, and software. Its support for RSS feeds with advanced filters, magnet links, encrypted connections, and more, ensures you have complete control over your downloads. Available on all major platforms and in around 70 languages, qBittorrent is both versatile and accessible.\n\n**Seamless Integration with Private Clouds like self-hosted server**. Deploying qBittorrent on private cloud devices such as self-hosted server offers unparalleled convenience. Enjoy virtually unlimited storage, enhanced privacy for your data, and local network speeds that make downloading and streaming effortless. This setup ensures that your media library is always at your fingertips, securely and swiftly.\n",
"description": "**Elevate Your Home Media Experience**. qBittorrent transforms your home media setup with a polished interface reminiscent of µTorrent, minus the ads. Unlike traditional download tools, it offers a streamlined, efficient, and ad-free experience. This makes it a perfect fit for those seeking a hassle-free way to manage and enjoy their media collections at home.\n\n**Feature-Rich and User-Friendly**. With qBittorrent, you gain access to a well-integrated and extensible search engine, allowing simultaneous searches across multiple torrent sites, and category-specific searches for books, music, and software. Its support for RSS feeds with advanced filters, magnet links, encrypted connections, and more, ensures you have complete control over your downloads. Available on all major platforms and in around 70 languages, qBittorrent is both versatile and accessible.\n\n**Seamless Integration with Private Clouds like self-hosted server**. Deploying qBittorrent on private cloud devices such as self-hosted server offers unparalleled convenience. Enjoy virtually unlimited storage, enhanced privacy for your data, and local network speeds that make downloading and streaming effortless. This setup ensures that your media library is always at your fingertips, securely and swiftly.\n",
"website": "https://www.qbittorrent.org",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/qbittorrent.webp",
"architectures": [
@@ -13617,7 +13683,7 @@
"variant": null,
"title": "Qui",
"repository": "https://ghcr.io/autobrr/qui",
"description": "**One dashboard for all your qBittorrent instances.** Qui is a fast, modern web interface for qBittorrent. Instead of juggling several separate WebUI tabs, you connect every qBittorrent instance to Qui and manage them all from a single, responsive dashboard. It ships as a lightweight single binary, so it stays quick and resource-friendly even when you are watching thousands of torrents.\n\n**Automation and cross-seeding built in.** Built by the team behind autobrr, Qui goes well beyond basic torrent listing. It adds powerful rule-based automation, built-in cross-seeding that automatically finds and adds matching torrents across your trackers, scheduled backups with restore, and a clean multi-language interface. Point it at your Torznab-compatible indexers \u2014 such as Prowlarr or Jackett \u2014 to put cross-seeding on autopilot, then filter, search, and bulk-manage torrents across every instance at once.\n\n**Made for your private cloud.** Running Qui on a private cloud device like self-hosted server keeps everything in one place on hardware you control. Pair it with your qBittorrent containers for local-network speeds, full ownership of your data, and a single tidy interface for your entire download stack \u2014 accessible from any device in your home.\n",
"description": "**One dashboard for all your qBittorrent instances.** Qui is a fast, modern web interface for qBittorrent. Instead of juggling several separate WebUI tabs, you connect every qBittorrent instance to Qui and manage them all from a single, responsive dashboard. It ships as a lightweight single binary, so it stays quick and resource-friendly even when you are watching thousands of torrents.\n\n**Automation and cross-seeding built in.** Built by the team behind autobrr, Qui goes well beyond basic torrent listing. It adds powerful rule-based automation, built-in cross-seeding that automatically finds and adds matching torrents across your trackers, scheduled backups with restore, and a clean multi-language interface. Point it at your Torznab-compatible indexers — such as Prowlarr or Jackett — to put cross-seeding on autopilot, then filter, search, and bulk-manage torrents across every instance at once.\n\n**Made for your private cloud.** Running Qui on a private cloud device like self-hosted server keeps everything in one place on hardware you control. Pair it with your qBittorrent containers for local-network speeds, full ownership of your data, and a single tidy interface for your entire download stack — accessible from any device in your home.\n",
"website": "https://getqui.com",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/qui.webp",
"architectures": [
@@ -13684,7 +13750,7 @@
"title": "Radarr",
"repository_name": "docker-radarr",
"repository": "https://github.com/linuxserver/docker-radarr",
"description": "Radarr - A fork of Sonarr to work with movies \u00e0 la Couchpotato.",
"description": "Radarr - A fork of Sonarr to work with movies à la Couchpotato.",
"website": "https://github.com/Radarr/Radarr",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/radarr.webp",
"architectures": [
@@ -13957,7 +14023,7 @@
"template_family": "curated-profile",
"title": "Redis Insight",
"repository": "https://github.com/redis/RedisInsight",
"description": "Redis\u2019s official visual tool for browsing Redis data, running commands and using diagnostics. It starts as a standalone service; connect it to Redis servers after opening the interface.",
"description": "Redis’s official visual tool for browsing Redis data, running commands and using diagnostics. It starts as a standalone service; connect it to Redis servers after opening the interface.",
"website": "https://redis.io/insight/",
"icon": "https://raw.githubusercontent.com/redis/RedisInsight/43bd288ed30dc6d2355723158dad5d7ecf4a280e/resources/icons/512x512.png",
"architectures": [
@@ -13989,7 +14055,7 @@
"template_family": "curated-profile",
"title": "Redmine",
"repository": "https://github.com/redmine/redmine",
"description": "Track projects, issues, milestones, time and documentation in a self-hosted web application. This single-image profile uses Redmine\u2019s built-in SQLite database and persists it alongside uploaded files.",
"description": "Track projects, issues, milestones, time and documentation in a self-hosted web application. This single-image profile uses Redmine’s built-in SQLite database and persists it alongside uploaded files.",
"website": "https://www.redmine.org/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/redmine.webp",
"architectures": [
@@ -14607,7 +14673,7 @@
"title": "Sealskin",
"repository_name": "docker-sealskin",
"repository": "https://github.com/linuxserver/docker-sealskin",
"description": "Sealskin is a self-hosted, client-server platform that enables users to run powerful, containerized desktop applications streamed directly to a web browser. It uses a browser extension to intercept user actions\u2014such as clicking a link or downloading a file and redirects them to a secure, isolated application environment running on a remote server.",
"description": "Sealskin is a self-hosted, client-server platform that enables users to run powerful, containerized desktop applications streamed directly to a web browser. It uses a browser extension to intercept user actions—such as clicking a link or downloading a file and redirects them to a secure, isolated application environment running on a remote server.",
"website": "https://github.com/selkies-project/sealskin/",
"icon": null,
"architectures": [
@@ -15286,6 +15352,44 @@
"hidden": false,
"template_id": "linuxserver-speedtest-tracker"
},
{
"id": "spoolman",
"provider": "Donkie",
"template_family": "curated-profile",
"title": "Spoolman",
"repository": "https://github.com/Donkie/Spoolman",
"description": "A self-hosted filament inventory for 3D printers. Manage spools, materials, manufacturers, locations and labels in the web UI, and integrate with Moonraker, OctoPrint and other printer tools. The official image uses SQLite by default; no separate database container is required.",
"website": "https://github.com/Donkie/Spoolman",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/spoolman.webp",
"architectures": [
"amd64",
"arm64"
],
"updated_at": null,
"main_image": "ghcr.io/donkie/spoolman:latest",
"category": "tools",
"category_label": "Tools",
"replaces_discovered_ids": [],
"curated_path": "catalog/curated/spoolman.json",
"template": "apps/spoolman.json",
"template_status": "generated-unvalidated",
"hidden": false,
"automatic_install_candidate": true,
"untranslated_blockers": [],
"requires_privileged_lxc": false,
"optional_privileged_lxc": false,
"requires_host_pid_namespace": false,
"requires_relaxed_confinement": false,
"optional_relaxed_confinement": false,
"requires_security_confirmation": false,
"multi_container": false,
"template_id": "image-spoolman",
"source_app_id": "spoolman",
"default_branch": "master",
"source_path": "OCI recipe from upstream installation guide",
"source_revision": "ff8efd517b76a4b2783f29c89e9b9fbcd8cd167b",
"pushed_at": "2026-10-31T09:39:19Z"
},
{
"id": "spotube",
"provider": "linuxserver.io",
@@ -15356,7 +15460,7 @@
"template_family": "curated-profile",
"title": "SQLPage",
"repository": "https://github.com/sqlpage/SQLPage",
"description": "Create web pages by writing SQL queries that SQLPage turns into a website. This profile uses the project\u2019s DuckDB image variant, which includes its embedded database driver and needs no database sidecar. A small starter page is created on first install.",
"description": "Create web pages by writing SQL queries that SQLPage turns into a website. This profile uses the project’s DuckDB image variant, which includes its embedded database driver and needs no database sidecar. A small starter page is created on first install.",
"website": "https://sql-page.com/",
"icon": "https://raw.githubusercontent.com/sqlpage/SQLPage/main/examples/official-site/assets/icon.webp",
"architectures": [
@@ -16018,7 +16122,7 @@
"variant": null,
"title": "Tailscale",
"repository": "https://hub.docker.com/r/tailscale/tailscale",
"description": "A modern self-hosted networking app built on WireGuard\u00ae, providing secure, encrypted connections between devices regardless of their location. Its zero-configuration networking eliminates the need for complex firewall rules, port forwarding, or network administration, making it ideal for businesses and individuals creating efficient, secure network environments.\n\nThe app's core features include seamless device connectivity and robust security. It uses WireGuard\u00ae for end-to-end encryption, ensuring traffic cannot be intercepted, with private keys stored solely on user devices. Automatic NAT traversal enables connections across computers, phones, servers, and IoT devices over different network types, forming a unified private network. It also offers identity-based access control, integrating with Google, Microsoft, GitHub, or custom SSO solutions for simple authentication, replacing traditional IP-based restrictions to enhance security.\n\nIt excels in delivering secure remote access to services and infrastructure. Users can effortlessly access home servers, connect to office networks while traveling, or establish secure links between cloud services. Subnet routing allows access to entire networks, exit nodes enable secure internet browsing, and MagicDNS simplifies device discovery. These features ensure efficient, secure access to resources from any location.\n\nIt supports nearly all platforms, including Linux, Windows, macOS, iOS, Android, and various router firmwares, with flexible deployment in cloud or on-premises environments. A user-friendly Web interface provides real-time monitoring of network topology, device status, and access controls, with community documentation aiding configuration optimization. Whether setting up secure access for small teams or managing enterprise-scale networks, the app\u2019s intuitive operation and high flexibility deliver a modern networking solution.\n\n**Key Features:**\n- End-to-end encryption via WireGuard\u00ae, ensuring uninterceptible traffic\n- Zero-configuration networking, eliminating complex firewall or port forwarding setup\n- Automatic NAT traversal for seamless device connectivity across network types\n- Identity-based access control with SSO integration (Google, Microsoft, GitHub)\n- Subnet routing for secure network-wide access\n- Exit nodes for safe internet browsing\n- MagicDNS for simplified device discovery\n\n**Learn More:**\n- [Tailscale Official Website](https://tailscale.com)\n- [Tailscale GitHub Repository](https://github.com/tailscale/tailscale)\n",
"description": "A modern self-hosted networking app built on WireGuard®, providing secure, encrypted connections between devices regardless of their location. Its zero-configuration networking eliminates the need for complex firewall rules, port forwarding, or network administration, making it ideal for businesses and individuals creating efficient, secure network environments.\n\nThe app's core features include seamless device connectivity and robust security. It uses WireGuard® for end-to-end encryption, ensuring traffic cannot be intercepted, with private keys stored solely on user devices. Automatic NAT traversal enables connections across computers, phones, servers, and IoT devices over different network types, forming a unified private network. It also offers identity-based access control, integrating with Google, Microsoft, GitHub, or custom SSO solutions for simple authentication, replacing traditional IP-based restrictions to enhance security.\n\nIt excels in delivering secure remote access to services and infrastructure. Users can effortlessly access home servers, connect to office networks while traveling, or establish secure links between cloud services. Subnet routing allows access to entire networks, exit nodes enable secure internet browsing, and MagicDNS simplifies device discovery. These features ensure efficient, secure access to resources from any location.\n\nIt supports nearly all platforms, including Linux, Windows, macOS, iOS, Android, and various router firmwares, with flexible deployment in cloud or on-premises environments. A user-friendly Web interface provides real-time monitoring of network topology, device status, and access controls, with community documentation aiding configuration optimization. Whether setting up secure access for small teams or managing enterprise-scale networks, the app’s intuitive operation and high flexibility deliver a modern networking solution.\n\n**Key Features:**\n- End-to-end encryption via WireGuard®, ensuring uninterceptible traffic\n- Zero-configuration networking, eliminating complex firewall or port forwarding setup\n- Automatic NAT traversal for seamless device connectivity across network types\n- Identity-based access control with SSO integration (Google, Microsoft, GitHub)\n- Subnet routing for secure network-wide access\n- Exit nodes for safe internet browsing\n- MagicDNS for simplified device discovery\n\n**Learn More:**\n- [Tailscale Official Website](https://tailscale.com)\n- [Tailscale GitHub Repository](https://github.com/tailscale/tailscale)\n",
"website": "https://tailscale.com",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/tailscale.webp",
"architectures": [
@@ -16629,7 +16733,7 @@
"template_family": "curated-profile",
"title": "Toxiproxy",
"repository": "https://github.com/Shopify/toxiproxy",
"description": "Run Shopify\u2019s Toxiproxy API server, then define proxies and failure conditions through its REST API.",
"description": "Run Shopify’s Toxiproxy API server, then define proxies and failure conditions through its REST API.",
"website": "https://github.com/Shopify/toxiproxy",
"icon": "https://github.com/Shopify.png",
"architectures": [
@@ -16724,7 +16828,7 @@
"title": "Transmission",
"repository_name": "docker-transmission",
"repository": "https://github.com/linuxserver/docker-transmission",
"description": "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, \u00b5TP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.",
"description": "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.",
"website": "https://www.transmissionbt.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/transmission.webp",
"architectures": [
@@ -17016,6 +17120,39 @@
"multi_container": true,
"template_id": "image-umami"
},
{
"id": "unbound",
"provider": "klutchell",
"template_family": "curated-profile",
"title": "Unbound",
"repository": "https://github.com/klutchell/unbound-docker",
"description": "Run the community-maintained klutchell image of Unbound as a DNS resolver on its own Proxmox LXC. It listens on TCP and UDP port 53 and can load optional custom configuration files.",
"website": "https://unbound.net/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/unbound.webp",
"architectures": [
"amd64",
"arm64"
],
"updated_at": null,
"main_image": "klutchell/unbound:main",
"category": "adblock",
"category_label": "Adblock & DNS",
"replaces_discovered_ids": [],
"curated_path": "catalog/curated/unbound.json",
"template_id": "image-unbound",
"template": "apps/unbound.json",
"template_status": "generated-unvalidated",
"hidden": false,
"automatic_install_candidate": true,
"untranslated_blockers": [],
"requires_privileged_lxc": false,
"optional_privileged_lxc": false,
"requires_host_pid_namespace": false,
"requires_relaxed_confinement": false,
"optional_relaxed_confinement": false,
"requires_security_confirmation": false,
"multi_container": false
},
{
"id": "ungoogled-chromium",
"provider": "linuxserver.io",
@@ -17525,7 +17662,7 @@
"variant": null,
"title": "VoceChat",
"repository": "https://hub.docker.com/r/privoce/vocechat-server",
"description": "VoceChat is a secure chat software designed for independent deployment, offering a flexible solution for seamless communication. It combines instant messaging with channel-based group chats, allowing you to engage in one-on-one conversations or create themed channels for group discussions.\n\nVoceChat supports a variety of message formats, including text, images, files, emojis, and rich text (Markdown), making your communication vibrant and expressive. Once deployed, it can be accessed via a WebAPP or mobile APP, ensuring a consistent experience across platforms.\n\nWith robust management features, VoceChat enables easy member and channel administration, giving you full control over your team or group\u2019s communication environment. Whether for individual users or enterprise teams, VoceChat delivers a secure, versatile, and efficient chat solution.\n",
"description": "VoceChat is a secure chat software designed for independent deployment, offering a flexible solution for seamless communication. It combines instant messaging with channel-based group chats, allowing you to engage in one-on-one conversations or create themed channels for group discussions.\n\nVoceChat supports a variety of message formats, including text, images, files, emojis, and rich text (Markdown), making your communication vibrant and expressive. Once deployed, it can be accessed via a WebAPP or mobile APP, ensuring a consistent experience across platforms.\n\nWith robust management features, VoceChat enables easy member and channel administration, giving you full control over your team or group’s communication environment. Whether for individual users or enterprise teams, VoceChat delivers a secure, versatile, and efficient chat solution.\n",
"website": "https://voce.chat",
"icon": "https://cdn.jsdelivr.net/gh/IceWhaleTech/CasaOS-AppStore@main/Apps/VoceChat/icon.png",
"architectures": [
@@ -17590,7 +17727,7 @@
"title": "Vscodium",
"repository_name": "docker-vscodium",
"repository": "https://github.com/linuxserver/docker-vscodium",
"description": "VSCodium is a community-driven, freely-licensed binary distribution of Microsoft\u2019s editor VS Code.",
"description": "VSCodium is a community-driven, freely-licensed binary distribution of Microsoft’s editor VS Code.",
"website": "https://vscodium.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/vscodium.webp",
"architectures": [
@@ -18254,7 +18391,7 @@
"title": "Wireguard",
"repository_name": "docker-wireguard",
"repository": "https://github.com/linuxserver/docker-wireguard",
"description": "WireGuard\u00ae is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances. Initially released for the Linux kernel, it is now cross-platform (Windows, macOS, BSD, iOS, Android) and widely deployable. It is currently under heavy development, but already it might be regarded as the most secure, easiest to use, and simplest VPN solution in the industry.",
"description": "WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances. Initially released for the Linux kernel, it is now cross-platform (Windows, macOS, BSD, iOS, Android) and widely deployable. It is currently under heavy development, but already it might be regarded as the most secure, easiest to use, and simplest VPN solution in the industry.",
"website": "https://www.wireguard.com/",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/wireguard.webp",
"architectures": [
@@ -18286,7 +18423,7 @@
"title": "Wireshark",
"repository_name": "docker-wireshark",
"repository": "https://github.com/linuxserver/docker-wireshark",
"description": "Wireshark is the world\u2019s foremost and widely-used network protocol analyzer. It lets you see what\u2019s happening on your network at a microscopic level and is the de facto (and often de jure) standard across many commercial and non-profit enterprises, government agencies, and educational institutions. Wireshark development thrives thanks to the volunteer contributions of networking experts around the globe and is the continuation of a project started by Gerald Combs in 1998.",
"description": "Wireshark is the world’s foremost and widely-used network protocol analyzer. It lets you see what’s happening on your network at a microscopic level and is the de facto (and often de jure) standard across many commercial and non-profit enterprises, government agencies, and educational institutions. Wireshark development thrives thanks to the volunteer contributions of networking experts around the globe and is the continuation of a project started by Gerald Combs in 1998.",
"website": "https://www.wireshark.org/",
"icon": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@main/webp/wireshark.webp",
"architectures": [
@@ -18733,6 +18870,38 @@
"requires_security_confirmation": false,
"hidden": true,
"template_id": "image-ztnet"
},
{
"id": "zwave-js-ui",
"provider": "Z-Wave JS",
"template_family": "curated-profile",
"title": "Z-Wave JS UI",
"repository": "https://github.com/zwave-js/zwave-js-ui",
"description": "A self-hosted Z-Wave control panel and MQTT gateway. Its web interface can start without a Z-Wave controller; device control requires a compatible USB or serial controller and network security keys.",
"website": "https://zwave-js.github.io/zwave-js-ui/",
"icon": "https://raw.githubusercontent.com/zwave-js/zwave-js-ui/c38e0d2cbbb826bf2bab2db596a3452e59ce0e8e/public/apple-touch-icon-180x180.png",
"architectures": [
"amd64",
"arm64"
],
"updated_at": "2026-11-02",
"main_image": "docker.io/zwavejs/zwave-js-ui:latest",
"category": "smarthome",
"category_label": "IoT & Smart Home",
"replaces_discovered_ids": [],
"curated_path": "catalog/curated/zwave-js-ui.json",
"template_id": "image-zwave-js-ui",
"template": "apps/zwave-js-ui.json",
"template_status": "generated-unvalidated",
"automatic_install_candidate": true,
"untranslated_blockers": [],
"requires_privileged_lxc": false,
"optional_privileged_lxc": false,
"requires_host_pid_namespace": false,
"requires_relaxed_confinement": false,
"optional_relaxed_confinement": false,
"requires_security_confirmation": false,
"multi_container": false
}
],
"discovery": {
+6
View File
@@ -115,9 +115,15 @@
],
"unpackerr": [
"/data"
],
"spoolman": [
"/home/app/.local/share/spoolman"
]
},
"sizes": {
"krusader": {
"/config": 4
},
"sharecovex": {
"/config": 1
}
+151
View File
@@ -0,0 +1,151 @@
"""The KitchenOwl entry follows the upstream two-service Compose deployment."""
import json
from pathlib import Path
import sys
import unittest
from unittest.mock import patch
from jsonschema import validate
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from proxmenux_oci.catalog import Catalog
from proxmenux_oci.custom import template_from_oci_compose
from proxmenux_oci.stack import assess, build_stack, DefaultsUI
from proxmenux_oci.installer import DEFAULT_MODE
CURATED_PATH = ROOT / "catalog/curated/kitchenowl.json"
APPS_PATH = ROOT / "catalog/apps/kitchenowl.json"
def compile_stack(template):
"""Compile the recipe with deterministic answers, without touching Proxmox."""
with patch("proxmenux_oci.installer.ask_storage", return_value="local-zfs"), \
patch("proxmenux_oci.installer.ask_bridge", return_value="vmbr0"), \
patch("proxmenux_oci.host.timezone", return_value="UTC"), \
patch("proxmenux_oci.network.ask_addresses",
side_effect=lambda ui, bridge, labels: ({name: "dhcp" for name in labels}, None)):
return build_stack(template, DefaultsUI(), DEFAULT_MODE)
class KitchenOwlRecipeTests(unittest.TestCase):
def setUp(self):
self.curated = json.loads(CURATED_PATH.read_text(encoding="utf-8"))
self.generated = json.loads(APPS_PATH.read_text(encoding="utf-8"))
self.index = json.loads((ROOT / "catalog/index.json").read_text(encoding="utf-8"))
self.services = {
service["name"]: service
for service in self.curated["compose_stack"]["services"]
}
def test_curated_and_catalog_templates_match_and_validate(self):
self.assertEqual(self.curated, self.generated)
schema = json.loads((ROOT / "schemas/oci-template.schema.json").read_text(encoding="utf-8"))
validate(self.curated, schema)
def test_official_compose_is_the_two_image_frontend_backend_stack(self):
self.assertEqual(set(self.services), {"front", "back"})
self.assertEqual(self.curated["compose_stack"]["service_count"], 2)
self.assertEqual(self.curated["compose_stack"]["main_service"], "front")
self.assertEqual(self.services["front"]["image"], "tombursch/kitchenowl-web:latest")
self.assertEqual(self.services["back"]["image"], "tombursch/kitchenowl-backend:latest")
self.assertEqual(self.services["front"]["depends_on"], ["back"])
self.assertEqual(self.curated["compose_stack"]["orchestration"]["start_order"], ["back", "front"])
self.assertTrue(self.services["front"]["frontend_network"])
self.assertTrue(self.services["front"]["private_network"])
self.assertFalse(self.services["back"]["frontend_network"])
self.assertTrue(self.services["back"]["private_network"])
self.assertNotIn("postgres", self.services)
self.assertNotIn("redis", self.services)
def test_frontend_uses_the_official_backend_alias_and_port(self):
# BACK_URL is inherited from the official web image (back:5000); leaving
# the override absent preserves the image default and Compose service alias.
self.assertNotIn("BACK_URL", self.services["front"]["compose"].get("environment", {}))
image_policy = self.curated["proxmox"]["image_metadata_policy"]
self.assertEqual(image_policy["environment"], "import-image-env-then-apply-compose-overrides")
self.assertEqual(self.curated["container_contract"]["ports"][0]["container_port"], 80)
def test_backend_data_and_generated_jwt_key_are_preserved_safely(self):
back = self.services["back"]["compose"]
self.assertEqual(back["volumes"][0]["target"], "/data")
self.assertEqual(back["environment"]["JWT_SECRET_KEY"], "${GENERATED_JWT_SECRET_KEY}")
self.assertNotIn("PLEASE_CHANGE_ME", json.dumps(back))
secret = self.curated["compose_stack"]["installer_inputs"]["generated_secrets"]
self.assertEqual(len(secret), 1)
self.assertEqual(secret[0]["bindings"], [{"service": "back", "environment_variable": "JWT_SECRET_KEY"}])
volume = next(v for v in self.curated["compose_stack"]["storage"] if v["service"] == "back")
self.assertEqual(volume["container_path"], "/data")
self.assertEqual(volume["mode"], "managed-volume")
self.assertTrue(volume["user_selectable"])
self.assertTrue(volume["backup"])
def test_image_healthchecks_and_first_run_setup_are_recorded(self):
self.assertIn("uwsgi_curl localhost:5000 /api/health/", str(self.services["back"]["compose"]["healthcheck"]))
self.assertIn("curl -f http://localhost/", str(self.services["front"]["compose"]["healthcheck"]))
self.assertEqual(self.curated["first_run"]["endpoints"][0]["port"], 80)
self.assertEqual(self.curated["first_run"]["credentials"], [])
self.assertTrue(self.curated["proxmox"]["stack_completion_notes"])
def test_candidate_passes_stack_assessment_and_round_trips_through_catalog_reader(self):
catalog = Catalog(ROOT)
template = catalog.compose("kitchenowl")
self.assertEqual(assess(template), [])
self.assertEqual(template["compose_stack"]["project_name"], "kitchenowl")
reloaded = template_from_oci_compose(json.dumps(template))
expected = dict(template)
# Reading a pasted/shared recipe deliberately resets trust status; the
# catalog reapplies laboratory verification only for its own entry.
expected["status"] = "generated-unvalidated"
self.assertEqual(reloaded, expected)
def test_compiled_plan_keeps_private_backend_health_and_persistent_data(self):
template = Catalog(ROOT).compose("kitchenowl")
plan = compile_stack(template)
self.assertEqual(plan["stack_name"], "kitchenowl")
self.assertEqual(plan["network"]["private_allocation"], "automatic")
self.assertEqual(plan["network"]["private_bridge"], "vmbr10")
services = {service["name"]: service for service in plan["services"]}
self.assertEqual(list(services), ["back", "front"])
back = services["back"]
front = services["front"]
self.assertIn("back", back["aliases"])
self.assertIn("front", front["aliases"])
self.assertNotIn("frontend", back)
self.assertTrue(front["frontend"])
backend_health = back["healthcheck"]
self.assertEqual(backend_health["type"], "exec")
self.assertEqual(backend_health["timeout_seconds"], 189)
self.assertIn("uwsgi_curl", backend_health["argv"][-1])
frontend_health = front["healthcheck"]
self.assertEqual(frontend_health["type"], "exec")
self.assertEqual(frontend_health["timeout_seconds"], 99)
self.assertIn("curl -f http://localhost/", frontend_health["argv"][-1])
self.assertEqual(frontend_health["endpoint"], {"scheme": "http", "port": 80, "path": "/"})
self.assertEqual([mount["container_path"] for mount in back["deployment"]["mounts"]], ["/data"])
self.assertEqual(back["deployment"]["mounts"][0]["type"], "managed-volume")
self.assertTrue(back["deployment"]["mounts"][0]["backup"])
self.assertEqual(front["deployment"]["mounts"], [])
preparation = back["template"]["proxmox"]["installer_profile"]["volume_preparations"]
self.assertEqual(preparation[0]["container_path"], "/data")
self.assertEqual(preparation[0]["only_when_mount_type"], "managed-volume")
self.assertEqual(template["compose_stack"]["storage"][0]["service"], "back")
self.assertFalse(template["compose_stack"]["storage"][0]["shared_with_other_lxc"])
def test_icon_architectures_and_unvalidated_status_are_accurate(self):
ui = self.curated["catalog_ui"]
self.assertEqual(ui["icon"], "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kitchenowl.webp")
self.assertEqual(ui["architectures"], ["amd64", "arm64"])
self.assertEqual(self.curated["status"], "generated-unvalidated")
self.assertFalse(self.curated["compatibility"]["validated"])
entry = next(app for app in self.index["applications"] if app["id"] == "kitchenowl")
self.assertEqual(entry["template"], "apps/kitchenowl.json")
self.assertTrue(entry["multi_container"])
self.assertEqual(entry["category"], "productivity")
if __name__ == "__main__":
unittest.main()
+135
View File
@@ -0,0 +1,135 @@
"""Keep the native Krusader OCI profile aligned with its upstream image."""
import json
from pathlib import Path
import sys
import unittest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from proxmenux_oci.catalog import Catalog
class KrusaderRecipeTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.curated = json.loads(
(ROOT / "catalog/curated/krusader.json").read_text(encoding="utf-8")
)
cls.app = json.loads(
(ROOT / "catalog/apps/krusader.json").read_text(encoding="utf-8")
)
cls.template = Catalog(ROOT).compose("krusader")
def test_catalog_copy_matches_curated_profile(self):
self.assertEqual(self.curated, self.app)
def test_upstream_image_and_manifest_architectures_are_recorded(self):
contract = self.curated["container_contract"]
self.assertEqual(contract["image"]["reference"], "jlesage/krusader:latest")
self.assertEqual(self.curated["source"]["provider"], "jlesage")
self.assertEqual(self.curated["catalog_ui"]["architectures"], ["amd64", "arm64"])
def test_web_gui_uses_authenticated_https_and_does_not_expose_vnc(self):
env = {
entry["name"]: entry.get("example")
for entry in self.template["container_contract"]["environment"]
}
self.assertEqual(env["WEB_AUTHENTICATION"], "1")
self.assertEqual(env["SECURE_CONNECTION"], "1")
self.assertEqual(env["VNC_LOCALHOST_ONLY"], "1")
self.assertEqual(
self.template["catalog_ui"]["launch"],
{"scheme": "https", "port": 5800, "path": "/"},
)
self.assertEqual(
[port["container_port"] for port in self.template["container_contract"]["ports"]],
[5800],
)
credential = self.template["first_run"]["credentials"][0]
self.assertEqual(credential["username"], "admin")
self.assertEqual(credential["password_environment"], "WEB_AUTHENTICATION_PASSWORD")
self.assertIn(
"WEB_AUTHENTICATION_PASSWORD",
self.template["proxmox"]["installer_profile"]["generated_sensitive_environment"],
)
def test_only_config_and_selected_storage_are_persistent(self):
volumes = {
volume["container_path"]: volume
for volume in self.template["container_contract"]["volumes"]
}
self.assertEqual(set(volumes), {"/config", "/storage"})
self.assertEqual(volumes["/config"]["installation_choice"], ["managed-volume"])
self.assertEqual(volumes["/storage"]["installation_choice"], ["managed-volume", "host-bind"])
self.assertEqual(volumes["/storage"]["default"], "host-bind")
self.assertTrue(all(volume["managed_volume"]["backup"] for volume in volumes.values()))
self.assertEqual(volumes["/config"]["managed_volume"]["default_size_gb"], 4)
compose = self.template["compose_stack"]["services"][0]["compose"]
self.assertEqual(compose["image"], "jlesage/krusader:latest")
def test_storage_directory_offers_host_bind_with_the_image_user_identity(self):
profile = self.template["proxmox"]["installer_profile"]
storage = next(
item for item in self.template["container_contract"]["volumes"]
if item["container_path"] == "/storage"
)
compose_storage = next(
item for item in self.template["compose_stack"]["storage"]
if item["container_path"] == "/storage"
)
self.assertEqual(storage["installation_choice"], ["managed-volume", "host-bind"])
self.assertEqual(storage["default"], "host-bind")
self.assertTrue(compose_storage["user_selectable"])
self.assertEqual(compose_storage["mode"], "user-selectable")
self.assertEqual(
compose_storage["installation_choice"], ["managed-volume", "host-bind"]
)
self.assertEqual(
profile["volume_owner"], {"uid": 1000, "gid": 1000}
)
self.assertEqual(
{entry["name"]: entry["example"] for entry in self.template["container_contract"]["environment"]}
["USER_ID"],
"1000",
)
self.assertEqual(
{entry["name"]: entry["example"] for entry in self.template["container_contract"]["environment"]}
["GROUP_ID"],
"1000",
)
def test_partial_live_evidence_does_not_mark_app_as_fully_verified(self):
self.assertEqual(
self.curated["validation"]["clean_install"], "passed-first-start"
)
self.assertEqual(
self.curated["validation"]["service_health"],
"passed-login-page-and-authenticated-session-http-200",
)
self.assertEqual(
self.curated["validation"]["credentials"],
"passed-generated-password-login-and-session-cookie",
)
self.assertEqual(
self.curated["validation"]["web_authentication"],
"passed-generated-credential-post-and-session-cookie",
)
self.assertEqual(
self.curated["validation"]["restart_persistence"],
"passed-container-restart-and-host-bind-write-check",
)
self.assertEqual(
self.curated["validation"]["browser_visual"],
"pending-self-signed-certificate-review",
)
self.assertEqual(self.curated["validation"]["backup_restore"], "pending")
self.assertEqual(self.curated["validation"]["update_preserves_data"], "pending")
self.assertEqual(self.curated["status"], "generated-unvalidated")
self.assertFalse(self.curated["compatibility"]["validated"])
if __name__ == "__main__":
unittest.main()
+140
View File
@@ -0,0 +1,140 @@
"""Spoolman uses its upstream single-image SQLite deployment in OCI Manager."""
import json
from pathlib import Path
import sys
import unittest
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from proxmenux_oci.catalog import Catalog
from proxmenux_oci.installer import DEFAULT_MODE, build_deployment
class StorageChoiceUI:
def __init__(self, volume_mode):
self.volume_mode = volume_mode
def choose(self, _prompt, options, default=None, **_kwargs):
available = {key for key, _label in options}
if self.volume_mode not in available:
raise AssertionError(f"{self.volume_mode} was not offered; got {available}")
return self.volume_mode
def ask(self, _prompt, default="", **_kwargs):
return default
def confirm(self, _prompt, default=False):
return default
def message(self, *_args, **_kwargs):
pass
class SpoolmanRecipeTests(unittest.TestCase):
def setUp(self):
self.catalog = Catalog(ROOT)
self.recipe = self.catalog.compose("spoolman")
self.generated = json.loads(
(ROOT / "catalog/apps/spoolman.json").read_text(encoding="utf-8")
)
self.curated = json.loads(
(ROOT / "catalog/curated/spoolman.json").read_text(encoding="utf-8")
)
def test_generated_and_curated_recipes_match(self):
self.assertEqual(self.generated, self.curated)
def test_official_image_and_catalog_architectures_match_engine_support(self):
image = self.recipe["container_contract"]["image"]
self.assertEqual(image["reference"], "ghcr.io/donkie/spoolman:latest")
self.assertEqual(image["registry"], "ghcr.io")
self.assertEqual(image["repository"], "ghcr.io/donkie/spoolman")
# The OCI catalog currently supports amd64 and arm64. The upstream
# manifest also has arm/v7, which the current catalog engine does not
# offer as an installable architecture.
self.assertEqual(self.recipe["catalog_ui"]["architectures"], ["amd64", "arm64"])
self.assertEqual(
self.recipe["source"]["image_repository_url"],
"https://github.com/Donkie/Spoolman/pkgs/container/spoolman",
)
def test_sqlite_is_the_default_and_no_database_service_is_required(self):
contract = self.recipe["container_contract"]
self.assertEqual(self.recipe["compose_stack"]["service_count"], 1)
self.assertEqual(self.recipe["compose_stack"]["services"][0]["image"],
"ghcr.io/donkie/spoolman:latest")
self.assertEqual(contract["related_services"], [])
env_names = {item["name"] for item in contract["environment"]}
self.assertNotIn("SPOOLMAN_DB_TYPE", env_names)
self.assertNotIn("SPOOLMAN_DB_NAME", env_names)
self.assertNotIn("SPOOLMAN_DIR_DATA", env_names)
data_path = "/home/app/.local/share/spoolman"
self.assertEqual([volume["container_path"] for volume in contract["volumes"]],
[data_path])
self.assertIn("SQLite", self.recipe["catalog_ui"]["description"]["en_US"])
def test_web_launch_and_first_start_healthcheck_use_container_port_8000(self):
self.assertEqual(self.recipe["container_contract"]["ports"][0]["container_port"], 8000)
self.assertEqual(self.recipe["catalog_ui"]["launch"]["port"], 8000)
self.assertEqual(self.recipe["first_run"]["endpoints"][0]["port"], 8000)
health = self.recipe["proxmox"]["installer_profile"]["startup_healthcheck"]
self.assertEqual((health["scheme"], health["port"], health["path"]),
("http", 8000, "/"))
self.assertIn("7912:8000", self.recipe["container_contract"]["original_compose"])
def test_persistent_data_volume_and_application_uid_are_declared(self):
path = "/home/app/.local/share/spoolman"
volume = self.recipe["container_contract"]["volumes"][0]
policy = json.loads(
(ROOT / "catalog/volume-policy.json").read_text(encoding="utf-8")
)
self.assertIn(path, policy["applications"]["spoolman"])
self.assertEqual(volume["installation_choice"], ["managed-volume", "host-bind"])
self.assertEqual(volume["default"], "managed-volume")
preparation = self.recipe["proxmox"]["installer_profile"]["volume_preparations"][0]
self.assertEqual(preparation["container_path"], path)
self.assertEqual(preparation["owner_strategy"], "mapped-application-user")
self.assertEqual(preparation["only_when_mount_type"], "managed-volume")
self.assertEqual(self.recipe["proxmox"]["installer_profile"]["volume_owner"],
{"uid": 1000, "gid": 1000})
def test_default_install_preserves_data_for_both_storage_choices(self):
for mode in ("managed-volume", "host-bind"):
with self.subTest(mode=mode):
with patch("proxmenux_oci.installer.ask_storage",
return_value="local-zfs"), \
patch("proxmenux_oci.installer.host.default_bridge",
return_value="vmbr0"), \
patch("proxmenux_oci.installer.host.timezone",
return_value="UTC"), \
patch("proxmenux_oci.installer.access.ask_ipv4",
return_value=("dhcp", None)):
plan = build_deployment(
self.recipe, StorageChoiceUI(mode), DEFAULT_MODE
)
self.assertEqual(len(plan["mounts"]), 1)
mount = plan["mounts"][0]
self.assertEqual(mount["container_path"],
"/home/app/.local/share/spoolman")
self.assertEqual(mount["type"], mode)
self.assertFalse(mount["read_only"])
if mode == "managed-volume":
self.assertEqual(mount["source"], "local-zfs")
self.assertEqual(mount["size_gb"], 4)
self.assertTrue(mount["backup"])
else:
self.assertEqual(
mount["source"],
"/mnt/oci-shared/spoolman/home-app-local-share-spoolman",
)
self.assertIsNone(mount["size_gb"])
self.assertFalse(mount["backup"])
if __name__ == "__main__":
unittest.main()
+128
View File
@@ -0,0 +1,128 @@
"""Guard the Unbound catalog contract against upstream image behavior."""
import json
from pathlib import Path
import sys
import unittest
import yaml
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
sys.path.insert(0, str(ROOT / "remote"))
from proxmenux_oci.catalog import Catalog
import oci_instance_transaction
class UnboundRecipeTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.curated = json.loads((ROOT / "catalog/curated/unbound.json").read_text(encoding="utf-8"))
cls.app = json.loads((ROOT / "catalog/apps/unbound.json").read_text(encoding="utf-8"))
cls.composed = Catalog(ROOT).compose("unbound")
cls.index = json.loads((ROOT / "catalog/index.json").read_text(encoding="utf-8"))
cls.index_entry = next(item for item in cls.index["applications"] if item["id"] == "unbound")
cls.original_compose = yaml.safe_load(cls.curated["container_contract"]["original_compose"])
def test_both_catalog_templates_and_image_metadata_match_upstream(self):
self.assertEqual(self.curated, self.app)
image = self.curated["container_contract"]["image"]
self.assertEqual(image["reference"], "klutchell/unbound:main")
self.assertEqual(image["registry"], "docker.io")
self.assertEqual(image["repository"], "klutchell/unbound")
self.assertEqual(image["tag"], "main")
self.assertIsNone(image["digest"]) # resolve the selected tag for each host architecture at install time
self.assertEqual(
image["pull_policy"],
"resolve-selected-tag-to-architecture-digest-at-install",
)
self.assertEqual(
self.curated["source"]["repository"],
"https://github.com/klutchell/unbound-docker",
)
self.assertEqual(
self.curated["source"]["revision"],
"1efbea960bf9462b6fb6dd71aa702ad32d6820d7",
)
def test_icon_and_supported_architectures_are_in_both_catalog_views(self):
icon = "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/unbound.webp"
self.assertEqual(self.curated["catalog_ui"]["icon"], icon)
self.assertEqual(self.index_entry["icon"], icon)
self.assertEqual(self.index_entry["template_id"], "image-unbound")
# The upstream manifest has amd64, arm64, arm/v7 and arm/v6. The catalog
# normalizes the two 32-bit ARM variants to armhf; the install index
# only lists the architectures currently supported by ProxMenux.
self.assertEqual(self.curated["catalog_ui"]["architectures"], ["amd64", "arm64", "armhf"])
self.assertEqual(self.index_entry["architectures"], ["amd64", "arm64"])
def test_dns_listener_publishes_tcp_and_udp_53(self):
expected = {("tcp", 53), ("udp", 53)}
contract_ports = {
(port["protocol"], port["container_port"])
for port in self.curated["container_contract"]["ports"]
}
self.assertEqual(contract_ports, expected)
original = self.original_compose["services"]["unbound"]
self.assertEqual(set(original["ports"]), {"53:53/tcp", "53:53/udp"})
profile_service = self.curated["compose_stack"]["services"][0]["compose"]
self.assertEqual(set(profile_service["ports"]), {"53:53/tcp", "53:53/udp"})
def test_upstream_entrypoint_is_preserved_and_health_uses_running_fallback(self):
image_policy = self.curated["proxmox"]["image_metadata_policy"]
self.assertEqual(image_policy["entrypoint"], "import-from-oci-image")
self.assertEqual(image_policy["cmd"], "import-from-oci-image")
# Upstream Dockerfile sets ENTRYPOINT ["unbound"] and no HEALTHCHECK.
# Preserve the image command; check process stability on install/update,
# then verify DNS answers separately with a query to port 53.
for service in (self.original_compose["services"]["unbound"],
self.curated["compose_stack"]["services"][0]["compose"]):
self.assertNotIn("entrypoint", service)
self.assertNotIn("command", service)
self.assertNotIn("healthcheck", service)
self.assertEqual(self.curated["catalog_ui"]["launch"]["port"], None)
self.assertEqual(self.curated["first_run"]["endpoints"], [])
check = self.composed["proxmox"]["installer_profile"]["startup_healthcheck"]
self.assertEqual(check, {"type": "running", "timeout_seconds": 90, "stability_seconds": 20})
self.assertEqual(oci_instance_transaction.effective_healthcheck(self.composed), check)
def test_custom_configuration_mount_is_persistent_and_exposes_both_storage_modes(self):
path = "/etc/unbound/custom.conf.d"
volume = next(v for v in self.curated["container_contract"]["volumes"]
if v["container_path"] == path)
self.assertFalse(volume["read_only"])
self.assertFalse(volume["required"])
self.assertEqual(volume["installation_choice"], ["managed-volume", "host-bind"])
self.assertEqual(volume["default"], "host-bind")
self.assertTrue(volume["managed_volume"]["backup"])
original_service = self.original_compose["services"]["unbound"]
self.assertIn("./config:/etc/unbound/custom.conf.d", original_service["volumes"])
profile_service = self.curated["compose_stack"]["services"][0]["compose"]
self.assertIn("./config:/etc/unbound/custom.conf.d", profile_service["volumes"])
storage = next(v for v in self.curated["compose_stack"]["storage"]
if v["container_path"] == path)
self.assertEqual(storage["mode"], "user-selectable")
self.assertEqual(storage["installation_choice"], ["managed-volume", "host-bind"])
self.assertTrue(storage["backup"])
prepared = next(v for v in self.composed["proxmox"]["installer_profile"]["volume_preparations"]
if v["container_path"] == path)
self.assertEqual(prepared["owner_strategy"], "mapped-application-user")
def test_dns_service_remains_unprivileged_and_data_is_not_misrepresented_as_web_ui(self):
self.assertTrue(self.curated["proxmox"]["defaults"]["unprivileged"])
self.assertFalse(self.index_entry["requires_privileged_lxc"])
self.assertEqual(self.curated["catalog_ui"]["launch"]["port"], None)
self.assertTrue(any("no web interface" in tip.casefold()
for tip in self.curated["catalog_ui"]["tips"]))
self.assertTrue(any("do not expose recursive dns publicly" in tip.casefold()
for tip in self.curated["catalog_ui"]["tips"]))
if __name__ == "__main__":
unittest.main()
+94
View File
@@ -0,0 +1,94 @@
"""Z-Wave JS UI's native OCI profile is safe to start without a radio controller."""
from pathlib import Path
import sys
import unittest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
sys.path.insert(0, str(ROOT / "remote"))
from proxmenux_oci.catalog import Catalog, supported_architectures
import oci_instance_transaction as transaction
class ZWaveJsUIRecipeTests(unittest.TestCase):
def setUp(self):
self.catalog = Catalog(ROOT)
self.curated_path = ROOT / "catalog/curated/zwave-js-ui.json"
self.generated_path = ROOT / "catalog/apps/zwave-js-ui.json"
def test_generated_template_matches_curated_profile(self):
self.assertEqual(self.curated_path.read_bytes(), self.generated_path.read_bytes())
def test_uses_the_official_image_and_only_catalog_supported_architectures(self):
template = self.catalog.compose("zwave-js-ui")
self.assertEqual(template["id"], "image-zwave-js-ui")
self.assertEqual(
template["container_contract"]["image"]["reference"],
"docker.io/zwavejs/zwave-js-ui:latest",
)
self.assertEqual(
template["catalog_ui"]["architectures"], ["amd64", "arm64"]
)
self.assertEqual(
supported_architectures(template["catalog_ui"]["architectures"]),
["amd64", "arm64"],
)
self.assertIn(
"/c38e0d2cbbb826bf2bab2db596a3452e59ce0e8e/public/apple-touch-icon-180x180.png",
template["catalog_ui"]["icon"],
)
def test_web_ui_is_the_startup_check_when_no_controller_is_attached(self):
template = self.catalog.compose("zwave-js-ui")
check = transaction.effective_healthcheck(template)
self.assertEqual((check["scheme"], check["port"], check["path"]),
("http", 8091, "/"))
self.assertNotEqual(check["path"], "/health")
endpoint = template["first_run"]["endpoints"][0]
self.assertEqual((endpoint["port"], endpoint["path"]), (8091, "/"))
devices = template["proxmox"]["installer_profile"]["optional_devices"]
self.assertEqual(len(devices), 1)
self.assertFalse(devices[0]["enabled_default"])
self.assertFalse(devices[0]["required_by_compose"])
self.assertTrue(devices[0]["host_path_default"].startswith("/dev/serial/by-id/"))
def test_fresh_store_enables_login_and_keeps_generated_credentials_out_of_recipe(self):
template = self.catalog.compose("zwave-js-ui")
profile = template["proxmox"]["installer_profile"]
generated = profile["generated_sensitive_environment"]
self.assertEqual(generated["SESSION_SECRET"]["bytes"], 32)
self.assertEqual(generated["DEFAULT_PASSWORD"]["bytes"], 20)
seeded = next(item for item in profile["generated_files"]
if item["container_path"] == "/usr/src/app/store/settings.json")
self.assertTrue(seeded["only_if_missing"])
self.assertTrue(seeded["owner"] == "mapped-root")
self.assertIn('"authEnabled":true', seeded["content"])
credential = template["first_run"]["credentials"][0]
self.assertEqual(credential["username"], "admin")
self.assertIsNone(credential["password"])
self.assertEqual(credential["password_environment"], "DEFAULT_PASSWORD")
self.assertNotIn("mysupersecretkey", template["container_contract"]["original_compose"])
def test_state_is_persisted_on_a_managed_volume(self):
template = self.catalog.compose("zwave-js-ui")
volume = template["container_contract"]["volumes"][0]
self.assertEqual(volume["container_path"], "/usr/src/app/store")
self.assertEqual(volume["installation_choice"], ["managed-volume"])
self.assertTrue(volume["managed_volume"]["backup"])
prep = template["proxmox"]["installer_profile"]["volume_preparations"]
self.assertEqual(prep[0]["container_path"], "/usr/src/app/store")
self.assertEqual(prep[0]["owner_strategy"], "mapped-root")
def test_includes_web_and_optional_zwave_websocket_ports(self):
template = self.catalog.compose("zwave-js-ui")
ports = {item["container_port"]: item
for item in template["container_contract"]["ports"]}
self.assertTrue(ports[8091]["required"])
self.assertFalse(ports[3000]["required"])
self.assertEqual(ports[8091]["protocol"], "tcp")
self.assertEqual(ports[3000]["protocol"], "tcp")
if __name__ == "__main__":
unittest.main()