{ "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": null, "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" ] } } }