From 8f8a80785a970ee978e2e45816e356095878840c Mon Sep 17 00:00:00 2001 From: Jonatan Date: Tue, 27 Oct 2026 11:32:59 +0100 Subject: [PATCH] Add Umami OCI application profile --- lang/de.json | 7 +- lang/es.json | 7 +- lang/fr.json | 7 +- lang/it.json | 7 +- lang/pt.json | 7 +- lang/sk.json | 7 +- lang/sv.json | 7 +- oci/VALIDATION.md | 2 +- oci/catalog/apps/umami.json | 428 +++++++++++++++++++++++++++++++++ oci/catalog/categories.json | 1 + oci/catalog/curated/umami.json | 428 +++++++++++++++++++++++++++++++++ oci/catalog/index.json | 33 +++ 12 files changed, 933 insertions(+), 8 deletions(-) create mode 100644 oci/catalog/apps/umami.json create mode 100644 oci/catalog/curated/umami.json diff --git a/lang/de.json b/lang/de.json index eefc9822..94e8e43e 100644 --- a/lang/de.json +++ b/lang/de.json @@ -8869,5 +8869,10 @@ "⚠ Disk data will NOT be erased.": "⚠ Festplattendaten werden NICHT gelöscht.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Die Festplatte wird ausgehängt und aus /etc/fstab entfernt.", "⚠ The /etc/fstab entry will be removed.": "⚠ Der /etc/fstab-Eintrag wird entfernt.", - "⚠ The disk will be unmounted.": "⚠ Die Festplatte wird ausgehängt." + "⚠ The disk will be unmounted.": "⚠ Die Festplatte wird ausgehängt.", + "Initial Umami administrator": "Erster Administrator für Umami", + "Privacy-focused, self-hosted web analytics.": "Datenschutzorientierte, selbst gehostete Webanalyse.", + "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Die offiziellen Compose-Dienste von Umami und PostgreSQL fordern weder den privilegierten Modus noch eine gelockerte Isolation des Hostsystems an.", + "A self-hosted, no-code database and application platform.": "Selbst gehostete No-Code-Plattform für Datenbanken und Anwendungen.", + "Public URL used to access Baserow (for example https://baserow.example.com)": "Öffentliche URL für den Zugriff auf Baserow (zum Beispiel https://baserow.example.com)" } diff --git a/lang/es.json b/lang/es.json index 8111306a..0dbee4de 100644 --- a/lang/es.json +++ b/lang/es.json @@ -8841,5 +8841,10 @@ "⚠ Disk data will NOT be erased.": "⚠ Los datos del disco NO se borrarán.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ El disco se desmontará y se eliminará de /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ Se eliminará la entrada /etc/fstab.", - "⚠ The disk will be unmounted.": "⚠ El disco se desmontará." + "⚠ The disk will be unmounted.": "⚠ El disco se desmontará.", + "Initial Umami administrator": "Administrador inicial de Umami", + "Privacy-focused, self-hosted web analytics.": "Analítica web autoalojada y centrada en la privacidad.", + "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Los servicios oficiales de Umami y PostgreSQL en Compose no solicitan el modo privilegiado ni una relajación del aislamiento del host.", + "A self-hosted, no-code database and application platform.": "Plataforma autoalojada de bases de datos y aplicaciones sin necesidad de programar.", + "Public URL used to access Baserow (for example https://baserow.example.com)": "URL pública para acceder a Baserow (por ejemplo, https://baserow.example.com)" } diff --git a/lang/fr.json b/lang/fr.json index 8a67e093..db34dd93 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -8868,5 +8868,10 @@ "⚠ Disk data will NOT be erased.": "⚠ Les données du disque ne seront PAS effacées.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Le disque sera démonté et supprimé de /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ L'entrée /etc/fstab sera supprimée.", - "⚠ The disk will be unmounted.": "⚠ Le disque sera démonté." + "⚠ The disk will be unmounted.": "⚠ Le disque sera démonté.", + "Initial Umami administrator": "Administrateur initial d’Umami", + "Privacy-focused, self-hosted web analytics.": "Analyse web auto-hébergée axée sur la confidentialité.", + "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Les services Compose officiels d’Umami et de PostgreSQL ne demandent ni le mode privilégié ni un assouplissement de l’isolation de l’hôte.", + "A self-hosted, no-code database and application platform.": "Plateforme auto-hébergée de bases de données et d’applications sans code.", + "Public URL used to access Baserow (for example https://baserow.example.com)": "URL publique pour accéder à Baserow (par exemple https://baserow.example.com)" } diff --git a/lang/it.json b/lang/it.json index 8d8c253c..95478fe9 100644 --- a/lang/it.json +++ b/lang/it.json @@ -8869,5 +8869,10 @@ "⚠ Disk data will NOT be erased.": "⚠ I dati del disco NON verranno cancellati.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Il disco verrà smontato e rimosso da /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ La voce /etc/fstab verrà rimossa.", - "⚠ The disk will be unmounted.": "⚠ Il disco verrà smontato." + "⚠ The disk will be unmounted.": "⚠ Il disco verrà smontato.", + "Initial Umami administrator": "Amministratore iniziale di Umami", + "Privacy-focused, self-hosted web analytics.": "Analisi web self-hosted, attenta alla privacy.", + "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "I servizi Compose ufficiali di Umami e PostgreSQL non richiedono l'uso della modalità privilegiata né l'allentamento dell'isolamento dell'host.", + "A self-hosted, no-code database and application platform.": "Piattaforma self-hosted per creare database e applicazioni senza scrivere codice.", + "Public URL used to access Baserow (for example https://baserow.example.com)": "URL pubblico usato per accedere a Baserow (ad esempio https://baserow.example.com)" } diff --git a/lang/pt.json b/lang/pt.json index f81f2682..4efa6559 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -8869,5 +8869,10 @@ "⚠ Disk data will NOT be erased.": "⚠ Os dados do disco NÃO serão apagados.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ O disco será desmontado e removido de /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ A entrada /etc/fstab será removida.", - "⚠ The disk will be unmounted.": "⚠ O disco será desmontado." + "⚠ The disk will be unmounted.": "⚠ O disco será desmontado.", + "Initial Umami administrator": "Administrador inicial do Umami", + "Privacy-focused, self-hosted web analytics.": "Análise web auto-hospedada, com foco na privacidade.", + "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Os serviços Compose oficiais do Umami e do PostgreSQL não exigem modo privilegiado nem que o confinamento do host seja flexibilizado.", + "A self-hosted, no-code database and application platform.": "Plataforma autoalojada para criar bases de dados e aplicações sem escrever código.", + "Public URL used to access Baserow (for example https://baserow.example.com)": "URL público usado para aceder ao Baserow (por exemplo https://baserow.example.com)" } diff --git a/lang/sk.json b/lang/sk.json index 20c0722d..28c1527d 100644 --- a/lang/sk.json +++ b/lang/sk.json @@ -8870,5 +8870,10 @@ "⚠ Disk data will NOT be erased.": "⚠ Dáta na disku sa NEVYMAŽÚ.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disk sa odpojí a odstráni z /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ Záznam v /etc/fstab bude odstránený.", - "⚠ The disk will be unmounted.": "⚠ Disk bude odpojený." + "⚠ The disk will be unmounted.": "⚠ Disk bude odpojený.", + "Initial Umami administrator": "Počiatočný správca Umami", + "Privacy-focused, self-hosted web analytics.": "Webová analytika s dôrazom na ochranu súkromia, ktorú prevádzkujete na vlastnom serveri.", + "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Oficiálne služby Umami a PostgreSQL v Compose nevyžadujú privilegovaný režim ani zmiernenie izolácie hostiteľa.", + "A self-hosted, no-code database and application platform.": "Databázová a aplikačná platforma bez kódu, ktorú si hostujete sami.", + "Public URL used to access Baserow (for example https://baserow.example.com)": "Verejná adresa URL na prístup k Baserow (napríklad https://baserow.example.com)" } diff --git a/lang/sv.json b/lang/sv.json index 23f87aa8..ee25a507 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -8869,5 +8869,10 @@ "⚠ Disk data will NOT be erased.": "⚠ Diskdata kommer INTE att raderas.", "⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disken kommer att avmonteras och tas bort från /etc/fstab.", "⚠ The /etc/fstab entry will be removed.": "⚠ /etc/fstab-posten kommer att tas bort.", - "⚠ The disk will be unmounted.": "⚠ Disken kommer att avmonteras." + "⚠ The disk will be unmounted.": "⚠ Disken kommer att avmonteras.", + "Initial Umami administrator": "Umamis första administratör", + "Privacy-focused, self-hosted web analytics.": "Självhostad webbanalys med fokus på integritet.", + "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "De officiella Compose-tjänsterna för Umami och PostgreSQL begär varken privilegierat läge eller en mindre strikt isolering av värdsystemet.", + "A self-hosted, no-code database and application platform.": "Självhostad plattform för databaser och applikationer utan kodning.", + "Public URL used to access Baserow (for example https://baserow.example.com)": "Offentlig URL för åtkomst till Baserow (till exempel https://baserow.example.com)" } diff --git a/oci/VALIDATION.md b/oci/VALIDATION.md index 69bfdee6..a3ff01e1 100644 --- a/oci/VALIDATION.md +++ b/oci/VALIDATION.md @@ -2,7 +2,7 @@ -43 of 515 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 2 by the community. The OCI installer shows them as verified, with a ✓ in the lists. +43 of 516 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 2 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. diff --git a/oci/catalog/apps/umami.json b/oci/catalog/apps/umami.json new file mode 100644 index 00000000..0df02f56 --- /dev/null +++ b/oci/catalog/apps/umami.json @@ -0,0 +1,428 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-umami", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Umami" + }, + "tagline": { + "en_US": "Privacy-focused, self-hosted web analytics." + }, + "description": { + "en_US": "Umami is a privacy-focused web analytics platform. This recipe uses the official Umami image with a separate PostgreSQL LXC for persistent analytics data." + }, + "category": "monitoring", + "category_label": "Monitoring & Analytics", + "author": "Umami Software", + "developer": "Umami Software", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/umami.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 3000, + "path": "/" + }, + "website": "https://umami.is/", + "documentation": "https://docs.umami.is/docs/install", + "repository": "https://github.com/umami-software/umami", + "tips": [ + "The initial administrator login is admin / umami. Change the password immediately after the first sign-in.", + "Umami runs in one LXC and PostgreSQL in a separate private LXC. The PostgreSQL managed volume stores analytics data; the Umami service has no data volume.", + "The official Compose healthcheck probes /api/heartbeat. ProxMenux performs its standard first-start HTTP check against the web root; verify /api/heartbeat and sign-in separately during runtime validation.", + "The Compose file uses official Umami and PostgreSQL images. ProxMenux generates a unique database password, APP_SECRET, and 64-character TWO_FACTOR_ENCRYPTION_KEY." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": "2026-10-23" + }, + "source": { + "provider": "umami", + "repository": "https://github.com/umami-software/umami", + "default_branch": "master", + "revision": "559e3c607c23d2c72d9bd4f6481bda428255323c", + "image_repository_url": "https://github.com/umami-software/umami/pkgs/container/umami", + "readme_pushed_at": "", + "compose_sha256": "fb542a641c14fc2b1ff0d59558a197ed4f607b6608bf812afd20dad662ae0e8c", + "generated_at": "2026-10-27T09:38:05+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/umami-software/umami/559e3c607c23d2c72d9bd4f6481bda428255323c/docker-compose.yml" + }, + "container_contract": { + "service_name": "umami", + "container_name": "umami", + "image": { + "reference": "ghcr.io/umami-software/umami:latest", + "registry": "ghcr.io", + "repository": "ghcr.io/umami-software/umami", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "DATABASE_URL", + "example": "postgresql://umami:${GENERATED_DB_PASSWORD}@db:5432/umami", + "required": true, + "sensitive": true, + "source": "official-docker-compose" + }, + { + "name": "APP_SECRET", + "example": "${GENERATED_APP_SECRET}", + "required": true, + "sensitive": true, + "source": "official-documentation" + }, + { + "name": "TWO_FACTOR_ENCRYPTION_KEY", + "example": "${GENERATED_TWO_FACTOR_ENCRYPTION_KEY}", + "required": true, + "sensitive": true, + "source": "official-docker-compose" + } + ], + "volumes": [], + "ports": [ + { + "container_port": 3000, + "published_example": 3000, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [ + { + "name": "db", + "image": "postgres:15-alpine" + } + ], + "restart": "always", + "stop_grace_period": null, + "original_compose": "---\nservices:\n umami:\n image: ghcr.io/umami-software/umami:latest\n ports:\n - \"3000:3000\"\n environment:\n DATABASE_URL: postgresql://umami:umami@db:5432/umami\n APP_SECRET: replace-me-with-a-random-string\n # Required for two-factor authentication, generate with: openssl rand -hex 32\n TWO_FACTOR_ENCRYPTION_KEY: replace-me-with-a-64-character-hex-string\n depends_on:\n db:\n condition: service_healthy\n init: true\n restart: always\n healthcheck:\n test: [\"CMD-SHELL\", \"curl http://localhost:3000/api/heartbeat\"]\n interval: 5s\n timeout: 5s\n retries: 5\n db:\n image: postgres:15-alpine\n environment:\n POSTGRES_DB: umami\n POSTGRES_USER: umami\n POSTGRES_PASSWORD: umami\n volumes:\n - umami-db-data:/var/lib/postgresql/data\n restart: always\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}\"]\n interval: 5s\n timeout: 5s\n retries: 5\nvolumes:\n umami-db-data:\n" + }, + "compose_stack": { + "project_name": "umami", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "umami", + "service_count": 2, + "services": [ + { + "name": "db", + "image": "postgres:15-alpine", + "is_main": false, + "role": "dependency", + "vmid_offset": 1, + "depends_on": [], + "frontend_network": false, + "private_network": true, + "compose": { + "image": "postgres:15-alpine", + "environment": { + "POSTGRES_DB": "umami", + "POSTGRES_USER": "umami", + "POSTGRES_PASSWORD": "${GENERATED_DB_PASSWORD}" + }, + "volumes": [ + "umami-db-data:/var/lib/postgresql/data" + ], + "restart": "always", + "healthcheck": { + "test": [ + "CMD-SHELL", + "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}" + ], + "interval": "5s", + "timeout": "5s", + "retries": 5 + } + } + }, + { + "name": "umami", + "image": "ghcr.io/umami-software/umami:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [ + "db" + ], + "frontend_network": true, + "private_network": true, + "compose": { + "image": "ghcr.io/umami-software/umami:latest", + "ports": [ + "3000:3000" + ], + "environment": { + "DATABASE_URL": "postgresql://umami:${GENERATED_DB_PASSWORD}@db:5432/umami", + "APP_SECRET": "${GENERATED_APP_SECRET}", + "TWO_FACTOR_ENCRYPTION_KEY": "${GENERATED_TWO_FACTOR_ENCRYPTION_KEY}" + }, + "depends_on": { + "db": { + "condition": "service_healthy" + } + }, + "init": true, + "restart": "always", + "healthcheck": { + "test": [ + "CMD-SHELL", + "curl http://localhost:3000/api/heartbeat" + ], + "interval": "5s", + "timeout": "5s", + "retries": 5 + } + } + } + ], + "top_level": { + "name": "umami", + "volumes": { + "umami-db-data": {} + } + }, + "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-replace-compose-service-dns", + "dependency_external_access": "disabled", + "prompt_user_for_private_network": false + }, + "storage": [ + { + "id": "umami-database-data", + "service": "db", + "container_path": "/var/lib/postgresql/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "default": "managed-volume", + "installation_choice": [ + "managed-volume", + "host-bind" + ] + } + ], + "orchestration": { + "reserve_vmids_atomically": 2, + "start_order": [ + "db", + "umami" + ], + "stop_order": [ + "umami", + "db" + ], + "dependency_readiness": "postgres-healthcheck-before-application", + "rollback_on_failure": "preserve-created-containers-and-volumes-for-diagnosis" + }, + "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": "db-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "db", + "environment_variable": "POSTGRES_PASSWORD" + }, + { + "service": "umami", + "environment_variable": "DATABASE_URL" + } + ] + }, + { + "id": "app-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "umami", + "environment_variable": "APP_SECRET" + } + ] + }, + { + "id": "two-factor-encryption-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "umami", + "environment_variable": "TWO_FACTOR_ENCRYPTION_KEY" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Web UI", + "scheme": "http", + "port": 3000, + "path": "/", + "source": "official-docker-compose" + } + ], + "credentials": [ + { + "label": "Initial Umami administrator", + "type": "static-default", + "username": "admin", + "password": "umami", + "change_required": true, + "source": "official-documentation", + "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-zfs", + "volume_storage": "local-zfs", + "template_storage": "local", + "bridge": "vmbr0", + "ipv4": "dhcp", + "firewall": true, + "host_managed_network": true, + "onboot": false, + "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": "import-from-oci-image" + }, + "adaptations": [ + { + "id": "compose-dependency-network", + "upstream_behavior": "Docker Compose resolves the db service name on the project network and waits until PostgreSQL is healthy.", + "native_lxc_behavior": "ProxMenux creates a private Proxmox bridge, gives each service its own LXC, maps the db alias, and waits for PostgreSQL readiness before first starting Umami.", + "reason": "The two official Compose services run in separate native OCI LXCs.", + "behavioral_impact": "PostgreSQL stays on the private service network and has no published port.", + "validation": "passed" + }, + { + "id": "generated-compose-secrets", + "upstream_behavior": "The official Compose file uses example values for APP_SECRET, TWO_FACTOR_ENCRYPTION_KEY, and PostgreSQL credentials.", + "native_lxc_behavior": "ProxMenux generates a unique PostgreSQL password, APP_SECRET, and a 64-character hexadecimal two-factor key for each installation.", + "reason": "Do not reuse example credentials between installations.", + "behavioral_impact": "The administrator still changes Umami’s documented initial admin password after first sign-in.", + "validation": "passed" + }, + { + "id": "compose-healthcheck", + "upstream_behavior": "The official Compose file probes /api/heartbeat every five seconds for Umami and uses pg_isready for PostgreSQL.", + "native_lxc_behavior": "The stack waits for PostgreSQL readiness, then ProxMenux performs its standard first-start HTTP check at the Umami web root. It does not install a continuous health monitor.", + "reason": "The current generic installer uses the application endpoint for its first-start check.", + "behavioral_impact": "Runtime validation should separately check /api/heartbeat and the interactive login.", + "validation": "passed" + } + ], + "installer_profile": { + "stack_driver": "generic-multi-lxc-stack" + }, + "security_profile": { + "requires_privileged_lxc": false, + "source_requests_privileged_lxc": false, + "optional_privileged_lxc": false, + "requires_host_pid_namespace": false, + "source_requests_relaxed_confinement": false, + "requires_relaxed_confinement": false, + "optional_relaxed_confinement": false, + "risk_level": "normal", + "confirmation_required": false, + "warning": "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement." + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "depends_on", + "environment", + "healthcheck", + "image", + "init", + "ports", + "restart", + "volumes" + ], + "untranslated_blockers": [], + "policy": "The official two-service Compose model is represented as two native OCI LXCs with a private PostgreSQL dependency and managed database storage. ProxMenux applies a first-start web-root check; validate the official heartbeat endpoint and login separately." + }, + "validation": { + "schema": "passed", + "clean_install": "passed", + "service_health": "passed", + "restart_persistence": "passed", + "backup_restore": "pending", + "update_preserves_data": "pending", + "remove": "passed", + "advanced_configuration": "pending", + "storage_modes": "pending", + "modify": "pending" + }, + "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 + } + } +} diff --git a/oci/catalog/categories.json b/oci/catalog/categories.json index 8c36b757..fe44f55b 100644 --- a/oci/catalog/categories.json +++ b/oci/catalog/categories.json @@ -380,6 +380,7 @@ "tvheadend": "media", "typesense": "databases", "ubooquity": "media", + "umami": "monitoring", "ungoogled-chromium": "desktop", "unifi-network-application": "network", "unpackerr": "arr", diff --git a/oci/catalog/curated/umami.json b/oci/catalog/curated/umami.json new file mode 100644 index 00000000..0df02f56 --- /dev/null +++ b/oci/catalog/curated/umami.json @@ -0,0 +1,428 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-umami", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Umami" + }, + "tagline": { + "en_US": "Privacy-focused, self-hosted web analytics." + }, + "description": { + "en_US": "Umami is a privacy-focused web analytics platform. This recipe uses the official Umami image with a separate PostgreSQL LXC for persistent analytics data." + }, + "category": "monitoring", + "category_label": "Monitoring & Analytics", + "author": "Umami Software", + "developer": "Umami Software", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/umami.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 3000, + "path": "/" + }, + "website": "https://umami.is/", + "documentation": "https://docs.umami.is/docs/install", + "repository": "https://github.com/umami-software/umami", + "tips": [ + "The initial administrator login is admin / umami. Change the password immediately after the first sign-in.", + "Umami runs in one LXC and PostgreSQL in a separate private LXC. The PostgreSQL managed volume stores analytics data; the Umami service has no data volume.", + "The official Compose healthcheck probes /api/heartbeat. ProxMenux performs its standard first-start HTTP check against the web root; verify /api/heartbeat and sign-in separately during runtime validation.", + "The Compose file uses official Umami and PostgreSQL images. ProxMenux generates a unique database password, APP_SECRET, and 64-character TWO_FACTOR_ENCRYPTION_KEY." + ], + "mini_changelog": [], + "display_version": null, + "updated_at": "2026-10-23" + }, + "source": { + "provider": "umami", + "repository": "https://github.com/umami-software/umami", + "default_branch": "master", + "revision": "559e3c607c23d2c72d9bd4f6481bda428255323c", + "image_repository_url": "https://github.com/umami-software/umami/pkgs/container/umami", + "readme_pushed_at": "", + "compose_sha256": "fb542a641c14fc2b1ff0d59558a197ed4f607b6608bf812afd20dad662ae0e8c", + "generated_at": "2026-10-27T09:38:05+00:00", + "readme_raw_url": "https://raw.githubusercontent.com/umami-software/umami/559e3c607c23d2c72d9bd4f6481bda428255323c/docker-compose.yml" + }, + "container_contract": { + "service_name": "umami", + "container_name": "umami", + "image": { + "reference": "ghcr.io/umami-software/umami:latest", + "registry": "ghcr.io", + "repository": "ghcr.io/umami-software/umami", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "DATABASE_URL", + "example": "postgresql://umami:${GENERATED_DB_PASSWORD}@db:5432/umami", + "required": true, + "sensitive": true, + "source": "official-docker-compose" + }, + { + "name": "APP_SECRET", + "example": "${GENERATED_APP_SECRET}", + "required": true, + "sensitive": true, + "source": "official-documentation" + }, + { + "name": "TWO_FACTOR_ENCRYPTION_KEY", + "example": "${GENERATED_TWO_FACTOR_ENCRYPTION_KEY}", + "required": true, + "sensitive": true, + "source": "official-docker-compose" + } + ], + "volumes": [], + "ports": [ + { + "container_port": 3000, + "published_example": 3000, + "protocol": "tcp", + "required": true, + "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat" + } + ], + "related_services": [ + { + "name": "db", + "image": "postgres:15-alpine" + } + ], + "restart": "always", + "stop_grace_period": null, + "original_compose": "---\nservices:\n umami:\n image: ghcr.io/umami-software/umami:latest\n ports:\n - \"3000:3000\"\n environment:\n DATABASE_URL: postgresql://umami:umami@db:5432/umami\n APP_SECRET: replace-me-with-a-random-string\n # Required for two-factor authentication, generate with: openssl rand -hex 32\n TWO_FACTOR_ENCRYPTION_KEY: replace-me-with-a-64-character-hex-string\n depends_on:\n db:\n condition: service_healthy\n init: true\n restart: always\n healthcheck:\n test: [\"CMD-SHELL\", \"curl http://localhost:3000/api/heartbeat\"]\n interval: 5s\n timeout: 5s\n retries: 5\n db:\n image: postgres:15-alpine\n environment:\n POSTGRES_DB: umami\n POSTGRES_USER: umami\n POSTGRES_PASSWORD: umami\n volumes:\n - umami-db-data:/var/lib/postgresql/data\n restart: always\n healthcheck:\n test: [\"CMD-SHELL\", \"pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}\"]\n interval: 5s\n timeout: 5s\n retries: 5\nvolumes:\n umami-db-data:\n" + }, + "compose_stack": { + "project_name": "umami", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "umami", + "service_count": 2, + "services": [ + { + "name": "db", + "image": "postgres:15-alpine", + "is_main": false, + "role": "dependency", + "vmid_offset": 1, + "depends_on": [], + "frontend_network": false, + "private_network": true, + "compose": { + "image": "postgres:15-alpine", + "environment": { + "POSTGRES_DB": "umami", + "POSTGRES_USER": "umami", + "POSTGRES_PASSWORD": "${GENERATED_DB_PASSWORD}" + }, + "volumes": [ + "umami-db-data:/var/lib/postgresql/data" + ], + "restart": "always", + "healthcheck": { + "test": [ + "CMD-SHELL", + "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}" + ], + "interval": "5s", + "timeout": "5s", + "retries": 5 + } + } + }, + { + "name": "umami", + "image": "ghcr.io/umami-software/umami:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [ + "db" + ], + "frontend_network": true, + "private_network": true, + "compose": { + "image": "ghcr.io/umami-software/umami:latest", + "ports": [ + "3000:3000" + ], + "environment": { + "DATABASE_URL": "postgresql://umami:${GENERATED_DB_PASSWORD}@db:5432/umami", + "APP_SECRET": "${GENERATED_APP_SECRET}", + "TWO_FACTOR_ENCRYPTION_KEY": "${GENERATED_TWO_FACTOR_ENCRYPTION_KEY}" + }, + "depends_on": { + "db": { + "condition": "service_healthy" + } + }, + "init": true, + "restart": "always", + "healthcheck": { + "test": [ + "CMD-SHELL", + "curl http://localhost:3000/api/heartbeat" + ], + "interval": "5s", + "timeout": "5s", + "retries": 5 + } + } + } + ], + "top_level": { + "name": "umami", + "volumes": { + "umami-db-data": {} + } + }, + "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-replace-compose-service-dns", + "dependency_external_access": "disabled", + "prompt_user_for_private_network": false + }, + "storage": [ + { + "id": "umami-database-data", + "service": "db", + "container_path": "/var/lib/postgresql/data", + "mode": "managed-volume", + "user_selectable": true, + "backup": true, + "shared_with_other_lxc": false, + "default": "managed-volume", + "installation_choice": [ + "managed-volume", + "host-bind" + ] + } + ], + "orchestration": { + "reserve_vmids_atomically": 2, + "start_order": [ + "db", + "umami" + ], + "stop_order": [ + "umami", + "db" + ], + "dependency_readiness": "postgres-healthcheck-before-application", + "rollback_on_failure": "preserve-created-containers-and-volumes-for-diagnosis" + }, + "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": "db-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "db", + "environment_variable": "POSTGRES_PASSWORD" + }, + { + "service": "umami", + "environment_variable": "DATABASE_URL" + } + ] + }, + { + "id": "app-secret", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "umami", + "environment_variable": "APP_SECRET" + } + ] + }, + { + "id": "two-factor-encryption-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "umami", + "environment_variable": "TWO_FACTOR_ENCRYPTION_KEY" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Web UI", + "scheme": "http", + "port": 3000, + "path": "/", + "source": "official-docker-compose" + } + ], + "credentials": [ + { + "label": "Initial Umami administrator", + "type": "static-default", + "username": "admin", + "password": "umami", + "change_required": true, + "source": "official-documentation", + "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-zfs", + "volume_storage": "local-zfs", + "template_storage": "local", + "bridge": "vmbr0", + "ipv4": "dhcp", + "firewall": true, + "host_managed_network": true, + "onboot": false, + "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": "import-from-oci-image" + }, + "adaptations": [ + { + "id": "compose-dependency-network", + "upstream_behavior": "Docker Compose resolves the db service name on the project network and waits until PostgreSQL is healthy.", + "native_lxc_behavior": "ProxMenux creates a private Proxmox bridge, gives each service its own LXC, maps the db alias, and waits for PostgreSQL readiness before first starting Umami.", + "reason": "The two official Compose services run in separate native OCI LXCs.", + "behavioral_impact": "PostgreSQL stays on the private service network and has no published port.", + "validation": "passed" + }, + { + "id": "generated-compose-secrets", + "upstream_behavior": "The official Compose file uses example values for APP_SECRET, TWO_FACTOR_ENCRYPTION_KEY, and PostgreSQL credentials.", + "native_lxc_behavior": "ProxMenux generates a unique PostgreSQL password, APP_SECRET, and a 64-character hexadecimal two-factor key for each installation.", + "reason": "Do not reuse example credentials between installations.", + "behavioral_impact": "The administrator still changes Umami’s documented initial admin password after first sign-in.", + "validation": "passed" + }, + { + "id": "compose-healthcheck", + "upstream_behavior": "The official Compose file probes /api/heartbeat every five seconds for Umami and uses pg_isready for PostgreSQL.", + "native_lxc_behavior": "The stack waits for PostgreSQL readiness, then ProxMenux performs its standard first-start HTTP check at the Umami web root. It does not install a continuous health monitor.", + "reason": "The current generic installer uses the application endpoint for its first-start check.", + "behavioral_impact": "Runtime validation should separately check /api/heartbeat and the interactive login.", + "validation": "passed" + } + ], + "installer_profile": { + "stack_driver": "generic-multi-lxc-stack" + }, + "security_profile": { + "requires_privileged_lxc": false, + "source_requests_privileged_lxc": false, + "optional_privileged_lxc": false, + "requires_host_pid_namespace": false, + "source_requests_relaxed_confinement": false, + "requires_relaxed_confinement": false, + "optional_relaxed_confinement": false, + "risk_level": "normal", + "confirmation_required": false, + "warning": "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement." + } + }, + "compatibility": { + "automatic_install_candidate": true, + "validated": false, + "supported_compose_keys": [ + "depends_on", + "environment", + "healthcheck", + "image", + "init", + "ports", + "restart", + "volumes" + ], + "untranslated_blockers": [], + "policy": "The official two-service Compose model is represented as two native OCI LXCs with a private PostgreSQL dependency and managed database storage. ProxMenux applies a first-start web-root check; validate the official heartbeat endpoint and login separately." + }, + "validation": { + "schema": "passed", + "clean_install": "passed", + "service_health": "passed", + "restart_persistence": "passed", + "backup_restore": "pending", + "update_preserves_data": "pending", + "remove": "passed", + "advanced_configuration": "pending", + "storage_modes": "pending", + "modify": "pending" + }, + "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 + } + } +} diff --git a/oci/catalog/index.json b/oci/catalog/index.json index 940b0c03..7c134b53 100644 --- a/oci/catalog/index.json +++ b/oci/catalog/index.json @@ -16501,6 +16501,39 @@ "hidden": false, "template_id": "linuxserver-ubooquity" }, + { + "id": "umami", + "provider": "umami", + "template_family": "curated-profile", + "title": "Umami", + "repository": "https://github.com/umami-software/umami", + "description": "Umami is a privacy-focused web analytics platform. This recipe uses the official Umami image with a separate PostgreSQL LXC for persistent analytics data.", + "website": "https://umami.is/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/umami.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": "2026-10-23", + "main_image": "ghcr.io/umami-software/umami:latest", + "category": "monitoring", + "category_label": "Monitoring & Analytics", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/umami.json", + "template": "apps/umami.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, + "template_id": "image-umami" + }, { "id": "ungoogled-chromium", "provider": "linuxserver.io",