Files
ProxMenux/oci/catalog/apps/rclone.json
T
MacRimiandClaude Opus 5.5 4437a671d2 ProxMenux 1.2.6.2-beta: OCI containers in the Monitor, docs and fixes
OCI manager Apps
- App tab: containers installed from an OCI image are identified from their
  installation record; the application and image versions are shown and an
  update is detected by image digest; repository link; Refresh data.
- Updates tab for OCI containers: Update and Recreate run the same flow as the
  OCI menu in the Monitor terminal; the pre-update backup can be kept in a
  backup storage; scheduled image updates with an optional minimum age.
- Logs tab: console output of the application, kept on the host
  (lxc.console.logfile + logrotate) and followed live.
- The Proxmox console opens a shell (cmode: shell) when the image has one.
- A damaged image download is fetched again before failing.
- Multi-container applications open at their LAN address; volume mount
  points on block storage report their usage.

Monitor
- Proxmox notifications are delivered to a loopback-only HTTP listener when
  HTTPS is enabled, so they no longer fail certificate verification.
- Log persistence counts recurring patterns only; an ended burst is not
  reported as persistent and its warning clears on its own (#386).
- Proxmox notification config backups are deduplicated and capped at three.
- The update icon on the Apps page opens the container on its Updates tab.
- Version 1.2.6.2-beta and its release notes in every Monitor language.

Docs
- OCI manager Apps and Audit & Report rebuilt as per-page message files,
  with a new page for OCI containers in the Monitor.
- Seven pages fixed where rich-text tags were missing from t.rich.

Translations
- Spanish fixes across the OCI engine, the Monitor and the TUI menus.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 21:51:12 +02:00

947 lines
41 KiB
JSON

{
"schema_version": "0.5.0",
"kind": "proxmenux.oci-template",
"id": "image-rclone",
"status": "laboratory-validated",
"catalog_ui": {
"title": {
"en_US": "Rclone WebUI"
},
"tagline": {
"en_US": "Cloud storage synchronization and FUSE mounts"
},
"description": {
"en_US": "Official Rclone image adapted as a native Proxmox OCI LXC with persistent configuration, authenticated WebUI and optional FUSE publication for other LXCs."
},
"category": "backup",
"category_label": "Backup & Recovery",
"author": "Rclone",
"developer": "Rclone",
"icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/rclone.webp",
"thumbnail": null,
"screenshots": [],
"architectures": [
"amd64",
"arm64"
],
"launch": {
"scheme": "http",
"port": 5572,
"path": "/"
},
"website": "https://rclone.org/",
"documentation": "https://rclone.org/install/#docker-installation",
"repository": "https://github.com/rclone/rclone",
"tips": [
"The installer generates WebUI credentials; the user creates and authorizes their own remote.",
"FUSE publication is optional and requires a privileged LXC plus explicit Proxmox host adaptations."
],
"mini_changelog": [],
"display_version": null,
"updated_at": "2026-09-12"
},
"source": {
"provider": "rclone",
"repository": "https://github.com/rclone/rclone",
"revision": "049e11eb7df3d9b30e6e5d400945866db980041bdc0c50b4ede09e079b2e9f52",
"image_repository_url": "https://hub.docker.com/r/rclone/rclone",
"readme_pushed_at": "2026-09-12T11:36:50Z",
"compose_sha256": "049e11eb7df3d9b30e6e5d400945866db980041bdc0c50b4ede09e079b2e9f52",
"generated_at": "2026-09-12T15:54:10+00:00",
"default_branch": "master"
},
"container_contract": {
"service_name": "rclone",
"container_name": "rclone",
"image": {
"reference": "rclone/rclone:latest",
"registry": "docker.io",
"repository": "rclone/rclone",
"tag": "latest",
"digest": null,
"pull_policy": "resolve-selected-tag-to-architecture-digest-at-install"
},
"environment": [
{
"name": "XDG_CONFIG_HOME",
"example": "/config",
"required": true,
"sensitive": false,
"source": "docker-compose"
}
],
"volumes": [
{
"id": "volume-0",
"container_path": "/config/rclone",
"compose_source_example": "rclone-config",
"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": "/data",
"compose_source_example": "/mnt/oci-shared/rclone/data",
"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": 5572,
"published_example": 5572,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
},
{
"container_port": 5573,
"published_example": 5573,
"protocol": "tcp",
"required": true,
"proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
}
],
"related_services": [],
"restart": "unless-stopped",
"stop_grace_period": null,
"original_compose": "name: rclone\nservices:\n rclone:\n image: rclone/rclone:latest\n command:\n - gui\n - --no-open-browser\n - --addr=:5572\n - --api-addr=:5573\n - --config=/config/rclone/rclone.conf\n environment:\n XDG_CONFIG_HOME: /config\n ports:\n - 5572:5572\n - 5573:5573\n volumes:\n - rclone-config:/config/rclone\n - /mnt/oci-shared/rclone/data:/data\n devices:\n - /dev/fuse:/dev/fuse\n cap_add:\n - SYS_ADMIN\n security_opt:\n - apparmor:unconfined\n restart: unless-stopped\nvolumes:\n rclone-config: {}\n"
},
"compose_stack": {
"project_name": "rclone",
"deployment_model": "one-native-oci-lxc-per-compose-service",
"user_experience": "single-application-install",
"main_service": "rclone",
"service_count": 1,
"services": [
{
"name": "rclone",
"image": "rclone/rclone:latest",
"is_main": true,
"role": "frontend",
"vmid_offset": 0,
"depends_on": [],
"frontend_network": true,
"private_network": false,
"compose": {
"image": "rclone/rclone:latest",
"command": [
"gui",
"--no-open-browser",
"--addr=:5572",
"--api-addr=:5573",
"--config=/config/rclone/rclone.conf"
],
"environment": {
"XDG_CONFIG_HOME": "/config"
},
"ports": [
"5572:5572",
"5573:5573"
],
"volumes": [
"rclone-config:/config/rclone",
"/mnt/oci-shared/rclone/data:/data"
],
"devices": [
"/dev/fuse:/dev/fuse"
],
"cap_add": [
"SYS_ADMIN"
],
"security_opt": [
"apparmor:unconfined"
],
"restart": "unless-stopped"
}
}
],
"top_level": {
"name": "rclone",
"volumes": {
"rclone-config": {}
}
},
"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": "rclone-volume-0",
"service": "rclone",
"container_path": "/config/rclone",
"mode": "managed-volume",
"user_selectable": false,
"backup": true,
"shared_with_other_lxc": false,
"source_path": null,
"source_path_prompt": null
},
{
"id": "rclone-volume-1",
"service": "rclone",
"container_path": "/data",
"mode": "host-bind",
"user_selectable": true,
"backup": false,
"shared_with_other_lxc": true,
"source_path": null,
"source_path_prompt": "Host directory for rclone:/data"
}
],
"orchestration": {
"reserve_vmids_atomically": 1,
"start_order": [
"rclone"
],
"stop_order": [
"rclone"
],
"dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
"rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
},
"installer_inputs": {
"prompted": [
"stack_name",
"base_vmid",
"rootfs_storage",
"persistent_data_destinations",
"frontend_bridge",
"frontend_ipv4_mode"
],
"automatic": [
"dependent_vmids",
"private_bridge",
"private_subnet",
"private_service_addresses",
"compose_service_aliases",
"generated_secrets",
"dependency_start_and_stop_order"
],
"generated_secrets": []
}
},
"first_run": {
"endpoints": [
{
"label": "Rclone WebUI",
"scheme": "http",
"port": 5572,
"path": "/",
"source": "validated-laboratory-profile"
}
],
"credentials": [
{
"label": "Rclone WebUI",
"type": "configured-or-installer-generated",
"username": "admin",
"password": null,
"username_environment": "RCLONE_RC_USER",
"password_environment": "RCLONE_RC_PASS",
"change_required": false,
"source": "official-rclone-rc-environment",
"retrieval": null
}
]
},
"proxmox": {
"runtime": "native-oci-lxc",
"technology_status": "proxmox-technology-preview",
"catalog": {
"replaces_discovered_ids": []
},
"defaults": {
"unprivileged": false,
"privileged_required": true,
"ostype": "auto-from-image",
"cores": 1,
"memory_mb": 512,
"swap_mb": 256,
"rootfs_size_gb": 4,
"rootfs_storage": "local-lvm",
"volume_storage": "local-lvm",
"template_storage": "local",
"bridge": "vmbr0",
"ipv4": "dhcp",
"firewall": true,
"host_managed_network": true,
"onboot": true,
"features": [
"nesting=1",
"fuse=1"
],
"shutdown_timeout_seconds": 30
},
"image_metadata_policy": {
"entrypoint": "import-from-oci-image-or-reviewed-generated-wrapper",
"cmd": "apply-selected-rclone-mode",
"environment": "preserve-image-env-then-apply-user-values",
"user": "import-from-oci-image",
"working_dir": "import-from-oci-image",
"stop_signal": "import-from-oci-image"
},
"adaptations": [
{
"id": "fuse-inside-oci-lxc",
"upstream_behavior": "The official Rclone Docker deployment receives /dev/fuse and SYS_ADMIN to run rclone mount.",
"native_lxc_behavior": "Create a privileged Proxmox OCI LXC with features fuse=1; a minimal wrapper reads persistent RC credentials, waits for host-managed networking and execs the official Rclone binary as PID 1.",
"reason": "FUSE is the core function, and the OCI process can start before Proxmox finishes host-managed networking.",
"behavioral_impact": "Equivalent mount and RC behavior; credentials remain at the official persistent configuration boundary.",
"validation": "Passed on CT138 with gdrive:; official Rclone became PID 1 and the internal fuse.rclone mount survived stop/start."
},
{
"id": "publish-fuse-submount",
"upstream_behavior": "Docker can publish a FUSE submount through a bind configured with shared propagation.",
"native_lxc_behavior": "A Proxmox hook starts a transient one-shot waiter after post-start. The waiter clones the FUSE tree from the LXC mount namespace with open_tree, creates canonical read/write and recursive read-only views with mount_setattr, and publishes both in the host namespace with move_mount; pre-stop removes them.",
"reason": "LXC makes the container mount tree a slave of the host, so rshared alone cannot propagate a container-created mount back to the host.",
"behavioral_impact": "Namespace topology only; no Rclone process, remote protocol or application data is moved to the host.",
"validation": "Passed on Proxmox VE 9.2.11/kernel 7.0.14-14-pve: canonical read/write publication, recursive read-only publication, clean unpublish, republish, and simultaneous Plex/Jellyfin consumption."
},
{
"id": "consumer-parent-plus-exact-mounts",
"upstream_behavior": "Consumers bind the published Docker host path with the requested read/write policy.",
"native_lxc_behavior": "Read-only consumers receive the shared remotes-ro root first and one exact mpN from that same intrinsically read-only publication per selected remote second.",
"reason": "The parent mount carries future mount/unmount propagation while the exact mpN includes an already-published FUSE tree during consumer startup; the host publication itself enforces read-only after Rclone mount replacement.",
"behavioral_impact": "Equivalent consumer path with explicit Proxmox storage metadata.",
"validation": "Validated with CT131 Plex and CT128 Jellyfin before and after restarting CT138 Rclone; both exposed the remote through the recursive read-only publication."
}
],
"laboratory_contract": {
"requirements": {
"proxmox_min_version": "9.1",
"commands": [
"pct",
"pvesm",
"skopeo",
"curl",
"jq",
"openssl"
],
"features": [
"native-oci-lxc",
"privileged-lxc",
"fuse=1",
"documented-mount-propagation",
"managed-volume-backup",
"authenticated-webui"
],
"thin_pool_checks": {
"minimum_free_percent": 15,
"warn_when_virtual_allocation_exceeds_pool": true,
"require_autoextend_or_explicit_confirmation": true
},
"security": {
"privileged_container_warning": true,
"dedicated_service_lxc": true,
"never_expose_rc_webui_to_untrusted_networks": true,
"consumer_paths_read_only_by_default": true
}
},
"configuration_schema": {
"vmid": {
"type": "integer",
"required": false,
"default": null
},
"hostname": {
"type": "string",
"required": true,
"default": "rclone",
"validation": {
"pattern": "^[a-z0-9][a-z0-9-]{0,62}$"
}
},
"rootfs_storage": {
"type": "storage-selector",
"required": true,
"content_types": [
"rootdir"
],
"default": "local-lvm"
},
"rootfs_size_gb": {
"type": "integer",
"required": true,
"default": 4,
"minimum": 2
},
"config_storage": {
"type": "storage-selector",
"required": true,
"content_types": [
"rootdir"
],
"default": "local-lvm"
},
"config_size_gb": {
"type": "integer",
"required": true,
"default": 2,
"minimum": 1
},
"data_host_path": {
"type": "host-directory",
"required": true,
"default": "/mnt/oci-shared/rclone/data",
"create_if_missing": true,
"description": "Directorio local para operaciones copy y sync. No contiene la configuracion persistente."
},
"webui_username": {
"type": "string",
"required": true,
"default": "admin",
"validation": {
"pattern": "^[A-Za-z0-9._-]{1,64}$"
}
},
"webui_password": {
"type": "generated-password",
"required": true,
"generate_when_empty": true,
"minimum_length": 24,
"sensitive": true
},
"webui_port": {
"type": "port",
"required": true,
"default": 5572
},
"api_port": {
"type": "port",
"required": true,
"default": 5573
},
"bridge": {
"type": "network-bridge-selector",
"required": true,
"default": "vmbr0"
},
"ipv4_mode": {
"type": "select",
"required": true,
"default": "dhcp",
"options": [
"dhcp",
"static"
]
},
"ipv4_address": {
"type": "ipv4-cidr",
"required_when": {
"field": "ipv4_mode",
"equals": "static"
}
},
"gateway": {
"type": "ipv4",
"required_when": {
"field": "ipv4_mode",
"equals": "static"
}
},
"onboot": {
"type": "boolean",
"required": true,
"default": true
},
"shared_mount_root": {
"type": "host-directory",
"required": true,
"default": "/mnt/oci-shared/remotes",
"create_if_missing": true,
"description": "Host root where the remote mounts appear, to be assigned afterwards to Plex, Jellyfin, qBittorrent or other OCI containers."
},
"mount_name": {
"type": "string",
"required": true,
"default": "remote",
"validation": {
"pattern": "^[A-Za-z0-9._-]{1,64}$"
}
},
"remote_name": {
"type": "rclone-remote-selector",
"required_after": "authorize_remote",
"description": "Remote created by the user; it is never included in the template."
},
"remote_path": {
"type": "string",
"required": true,
"default": ""
},
"vfs_cache_mode": {
"type": "select",
"required": true,
"default": "full",
"options": [
"off",
"minimal",
"writes",
"full"
]
},
"shared_mount_root_parent": {
"type": "host-directory",
"required": true,
"default": "/mnt/oci-shared",
"create_if_missing": true,
"description": "Punto de montaje del host que contiene las publicaciones de lectura/escritura y de solo lectura; el hook lo configura como rshared."
},
"shared_mount_read_only_root": {
"type": "host-directory",
"required": true,
"default": "/mnt/oci-shared/remotes-ro",
"create_if_missing": true,
"description": "Vista FUSE recursivamente de solo lectura para consumidores multimedia como Plex y Jellyfin."
}
},
"mounts": [
{
"id": "config",
"container_path": "/config/rclone",
"source": "managed-volume",
"storage_field": "config_storage",
"size_field": "config_size_gb",
"backup": true,
"required": true,
"contains_secrets": true,
"contains": [
"rclone.conf",
"rclone.log",
"cache"
]
},
{
"id": "internal-fuse-root",
"container_path": "/data/mounts",
"source": "container-rootfs-directory",
"read_only": false,
"backup": false,
"required_in_mount_mode": true,
"purpose": "Internal target for official rclone mount before host publication."
}
],
"environment": [
{
"name": "XDG_CONFIG_HOME",
"value": "/config"
}
],
"deployment": {
"runtime": "proxmox-native-oci-lxc",
"unprivileged": false,
"privileged_container_required": true,
"fuse_device_inside_container_required": true,
"entrypoint": "/usr/local/bin/rclone gui --no-open-browser --addr=:${webui_port} --api-addr=:${api_port} --config=/config/rclone/rclone.conf --cache-dir=/config/rclone/cache --log-file=/config/rclone/rclone.log --log-level=${log_level}",
"working_directory": "/data",
"hostname_default": "rclone",
"onboot_default": true,
"startup_order_default": 10,
"startup_delay_seconds_default": 20,
"shutdown_timeout_seconds": 30,
"halt_signal": "SIGTERM",
"features": [
"nesting=1",
"fuse=1"
],
"ports": [
{
"port_from": "webui_port",
"protocol": "tcp",
"purpose": "authenticated-web-ui"
},
{
"port_from": "api_port",
"protocol": "tcp",
"purpose": "authenticated-remote-control-api"
}
],
"preserve_image_environment": true,
"restart_method": "clean-stop-then-start",
"restart_note": "On some OCI containers, pct reboot left a residual lxc-start monitor behind. The installer prefers pct stop followed by pct start, and checks the new PID.",
"entrypoint_source": "setup-direct-command-or-generated-mount-wrapper",
"entrypoint_override": "setup-mode-official-rclone-gui-command",
"runtime_modes": {
"setup": {
"purpose": "Create and authorize the user's own remote through the authenticated WebUI.",
"entrypoint": "/usr/local/bin/rclone gui --no-open-browser --addr=:${webui_port} --api-addr=:${api_port} --config=/config/rclone/rclone.conf --cache-dir=/config/rclone/cache --log-file=/config/rclone/rclone.log --log-level=${log_level}"
},
"mount": {
"purpose": "Mount one selected remote, keep the authenticated RC WebUI/API available and publish the FUSE tree for native Proxmox consumers.",
"entrypoint": "/usr/local/bin/rclone-mount-lxc-start",
"wrapper_behavior": "Read RC credentials from /config, wait for host-managed networking, then exec the official Rclone binary.",
"official_command": "/usr/local/bin/rclone mount ${remote_name}:${remote_path} /data/mounts/${mount_name}",
"webui_assets": "official-rclone-webui-selected-by---rc-web-gui",
"webui_serving": "official --rc-web-gui flags on the RC listener",
"pid1": "official-rclone-binary-after-wrapper-exec",
"restart_persistence": "Proxmox starts the generated mount wrapper on every boot.",
"credentials": {
"source": "/config/rclone/webui.credentials",
"mode": "0600",
"exported_only_inside_lxc": [
"RCLONE_RC_USER",
"RCLONE_RC_PASS"
],
"stored_in_pve_config": false
}
}
}
},
"bootstrap": {
"mode": "two-phase-official-rclone-process",
"steps": [
"validate-privileged-fuse-and-propagation-requirements",
"pull-oci-image-by-digest",
"create-managed-config-volume",
"create-shared-mount-root",
"generate-webui-password-when-empty",
"apply-webui-credentials-to-proxmox-env",
"create-privileged-container-with-fuse",
"start-setup-mode",
"verify-authenticated-webui-and-api",
"show-authorize-remote-next-action"
],
"credentials_policy": {
"delivery": "Proxmox env property",
"environment": [
"RCLONE_RC_USER",
"RCLONE_RC_PASS"
],
"pve_visibility": "visible-by-design",
"remote_credentials_storage": "/config/rclone/rclone.conf"
}
},
"post_install_workflows": {
"authorize_remote": {
"when": "after-base-install",
"performed_by": "user-in-authenticated-webui",
"requires_terminal": false,
"initial_state": {
"rclone_config": "empty",
"preconfigured_remotes": 0,
"import_remote_from_another_installation": false
},
"steps": [
"open-generated-webui-access-url",
"select-new-remote-provider",
"create-new-remote-from-scratch",
"complete-provider-oauth",
"verify-remote-with-authenticated-rc-api"
],
"result": {
"config_path": "/config/rclone/rclone.conf",
"tokens_persist_in_managed_config_volume": true
}
},
"publish_remote": {
"when": "after-authorize-remote",
"performed_by": "installer-with-explicit-user-selection",
"requires_terminal": false,
"steps": [
"list-user-created-remotes-through-authenticated-rc-api",
"ask-user-for-remote-path-and-mount-name",
"stop-setup-mode",
"apply-official-rclone-mount-entrypoint",
"start-container",
"verify-fuse-inside-container",
"verify-submount-visible-on-host",
"optionally-assign-published-path-to-selected-consumer-lxc"
],
"consumer_policy": "Consumers are never modified unless the user selects them explicitly.",
"status": "installer-implemented"
}
},
"healthcheck": {
"type": "authenticated-http-and-api",
"webui": {
"port_from": "webui_port",
"path": "/",
"expected_status": 200
},
"api": {
"port_from": "api_port",
"method": "POST",
"path": "/core/version",
"expected_version": "v1.75.0",
"credentials_from": "lxc.environment.runtime:RCLONE_RC_USER,RCLONE_RC_PASS"
},
"retries": 30,
"start_period_seconds": 60
},
"backup_restore": {
"native_proxmox_backup": true,
"included_mounts": [
"/config/rclone"
],
"excluded_mounts": [
"/data"
],
"external_backup_recommended": [
"data_host_path-if-it-contains-unique-local-data"
],
"restore_order": [
"restore-vzdump",
"verify-managed-config-volume",
"verify-data-host-path",
"start-rclone-oci",
"verify-authenticated-webui-and-api"
],
"application_consistency": "Use stop mode when active copy or sync jobs require a consistent snapshot. OAuth tokens in rclone.conf are included in the managed config volume."
},
"boundaries": {
"bundles_webui_credentials": false,
"bundles_remote_credentials": false,
"bundles_rclone_remotes": false,
"bundles_user_data": false,
"installer_must_not_create_external_remotes": true,
"installer_must_not_import_remotes_from_other_lxcs": true,
"template_starts_with_empty_rclone_config": true,
"user_must_create_and_authorize_own_remote": true,
"cross_lxc_fuse_publication_supported": "laboratory-validated",
"consumer_assignments_require_explicit_user_selection": true,
"rclone_runs_inside_its_oci_lxc": true,
"no_host_rclone_binary_or_application_supervisor": true
},
"post_install_output": {
"url_template": "http://${container_ip}:${webui_port}/login?pass=${urlencode(webui_password)}&url=${urlencode(http://${container_ip}:${api_port}/)}&user=${urlencode(webui_username)}",
"sensitive": true,
"display_once_after_install": true,
"never_log": true
},
"generated_assets": {
"mount-mode-wrapper": {
"target": "lxc:/usr/local/bin/rclone-mount-lxc-start",
"mode": "0755",
"content_template": "#!/bin/sh\nset -eu\n\ncredentials=/config/rclone/webui.credentials\nexport RCLONE_RC_USER=\"$(sed -n 's/^username=//p' \"$credentials\")\"\nexport RCLONE_RC_PASS=\"$(sed -n 's/^password=//p' \"$credentials\")\"\nremote_name=\"$(printf '%s' '{{remote_name_base64}}' | base64 -d)\"\nremote_path=\"$(printf '%s' '{{remote_path_base64}}' | base64 -d)\"\n\ntest -n \"$RCLONE_RC_USER\"\ntest -n \"$RCLONE_RC_PASS\"\ntest -n \"$remote_name\"\n\nnetwork_ready=false\nfor _ in $(seq 1 120); do\n if ip route get 1.1.1.1 >/dev/null 2>&1; then\n network_ready=true\n break\n fi\n sleep 1\ndone\ntest \"$network_ready\" = true\n\nexec /usr/local/bin/rclone mount \"${remote_name}:${remote_path}\" /data/mounts/{{mount_name}} \\\n --config /config/rclone/rclone.conf \\\n --allow-other \\\n --vfs-cache-mode {{vfs_cache_mode}} \\\n --cache-dir /config/rclone/cache \\\n --rc \\\n --rc-addr :{{webui_port}} \\\n --rc-web-gui \\\n --rc-web-gui-no-open-browser \\\n --log-file /config/rclone/rclone.log \\\n --log-level ERROR\n"
},
"mount-tree-publisher-source": {
"target": "host:/usr/local/libexec/proxmenux-oci-mount-publish",
"runtime": "python3-from-proxmox-host",
"mode": "0755",
"content": "#!/usr/bin/env python3\n\"\"\"Clone a FUSE mount from an LXC namespace into the Proxmox host namespace.\"\"\"\n\nfrom __future__ import annotations\n\nimport ctypes\nimport os\nimport platform\nimport sys\n\n\nAT_FDCWD = -100\nAT_EMPTY_PATH = 0x1000\nAT_RECURSIVE = 0x8000\nCLONE_NEWNS = 0x00020000\nMOVE_MOUNT_F_EMPTY_PATH = 0x00000004\nMOUNT_ATTR_RDONLY = 0x00000001\nOPEN_TREE_CLONE = 1\n\nSYSCALLS = {\n \"x86_64\": (428, 429, 442),\n \"amd64\": (428, 429, 442),\n \"aarch64\": (428, 429, 442),\n \"arm64\": (428, 429, 442),\n}\n\n\nclass MountAttr(ctypes.Structure):\n _fields_ = [\n (\"attr_set\", ctypes.c_uint64),\n (\"attr_clr\", ctypes.c_uint64),\n (\"propagation\", ctypes.c_uint64),\n (\"userns_fd\", ctypes.c_uint64),\n ]\n\n\ndef fail(step: str) -> None:\n error = ctypes.get_errno()\n raise OSError(error, f\"{step}: {os.strerror(error)}\")\n\n\ndef main() -> int:\n if len(sys.argv) != 5 or sys.argv[4] not in {\"rw\", \"ro\"}:\n print(f\"usage: {sys.argv[0]} PID SOURCE TARGET rw|ro\", file=sys.stderr)\n return 2\n machine = platform.machine().lower()\n if machine not in SYSCALLS:\n print(f\"unsupported host architecture: {machine}\", file=sys.stderr)\n return 2\n open_tree_nr, move_mount_nr, mount_setattr_nr = SYSCALLS[machine]\n pid, source, target, mode = sys.argv[1:]\n libc = ctypes.CDLL(None, use_errno=True)\n libc.syscall.restype = ctypes.c_long\n libc.setns.argtypes = (ctypes.c_int, ctypes.c_int)\n libc.setns.restype = ctypes.c_int\n\n host_ns = os.open(\"/proc/self/ns/mnt\", os.O_RDONLY | os.O_CLOEXEC)\n host_root = os.open(\"/\", os.O_PATH | os.O_DIRECTORY | os.O_CLOEXEC)\n ct_ns = os.open(f\"/proc/{pid}/ns/mnt\", os.O_RDONLY | os.O_CLOEXEC)\n ct_root = os.open(f\"/proc/{pid}/root\", os.O_PATH | os.O_DIRECTORY | os.O_CLOEXEC)\n try:\n if libc.setns(ct_ns, CLONE_NEWNS) != 0:\n fail(\"enter container namespace\")\n os.fchdir(ct_root)\n os.chroot(\".\")\n os.chdir(\"/\")\n tree = libc.syscall(\n open_tree_nr,\n AT_FDCWD,\n os.fsencode(source),\n OPEN_TREE_CLONE | os.O_CLOEXEC,\n )\n if tree < 0:\n fail(\"clone source mount tree\")\n try:\n if mode == \"ro\":\n attributes = MountAttr(attr_set=MOUNT_ATTR_RDONLY)\n result = libc.syscall(\n mount_setattr_nr,\n tree,\n ctypes.c_char_p(b\"\"),\n AT_EMPTY_PATH | AT_RECURSIVE,\n ctypes.byref(attributes),\n ctypes.sizeof(attributes),\n )\n if result != 0:\n fail(\"make cloned mount tree read-only\")\n if libc.setns(host_ns, CLONE_NEWNS) != 0:\n fail(\"return to host namespace\")\n os.fchdir(host_root)\n os.chroot(\".\")\n os.chdir(\"/\")\n result = libc.syscall(\n move_mount_nr,\n tree,\n ctypes.c_char_p(b\"\"),\n AT_FDCWD,\n os.fsencode(target),\n MOVE_MOUNT_F_EMPTY_PATH,\n )\n if result != 0:\n fail(\"publish mount tree\")\n finally:\n os.close(tree)\n finally:\n for descriptor in (ct_root, ct_ns, host_root, host_ns):\n os.close(descriptor)\n return 0\n\n\nif __name__ == \"__main__\":\n try:\n raise SystemExit(main())\n except OSError as exc:\n print(exc, file=sys.stderr)\n raise SystemExit(1)\n"
},
"mount-publication-waiter": {
"target": "host:/usr/local/libexec/proxmenux-oci-mount-wait",
"mode": "0755",
"lifecycle": "transient-systemd-oneshot-only",
"content": "#!/usr/bin/env bash\nset -euo pipefail\n\nvmid=${1:?missing VMID}\ninside=${2:?missing source path}\npublished=${3:?missing target path}\npublished_ro=${4:?missing read-only target path}\nhelper=${5:?missing publisher helper}\n\nunpublish() {\n local target=$1\n if mountpoint -q \"$target\"; then\n umount \"$target\" || umount -l \"$target\"\n fi\n}\n\nfor _ in $(seq 1 300); do\n pid=$(lxc-info -n \"$vmid\" -pH 2>/dev/null || true)\n if [[ -n $pid ]] && awk -v path=\"$inside\" '$5 == path && $0 ~ / - fuse(\\.rclone)? / { found=1 } END { exit !found }' \"/proc/$pid/mountinfo\"; then\n unpublish \"$published_ro\"\n unpublish \"$published\"\n \"$helper\" \"$pid\" \"$inside\" \"$published\" rw\n if ! \"$helper\" \"$pid\" \"$inside\" \"$published_ro\" ro; then\n unpublish \"$published\"\n exit 1\n fi\n findmnt -T \"$published\" -n -o FSTYPE | grep -q '^fuse'\n findmnt -T \"$published_ro\" -n -o FSTYPE | grep -q '^fuse'\n findmnt -T \"$published_ro\" -n -o VFS-OPTIONS | tr ',' '\n' | grep -qx ro\n logger -t proxmenux-rclone \"Published CT $vmid $inside at $published (rw) and $published_ro (ro)\"\n exit 0\n fi\n sleep 1\ndone\n\nlogger -t proxmenux-rclone \"Timed out waiting for CT $vmid FUSE mount at $inside\"\nexit 1\n"
},
"proxmox-hookscript": {
"target": "snippet-storage:proxmenux-rclone-${ctid}-fuse-hook.sh",
"mode": "0755",
"phases": [
"pre-start",
"post-start",
"pre-stop",
"post-stop"
],
"content_template": "#!/usr/bin/env bash\nset -euo pipefail\n\nvmid=${1:?missing VMID}\nphase=${2:?missing phase}\ninside=/data/mounts/{{mount_name}}\npublished={{shared_mount_root}}/{{mount_name}}\npublished_ro={{shared_mount_read_only_root}}/{{mount_name}}\nhelper=/usr/local/libexec/proxmenux-oci-mount-publish\nwaiter=/usr/local/libexec/proxmenux-oci-mount-wait\nunit=\"proxmenux-rclone-publish-$vmid.service\"\n\nunpublish() {\n local target=$1\n if mountpoint -q \"$target\"; then\n umount \"$target\" || umount -l \"$target\"\n fi\n}\n\nstop_waiter() {\n systemctl stop \"$unit\" >/dev/null 2>&1 || true\n systemctl reset-failed \"$unit\" >/dev/null 2>&1 || true\n}\n\ncase \"$phase\" in\n pre-start)\n install -d -m 0755 \"$published\" \"$published_ro\"\n if ! mountpoint -q {{shared_mount_root_parent}}; then\n mount --bind {{shared_mount_root_parent}} {{shared_mount_root_parent}}\n fi\n mount --make-rshared {{shared_mount_root_parent}}\n stop_waiter\n unpublish \"$published_ro\"\n unpublish \"$published\"\n ;;\n post-start)\n stop_waiter\n systemd-run --quiet --collect --unit=\"$unit\" -- \\\n \"$waiter\" \"$vmid\" \"$inside\" \"$published\" \"$published_ro\" \"$helper\"\n ;;\n pre-stop|post-stop)\n stop_waiter\n unpublish \"$published_ro\"\n unpublish \"$published\"\n ;;\nesac\n"
}
},
"consumer_integration": {
"optional": true,
"canonical_read_write_root_host_path": "${shared_mount_root}",
"read_only_root_host_path": "${shared_mount_read_only_root}",
"read_only_remote_host_path": "${shared_mount_read_only_root}/${mount_name}",
"required_mount_order": [
"shared-root-parent",
"exact-published-remote"
],
"plex_example": {
"parent": "${shared_mount_read_only_root},mp=/data/remotes,backup=0,ro=1",
"exact": "${shared_mount_read_only_root}/${mount_name},mp=/data/remotes/${mount_name},backup=0,ro=1"
},
"jellyfin_example": {
"parent": "${shared_mount_read_only_root},mp=/media/remotes,backup=0,ro=1",
"exact": "${shared_mount_read_only_root}/${mount_name},mp=/media/remotes/${mount_name},backup=0,ro=1"
},
"restart_rule": "Keep both parent and exact mpN declarations so consumers recover when the Rclone FUSE publication is replaced."
},
"notes": [
"La configuracion, los tokens y las credenciales RC permanecen en /config/rclone dentro del volumen backup=1.",
"El usuario crea y autoriza su propio remote desde la WebUI; la plantilla no incluye remotes del laboratorio.",
"El modo mount usa un wrapper minimo que termina con exec del binario oficial; Rclone queda como PID 1.",
"La publicacion usa un hookscript oficial de Proxmox y una tarea systemd transitoria; no instala Rclone ni un supervisor permanente en el host.",
"Cada remoto se publica en una raiz canonica de lectura/escritura y en otra raiz recursivamente de solo lectura; cada consumidor selecciona la politica adecuada.",
"Los consumidores son opcionales y deben declarar el padre compartido mas un mpN exacto por remoto.",
"El perfil fue validado con reinicios de Rclone, Plex y Jellyfin."
],
"references": {
"official_docker_install": "https://rclone.org/install/#docker-installation",
"official_gui_command": "https://rclone.org/commands/rclone_gui/",
"official_mount_command": "https://rclone.org/commands/rclone_mount/",
"official_vfs_documentation": "https://rclone.org/commands/rclone_mount/#vfs-file-caching"
}
},
"security_profile": {
"requires_privileged_lxc": true,
"risk_level": "high",
"confirmation_required": true,
"warning": "Rclone mount requires a privileged LXC with FUSE access. Use this profile only on a trusted node and network."
},
"installer_profile": {
"generated_files": [
{
"id": "rclone-setup-wrapper",
"container_path": "/usr/local/bin/rclone-setup-lxc-start",
"owner": "mapped-root",
"mode": "0755",
"content": "#!/bin/sh\nset -eu\nmkdir -p /config/rclone/cache\nexec /usr/local/bin/rclone gui --no-open-browser --addr=:5572 --api-addr=:5573 --config=/config/rclone/rclone.conf --cache-dir=/config/rclone/cache --log-file=/config/rclone/rclone.log --log-level=ERROR\n"
}
],
"volume_preparations": [
{
"container_path": "/config/rclone",
"remove_lost_found": true,
"owner_strategy": "mapped-root"
}
],
"runtime": {
"entrypoint": "/usr/local/bin/rclone-setup-lxc-start",
"working_directory": "/data",
"halt_signal": "SIGTERM"
},
"startup_healthcheck": {
"scheme": "http",
"port": 5572,
"path": "/",
"verify_tls": false,
"timeout_seconds": 180,
"request_timeout_seconds": 5,
"stability_seconds": 0
}
}
},
"compatibility": {
"automatic_install_candidate": true,
"validated": true,
"supported_compose_keys": [
"container_name",
"environment",
"image",
"ports",
"restart",
"stop_grace_period",
"volumes"
],
"untranslated_blockers": [],
"policy": "Automatic installation implements the validated privileged FUSE LXC, generated wrappers, and host publication workflow with explicit user consent."
},
"validation": {
"schema": "passed-at-generation",
"profile": {
"reference_host": "Proxmox VE 9.2.11, kernel 7.0.14-16-pve",
"reference_lxc": "CT120",
"internal_fuse_mount": "passed",
"host_publication": "passed",
"host_read_write_publication": "passed",
"host_recursive_read_only_publication": "passed",
"host_to_lxc_visibility": "passed",
"lxc_to_host_visibility": "passed",
"stop_unpublish": "passed",
"restart_republish_seconds": 2,
"plex_consumer": "passed-read-only",
"jellyfin_consumer": "passed-read-only",
"credentials_outside_config": false,
"transient_waiter_after_success": "inactive",
"installer_base_mode": "passed",
"privileged_oci_import_conversion": "passed-preserving-xattrs",
"official_rclone_version": "1.75.1",
"webui_mode": "passed-official-embedded-webui"
},
"validated_profile": {
"id": "pve55-rclone-direct-fuse",
"container_id": 120,
"validation_status": "passed",
"passed": [
"allow-other",
"consumer-lxc-read-only-policy",
"fuse-mount-inside-lxc",
"host-read-write-and-recursive-read-only-views",
"managed-config-volume",
"no-host-rclone-supervisor",
"official-rclone-binary-as-pid1",
"restart-with-published-host-mount",
"reverse-submount-publication-to-host"
],
"pending": []
},
"source_profile": "rclone-oci.json"
},
"lifecycle": {
"update_strategy": "replace-rootfs-from-latest-oci-image-preserve-managed-config-volume-and-reapply-reviewed-assets",
"registry_state": {
"resolved_architecture": null,
"resolved_digest": null,
"image_version_label": null,
"image_created": null
},
"change_detection": "compare-resolved-architecture-digest",
"automatic_unattended_updates": false,
"validated_workflows": {
"install": [
"validate-requirements",
"pull-oci-image-by-digest",
"create-managed-config-volume",
"create-shared-mount-root",
"create-privileged-fuse-container",
"install-generated-fuse-publication-assets-on-host",
"attach-proxmox-hookscript",
"start-setup-mode",
"wait-for-authenticated-healthcheck",
"show-authorize-remote-next-action"
],
"update": [
"stop-active-mount-cleanly-and-unpublish-host-tree",
"backup-container",
"pull-version-pinned-image",
"recreate-rootfs-preserving-managed-config-volume",
"reinstall-generated-wrapper-and-publication-assets",
"restore-selected-runtime-mode",
"start-container",
"verify-authenticated-api-internal-fuse-host-publication-and-consumers"
],
"uninstall": {
"remove_rootfs": true,
"preserve_config_by_default": true,
"preserve_data_by_default": true
},
"activate_mount": [
"validate-selected-remote",
"generate-mount-wrapper-without-embedding-secrets",
"remove-legacy-rclone-rc-runtime-secret-lines-from-pve-config",
"set-mount-wrapper-as-entrypoint",
"stop-then-start-container",
"wait-for-host-published-fuse-tree",
"attach-shared-root-and-exact-remote-mounts-to-selected-consumers",
"validate-read-policy-from-each-consumer"
]
}
}
}