diff --git a/oci/VALIDATION.md b/oci/VALIDATION.md index 339a016d..9e760381 100644 --- a/oci/VALIDATION.md +++ b/oci/VALIDATION.md @@ -2,7 +2,7 @@ -43 of 336 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 334 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/bitcoin-knots.json b/oci/catalog/apps/bitcoin-knots.json index 17d8854c..1d059d4a 100644 --- a/oci/catalog/apps/bitcoin-knots.json +++ b/oci/catalog/apps/bitcoin-knots.json @@ -48,7 +48,9 @@ } ], "display_version": null, - "updated_at": "2026-04-03" + "updated_at": "2026-04-03", + "hidden": true, + "hidden_reason": "Its image stops without closing the application cleanly, also under Docker; hidden until the image is fixed" }, "source": { "provider": "linuxserver.io", diff --git a/oci/catalog/apps/duplicati-official.json b/oci/catalog/apps/duplicati-official.json new file mode 100644 index 00000000..a9850ac6 --- /dev/null +++ b/oci/catalog/apps/duplicati-official.json @@ -0,0 +1,555 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-duplicati-official", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Duplicati Official" + }, + "tagline": { + "en_US": "Official Duplicati image: encrypted, incremental backups to local, network and cloud storage." + }, + "description": { + "en_US": "Official Duplicati image: encrypted, incremental backups to local, network and cloud storage." + }, + "category": "backup", + "category_label": "Backup & Recovery", + "author": "Duplicati", + "developer": "Duplicati", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/duplicati.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8200, + "path": "/" + }, + "website": "https://www.duplicati.com/", + "documentation": "https://docs.duplicati.com/", + "repository": "https://github.com/duplicati/duplicati", + "tips": [], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "duplicati", + "repository": "https://github.com/duplicati/duplicati", + "revision": "66c05069de88ccb6054907665ef7813e0cf664f4e1a2032cd943a1b8d72e8530", + "image_repository_url": "https://hub.docker.com/r/duplicati/duplicati", + "readme_pushed_at": "", + "compose_sha256": "66c05069de88ccb6054907665ef7813e0cf664f4e1a2032cd943a1b8d72e8530", + "generated_at": "2026-10-11T09:05:05+00:00" + }, + "container_contract": { + "service_name": "duplicati", + "container_name": "duplicati", + "image": { + "reference": "duplicati/duplicati:latest", + "registry": "docker.io", + "repository": "duplicati/duplicati", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "SETTINGS_ENCRYPTION_KEY", + "example": "", + "required": true, + "sensitive": true, + "source": "docker-compose" + }, + { + "name": "DUPLICATI__WEBSERVICE_PASSWORD", + "example": "", + "required": true, + "sensitive": true, + "source": "docker-compose", + "prompt": "Password of the Duplicati web interface" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "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": 8 + } + }, + { + "id": "volume-1", + "container_path": "/backups", + "compose_source_example": "./backups", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + }, + { + "id": "volume-2", + "container_path": "/source", + "compose_source_example": "./source", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 8200, + "published_example": 8200, + "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 duplicati:\n image: duplicati/duplicati:latest\n container_name: duplicati\n ports:\n - 8200:8200\n volumes:\n - ./data:/data\n - ./backups:/backups\n - ./source:/source\n environment:\n - SETTINGS_ENCRYPTION_KEY=${GENERATED_SETTINGS_ENCRYPTION_KEY}\n - DUPLICATI__WEBSERVICE_PASSWORD=${GENERATED_DUPLICATI_WEBSERVICE_PASSWORD}\n restart: unless-stopped\n" + }, + "compose_stack": { + "project_name": "duplicati", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "duplicati", + "service_count": 1, + "services": [ + { + "name": "duplicati", + "image": "duplicati/duplicati:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "duplicati/duplicati:latest", + "container_name": "duplicati", + "ports": [ + "8200:8200" + ], + "volumes": [ + "./data:/data", + "./backups:/backups", + "./source:/source" + ], + "environment": [ + "SETTINGS_ENCRYPTION_KEY=${GENERATED_SETTINGS_ENCRYPTION_KEY}", + "DUPLICATI__WEBSERVICE_PASSWORD=${GENERATED_DUPLICATI_WEBSERVICE_PASSWORD}" + ], + "restart": "unless-stopped" + } + } + ], + "top_level": {}, + "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": "duplicati-volume-0", + "service": "duplicati", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "duplicati-volume-1", + "service": "duplicati", + "container_path": "/backups", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "duplicati-volume-2", + "service": "duplicati", + "container_path": "/source", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "duplicati" + ], + "stop_order": [ + "duplicati" + ], + "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": "duplicati-webservice-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "duplicati", + "environment_variable": "DUPLICATI__WEBSERVICE_PASSWORD" + } + ] + }, + { + "id": "settings-encryption-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "duplicati", + "environment_variable": "SETTINGS_ENCRYPTION_KEY" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Web UI", + "scheme": "http", + "port": 8200, + "path": "/", + "source": "compose-metadata" + } + ], + "credentials": [ + { + "label": "Duplicati web interface (password only)", + "type": "configured-or-installer-generated", + "username": "Not required (password only)", + "password": null, + "password_environment": "DUPLICATI__WEBSERVICE_PASSWORD", + "change_required": false, + "source": "duplicati-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-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" + }, + "installer_profile": { + "generated_sensitive_environment": { + "SETTINGS_ENCRYPTION_KEY": { + "strategy": "token-hex", + "bytes": 16, + "prompt": false + }, + "DUPLICATI__WEBSERVICE_PASSWORD": { + "strategy": "token-hex", + "bytes": 12, + "prompt": true + } + }, + "volume_preparations": [ + { + "container_path": "/data", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + }, + { + "container_path": "/backups", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + }, + { + "container_path": "/source", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + } + ] + }, + "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" + } + ] + }, + "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": "pending", + "service_health": "pending", + "restart_persistence": "pending", + "backup_restore": "pending", + "update_preserves_data": "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 + } +} diff --git a/oci/catalog/apps/emulatorjs.json b/oci/catalog/apps/emulatorjs.json index 13718c52..d925f08a 100644 --- a/oci/catalog/apps/emulatorjs.json +++ b/oci/catalog/apps/emulatorjs.json @@ -35,7 +35,9 @@ "tips": [], "mini_changelog": [], "display_version": null, - "updated_at": null + "updated_at": null, + "hidden": true, + "hidden_reason": "Its image no longer publishes a version for any architecture; hidden until it does" }, "source": { "provider": "linuxserver.io", diff --git a/oci/catalog/apps/sickchill.json b/oci/catalog/apps/sickchill.json index 0553892c..0de62bd3 100644 --- a/oci/catalog/apps/sickchill.json +++ b/oci/catalog/apps/sickchill.json @@ -35,7 +35,9 @@ "tips": [], "mini_changelog": [], "display_version": null, - "updated_at": null + "updated_at": null, + "hidden": true, + "hidden_reason": "Its image no longer publishes a version for any architecture; hidden until it does" }, "source": { "provider": "linuxserver.io", @@ -302,7 +304,7 @@ "unprivileged": true, "ostype": "auto-from-image", "cores": 2, - "memory_mb": 128, + "memory_mb": 1024, "swap_mb": 512, "rootfs_size_gb": 8, "rootfs_storage": "local-lvm", diff --git a/oci/catalog/curated/duplicati-official.json b/oci/catalog/curated/duplicati-official.json new file mode 100644 index 00000000..a9850ac6 --- /dev/null +++ b/oci/catalog/curated/duplicati-official.json @@ -0,0 +1,555 @@ +{ + "schema_version": "0.5.0", + "kind": "proxmenux.oci-template", + "id": "image-duplicati-official", + "status": "generated-unvalidated", + "catalog_ui": { + "title": { + "en_US": "Duplicati Official" + }, + "tagline": { + "en_US": "Official Duplicati image: encrypted, incremental backups to local, network and cloud storage." + }, + "description": { + "en_US": "Official Duplicati image: encrypted, incremental backups to local, network and cloud storage." + }, + "category": "backup", + "category_label": "Backup & Recovery", + "author": "Duplicati", + "developer": "Duplicati", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/duplicati.webp", + "thumbnail": null, + "screenshots": [], + "architectures": [ + "amd64", + "arm64" + ], + "launch": { + "scheme": "http", + "port": 8200, + "path": "/" + }, + "website": "https://www.duplicati.com/", + "documentation": "https://docs.duplicati.com/", + "repository": "https://github.com/duplicati/duplicati", + "tips": [], + "mini_changelog": [], + "display_version": null, + "updated_at": null + }, + "source": { + "provider": "duplicati", + "repository": "https://github.com/duplicati/duplicati", + "revision": "66c05069de88ccb6054907665ef7813e0cf664f4e1a2032cd943a1b8d72e8530", + "image_repository_url": "https://hub.docker.com/r/duplicati/duplicati", + "readme_pushed_at": "", + "compose_sha256": "66c05069de88ccb6054907665ef7813e0cf664f4e1a2032cd943a1b8d72e8530", + "generated_at": "2026-10-11T09:05:05+00:00" + }, + "container_contract": { + "service_name": "duplicati", + "container_name": "duplicati", + "image": { + "reference": "duplicati/duplicati:latest", + "registry": "docker.io", + "repository": "duplicati/duplicati", + "tag": "latest", + "digest": null, + "pull_policy": "resolve-written-reference-to-architecture-digest-at-install" + }, + "environment": [ + { + "name": "SETTINGS_ENCRYPTION_KEY", + "example": "", + "required": true, + "sensitive": true, + "source": "docker-compose" + }, + { + "name": "DUPLICATI__WEBSERVICE_PASSWORD", + "example": "", + "required": true, + "sensitive": true, + "source": "docker-compose", + "prompt": "Password of the Duplicati web interface" + } + ], + "volumes": [ + { + "id": "volume-0", + "container_path": "/data", + "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": 8 + } + }, + { + "id": "volume-1", + "container_path": "/backups", + "compose_source_example": "./backups", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + }, + { + "id": "volume-2", + "container_path": "/source", + "compose_source_example": "./source", + "read_only": false, + "required": true, + "installation_choice": [ + "managed-volume", + "host-bind" + ], + "default": "managed-volume", + "managed_volume": { + "backup": true, + "default_size_gb": 8 + } + } + ], + "ports": [ + { + "container_port": 8200, + "published_example": 8200, + "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 duplicati:\n image: duplicati/duplicati:latest\n container_name: duplicati\n ports:\n - 8200:8200\n volumes:\n - ./data:/data\n - ./backups:/backups\n - ./source:/source\n environment:\n - SETTINGS_ENCRYPTION_KEY=${GENERATED_SETTINGS_ENCRYPTION_KEY}\n - DUPLICATI__WEBSERVICE_PASSWORD=${GENERATED_DUPLICATI_WEBSERVICE_PASSWORD}\n restart: unless-stopped\n" + }, + "compose_stack": { + "project_name": "duplicati", + "deployment_model": "one-native-oci-lxc-per-compose-service", + "user_experience": "single-application-install", + "main_service": "duplicati", + "service_count": 1, + "services": [ + { + "name": "duplicati", + "image": "duplicati/duplicati:latest", + "is_main": true, + "role": "frontend", + "vmid_offset": 0, + "depends_on": [], + "frontend_network": true, + "private_network": false, + "compose": { + "image": "duplicati/duplicati:latest", + "container_name": "duplicati", + "ports": [ + "8200:8200" + ], + "volumes": [ + "./data:/data", + "./backups:/backups", + "./source:/source" + ], + "environment": [ + "SETTINGS_ENCRYPTION_KEY=${GENERATED_SETTINGS_ENCRYPTION_KEY}", + "DUPLICATI__WEBSERVICE_PASSWORD=${GENERATED_DUPLICATI_WEBSERVICE_PASSWORD}" + ], + "restart": "unless-stopped" + } + } + ], + "top_level": {}, + "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": "duplicati-volume-0", + "service": "duplicati", + "container_path": "/data", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "duplicati-volume-1", + "service": "duplicati", + "container_path": "/backups", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + }, + { + "id": "duplicati-volume-2", + "service": "duplicati", + "container_path": "/source", + "mode": "managed-volume", + "user_selectable": false, + "backup": true, + "shared_with_other_lxc": false, + "source_path": null, + "source_path_prompt": null + } + ], + "orchestration": { + "reserve_vmids_atomically": 1, + "start_order": [ + "duplicati" + ], + "stop_order": [ + "duplicati" + ], + "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": "duplicati-webservice-password", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "duplicati", + "environment_variable": "DUPLICATI__WEBSERVICE_PASSWORD" + } + ] + }, + { + "id": "settings-encryption-key", + "strategy": "generate-cryptographically-random-at-install", + "bindings": [ + { + "service": "duplicati", + "environment_variable": "SETTINGS_ENCRYPTION_KEY" + } + ] + } + ] + } + }, + "first_run": { + "endpoints": [ + { + "label": "Web UI", + "scheme": "http", + "port": 8200, + "path": "/", + "source": "compose-metadata" + } + ], + "credentials": [ + { + "label": "Duplicati web interface (password only)", + "type": "configured-or-installer-generated", + "username": "Not required (password only)", + "password": null, + "password_environment": "DUPLICATI__WEBSERVICE_PASSWORD", + "change_required": false, + "source": "duplicati-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-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" + }, + "installer_profile": { + "generated_sensitive_environment": { + "SETTINGS_ENCRYPTION_KEY": { + "strategy": "token-hex", + "bytes": 16, + "prompt": false + }, + "DUPLICATI__WEBSERVICE_PASSWORD": { + "strategy": "token-hex", + "bytes": 12, + "prompt": true + } + }, + "volume_preparations": [ + { + "container_path": "/data", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + }, + { + "container_path": "/backups", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + }, + { + "container_path": "/source", + "remove_lost_found": true, + "owner_strategy": "mapped-application-user", + "only_when_mount_type": "managed-volume" + } + ] + }, + "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" + } + ] + }, + "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": "pending", + "service_health": "pending", + "restart_persistence": "pending", + "backup_restore": "pending", + "update_preserves_data": "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 + } +} diff --git a/oci/catalog/generation-report-curated.json b/oci/catalog/generation-report-curated.json index a08de59b..c797e035 100644 --- a/oci/catalog/generation-report-curated.json +++ b/oci/catalog/generation-report-curated.json @@ -1,5 +1,6 @@ { "generated": [ + "duplicati-official", "emby-official", "fileflows", "frigate", @@ -21,7 +22,7 @@ "stremio", "tandoor" ], - "generated_count": 20, + "generated_count": 21, "failed": [], "failed_count": 0 } diff --git a/oci/catalog/index.json b/oci/catalog/index.json index 3874c542..6a73ff8b 100644 --- a/oci/catalog/index.json +++ b/oci/catalog/index.json @@ -921,7 +921,7 @@ "requires_relaxed_confinement": false, "optional_relaxed_confinement": false, "requires_security_confirmation": false, - "hidden": false, + "hidden": true, "template_id": "linuxserver-bitcoin-knots" }, { @@ -2608,6 +2608,38 @@ "hidden": false, "template_id": "linuxserver-duplicati" }, + { + "id": "duplicati-official", + "provider": "duplicati", + "template_family": "curated-profile", + "title": "Duplicati Official", + "repository": "https://github.com/duplicati/duplicati", + "description": "Official Duplicati image: encrypted, incremental backups to local, network and cloud storage.", + "website": "https://www.duplicati.com/", + "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/duplicati.webp", + "architectures": [ + "amd64", + "arm64" + ], + "updated_at": null, + "main_image": "duplicati/duplicati:latest", + "category": "backup", + "category_label": "Backup & Recovery", + "replaces_discovered_ids": [], + "curated_path": "catalog/curated/duplicati-official.json", + "template": "apps/duplicati-official.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, + "hidden": false, + "template_id": "image-duplicati-official" + }, { "id": "eden", "provider": "linuxserver.io", @@ -2775,7 +2807,7 @@ "requires_relaxed_confinement": false, "optional_relaxed_confinement": false, "requires_security_confirmation": false, - "hidden": false, + "hidden": true, "template_id": "image-emulatorjs" }, { @@ -9861,7 +9893,7 @@ "requires_relaxed_confinement": false, "optional_relaxed_confinement": false, "requires_security_confirmation": false, - "hidden": false, + "hidden": true, "template_id": "image-sickchill" }, { diff --git a/oci/catalog/overlays/bitcoin-knots.json b/oci/catalog/overlays/bitcoin-knots.json index b1cbf5eb..35fae84d 100644 --- a/oci/catalog/overlays/bitcoin-knots.json +++ b/oci/catalog/overlays/bitcoin-knots.json @@ -1,4 +1,8 @@ { + "catalog_ui": { + "hidden": true, + "hidden_reason": "Its image stops without closing the application cleanly, also under Docker; hidden until the image is fixed" + }, "proxmox": { "installer_profile": { "volume_preparations": [ diff --git a/oci/catalog/overlays/emulatorjs.json b/oci/catalog/overlays/emulatorjs.json index cb0e91f2..b7e675e6 100644 --- a/oci/catalog/overlays/emulatorjs.json +++ b/oci/catalog/overlays/emulatorjs.json @@ -1,4 +1,8 @@ { + "catalog_ui": { + "hidden": true, + "hidden_reason": "Its image no longer publishes a version for any architecture; hidden until it does" + }, "proxmox": { "installer_profile": { "volume_preparations": [ diff --git a/oci/catalog/overlays/sickchill.json b/oci/catalog/overlays/sickchill.json index f9414092..787b1187 100644 --- a/oci/catalog/overlays/sickchill.json +++ b/oci/catalog/overlays/sickchill.json @@ -1,4 +1,8 @@ { + "catalog_ui": { + "hidden": true, + "hidden_reason": "Its image no longer publishes a version for any architecture; hidden until it does" + }, "proxmox": { "installer_profile": { "volume_preparations": [