How the CasaOS Storage Service Manages External Storage Mounts

CasaOS abstracts external storage handling through a dedicated Storage Service that communicates with an embedded rclone instance via a local Unix-socket HTTP API, enabling automatic mounting of SMB, NFS, and cloud drives at boot.

The CasaOS storage service provides a unified interface for integrating external storage devices and cloud remotes into the local filesystem. By leveraging rclone as the underlying engine and exposing a Go-based service layer, CasaOS treats SMB shares, NFS exports, and cloud storage providers as first-class citizens that persist across reboots. This article examines the source code implementation to explain how the CasaOS storage service manages external storage mounts through configuration, mounting, and reconciliation workflows.

Architecture Overview

The storage subsystem consists of three primary layers. The model layer defines the StorageA struct in model/storage.go to persist mount metadata. The httper driver in pkg/utils/httper/drive.go handles low-level HTTP communication with rclone's Unix socket. Finally, the service layer in service/storage.go orchestrates high-level operations like mounting and automatic reconciliation.

How External Storage Mounts Work in CasaOS

1. Creating Remote Configurations

When a user adds a new external drive, the service constructs a key-value map describing the remote. The CreateConfig method in pkg/utils/httper/drive.go (lines 13-30) translates this map into a JSON payload and posts it to rclone's /config/create endpoint.

This process stores credentials and connection parameters without exposing them to the host environment variables directly. The configuration persists in rclone's internal config database, keyed by a friendly name provided by the user.

2. Mounting External Drives

After configuration, the MountStorage method in service/storage.go (lines 27-30) prepares the mount point using file.IsNotExistMkDir to create the directory if missing. It then invokes httper.Mount, which posts a form to rclone's /mount/mount endpoint with three critical parameters:

  • mount_point: The local filesystem path
  • fs: The remote identifier in rclone syntax (e.g., my-remote:)
  • options: Mount flags including AllowOther and CacheMode

The implementation in pkg/utils/httper/drive.go (lines 80-95) handles the HTTP POST via a Resty client bound to /var/run/rclone/rclone.sock.

3. Automatic Reconciliation at Startup

To ensure persistence across reboots, CheckAndMountAll in service/storage.go (lines 66-99) implements a reconciliation loop. The method retrieves all remote names via httper.GetAllConfigName, then iterates through each configuration.

For every remote, it extracts the mount_point from GetConfigByName and checks active mounts using GetStorages. If a defined remote lacks an active mount, the service automatically invokes MountStorage. This guarantees that SMB shares, cloud drives, and NFS mounts become available immediately after CasaOS starts.

4. Unmounting and Cleanup

Removal operations follow the reverse path. UnmountStorage in service/storage.go (lines 31-40) calls httper.Unmount, which posts to rclone's /mount/unmount endpoint (implemented in drive.go lines 96-110).

After a successful unmount, the service removes the empty mount-point directory from the local filesystem. This prevents stale directory accumulation while ensuring clean detachment of network storage.

Data Model and HTTP Client

Each external storage entry is represented by the StorageA struct defined in model/storage.go (lines 5-18). This structure tracks:

  • MountPath: The local directory where the remote is attached
  • Driver: The storage backend type (smb, nfs, onedrive, etc.)
  • Status: Current operational state
  • Addition: Driver-specific configuration data

All API communication occurs through a dedicated Resty client created by NewRestyClient in pkg/utils/httper/drive.go (lines 47-61). This client binds exclusively to the Unix socket at /var/run/rclone/rclone.sock, ensuring the storage service never requires direct external network access. All rclone operations—configuration, mounting, and unmounting—flow through this private channel.

Practical Implementation Examples

The following Go examples demonstrate the storage service API as implemented in the CasaOS codebase:

// Example: add a new SMB share and mount it
func addAndMountSMB() error {
    // 1️⃣ Build the config map expected by rclone
    cfg := map[string]string{
        "type":          "smb",
        "host":          "192.168.1.100",
        "user":          "guest",
        "pass":          "password",
        "mount_point":   "/mnt/smb-share",
        "vendor":        "smb",
    }

    // 2️⃣ Store the config under a friendly name
    if err := MyService.Storage().CreateConfig(cfg, "my-smb", "smb"); err != nil {
        return err
    }

    // 3️⃣ Mount the newly created remote
    // The fs argument follows rclone syntax: "<remote_name>:" (note the trailing colon)
    return MyService.Storage().MountStorage(cfg["mount_point"], "my-smb:")
}
// Example: ensure all defined remotes are mounted (called on startup)
func reconcileMounts() error {
    return MyService.Storage().CheckAndMountAll()
}
// Example: unmount and clean up a specific mount point
func unmountShare(mp string) error {
    return MyService.Storage().UnmountStorage(mp)
}

Summary

  • CasaOS manages external storage through a Storage Service that wraps an embedded rclone instance communicating over a Unix socket at /var/run/rclone/rclone.sock.
  • Configuration persistence occurs via CreateConfig in pkg/utils/httper/drive.go, which posts to rclone's /config/create endpoint.
  • Mount operations are handled by MountStorage in service/storage.go, creating local directories and invoking rclone's /mount/mount API.
  • Automatic reconciliation via CheckAndMountAll ensures all defined remotes mount automatically after system restarts.
  • Clean unmounting removes both the rclone mount and the local directory through UnmountStorage.

Frequently Asked Questions

How does CasaOS handle external storage without direct network calls?

The storage service uses a dedicated Resty HTTP client bound to a local Unix socket at /var/run/rclone/rclone.sock. All communication with rclone—including configuration, mounting, and unmounting—traverses this private channel. The rclone process itself handles external network connectivity, while CasaOS remains isolated from direct remote storage protocols.

What happens to external mounts when CasaOS restarts?

The CheckAndMountAll method in service/storage.go (lines 66-99) executes during startup. It iterates through all stored configurations retrieved via httper.GetAllConfigName, verifies active mounts against the desired state, and automatically remounts any missing remotes. This reconciliation loop ensures SMB shares, cloud drives, and NFS exports persist across reboots without manual intervention.

Which remote storage types does CasaOS support?

CasaOS supports any storage backend compatible with rclone, including SMB, NFS, OneDrive, Dropbox, Google Drive, and S3-compatible object storage. The StorageA model in model/storage.go stores the driver type in the Driver field, while the Addition field accommodates driver-specific configuration parameters required by each backend.

Where does CasaOS store external storage configuration?

Configuration data persists in two locations. The rclone-specific connection parameters (credentials, endpoints, options) are stored in rclone's internal configuration database via the /config/create API endpoint. The CasaOS-specific metadata—including mount points, status, and driver type—is stored in the CasaOS database via the StorageA struct defined in model/storage.go.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →