Add Umami OCI application profile

This commit is contained in:
Jonatan
2026-10-27 11:32:59 +01:00
parent 688b7f54bc
commit 8f8a80785a
12 changed files with 933 additions and 8 deletions
+428
View File
@@ -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
}
}
}