How to Implement Custom Storage Drivers in CasaOS: A Complete Developer Guide

CasaOS discovers custom storage drivers through a plugin architecture that requires implementing Go interfaces defined in internal/driver/driver.go, enabling any conforming backend to integrate with the storage service.

CasaOS is an open-source home server system that abstracts cloud storage through a modular driver system. To add support for a new cloud provider or proprietary storage API, developers must build a driver package that satisfies the core interfaces and register it with the internal factory. This guide explains the exact implementation steps, file paths, and method signatures required to create custom storage drivers in CasaOS based on the current source code architecture.

Understanding the CasaOS Driver Interface Architecture

Core Interfaces in internal/driver/driver.go

The storage subsystem defines several contracts that every driver must satisfy. According to the source code in internal/driver/driver.go, these interfaces segregate capabilities:

  • Meta: Handles configuration and lifecycle. Required methods include Config() Config, GetStorage() *model.StorageA, SetStorage(model.StorageA), GetAddition() Additional, Init(ctx context.Context) error, and Drop(ctx context.Context) error.
  • Reader: Provides read-only operations. While it defines no mandatory methods, drivers typically expose listing functions here.
  • User: Supplies user context and credentials. Requires GetUserInfo(ctx context.Context) (string, error) and GetInfo(ctx context.Context) (string, string, string, error).
  • Optional Writer Interfaces: Implement Writer, Mkdir, Move, Rename, Copy, Remove, or Put only if your backend supports modifications.

The Storage Model

CasaOS uses a generic representation of remote file systems through model.StorageA. This struct, defined in model/storage.go, acts as the bridge between the UI and your driver. The Meta interface supplies a pointer to this model via GetStorage and SetStorage, allowing the driver to persist state.

How CasaOS Discovers and Loads Drivers

The storage service (service/storage.go) orchestrates driver initialization at startup. It reads the global configuration, invokes the factory function driver.New(cfg.Name), and calls Init on each instance:

// Simplified excerpt from service/storage.go
for _, cfg := range cfgs {
    drv, err := driver.New(cfg.Name)   // factory returns concrete driver
    if err == nil {
        _ = drv.Init(ctx)               // initialise the driver
        s.drivers[cfg.Name] = drv
    }
}

The factory function in internal/driver/driver.go uses a switch statement to map configuration names to concrete implementations. This is where you must register your custom driver.

Step-by-Step Guide to Building a Custom Driver

Step 1: Create the Driver Package

Create a new directory under drivers/ for your backend:

mkdir -p drivers/mycloud

Step 2: Implement the Meta Interface

Your driver struct must embed driver.Config and implement lifecycle methods:

type MyCloud struct {
    cfg     driver.Config
    storage *model.StorageA
}

func (d *MyCloud) Config() driver.Config           { return d.cfg }
func (d *MyCloud) GetStorage() *model.StorageA   { return d.storage }
func (d *MyCloud) SetStorage(s model.StorageA)     { d.storage = &s }
func (d *MyCloud) GetAddition() driver.Additional  { return nil }

func (d *MyCloud) Init(ctx context.Context) error {
    // Authentication and setup logic here
    return nil
}

func (d *MyCloud) Drop(ctx context.Context) error {
    // Cleanup logic here
    return nil
}

Step 3: Implement the User Interface

Provide the root folder path and driver metadata:

func (d *MyCloud) GetUserInfo(ctx context.Context) (string, error) {
    return d.cfg.DefaultRoot, nil
}

func (d *MyCloud) GetInfo(ctx context.Context) (string, string, string, error) {
    return "MyCloud", "v1.0", d.cfg.Name, nil
}

Step 4: Implement Read Operations

At minimum, implement a List method that the UI can call to enumerate files:

func (d *MyCloud) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
    // Call MyCloud API and translate response to []model.Obj
    return nil, nil
}

Step 5: Add Write Capabilities (Optional)

If your backend supports uploads, implement the Put interface:

func (d *MyCloud) Put(ctx context.Context, dst model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
    // Upload implementation with progress reporting
    return nil
}

Similarly, implement Remove, Mkdir, Move, Rename, or Copy only as needed.

Step 6: Register the Driver in the Factory

Open internal/driver/driver.go and add your driver to the New function:

func New(name string) (driver.Driver, error) {
    switch name {
    case "mycloud":
        return &mycloud.MyCloud{cfg: driver.Config{Name: name}}, nil
    // existing cases...
    default:
        return nil, fmt.Errorf("unknown driver %s", name)
    }
}

Step 7: Configure and Build

Add a configuration block to conf/conf.conf.sample:

"mycloud": {
    "name": "mycloud",
    "default_root": "/",
    "only_proxy": false
}

Rebuild the project:

make build

Complete Skeleton Driver Example

Below is a minimal, compilable driver skeleton for drivers/mycloud/driver.go:

package mycloud

import (
    "context"

    "github.com/IceWhaleTech/CasaOS/internal/driver"
    "github.com/IceWhaleTech/CasaOS/model"
)

type MyCloud struct {
    cfg     driver.Config
    storage *model.StorageA
}

// Meta interface implementation
func (d *MyCloud) Config() driver.Config           { return d.cfg }
func (d *MyCloud) GetStorage() *model.StorageA   { return d.storage }
func (d *MyCloud) SetStorage(s model.StorageA)     { d.storage = &s }
func (d *MyCloud) GetAddition() driver.Additional  { return nil }
func (d *MyCloud) Init(ctx context.Context) error { return nil }
func (d *MyCloud) Drop(ctx context.Context) error { return nil }

// User interface implementation
func (d *MyCloud) GetUserInfo(ctx context.Context) (string, error) {
    return d.cfg.DefaultRoot, nil
}
func (d *MyCloud) GetInfo(ctx context.Context) (string, string, string, error) {
    return "MyCloud", "v0.1", d.cfg.Name, nil
}

// Reader implementation
func (d *MyCloud) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
    // Translate API response to model.Obj slice
    return nil, nil
}

// Optional Writer implementation
func (d *MyCloud) Put(ctx context.Context, dst model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
    return nil
}

Key Source Files for Reference

Study these implementations to understand production patterns:

Testing Your Custom Storage Driver

  1. Unit Testing: Create drivers/mycloud/driver_test.go to exercise Init, List, and any write methods in isolation.
  2. Integration Testing: Run go test ./... from the project root. The storage service will attempt to load your driver if the configuration block is present.
  3. Manual Verification: Start CasaOS with your configuration enabled, open the web UI, and verify that the new storage appears and correctly lists files.

Summary

  • Create a new package under drivers/ implementing the Meta, User, and Reader interfaces defined in internal/driver/driver.go.
  • Register the driver in the New factory function within internal/driver/driver.go to enable instantiation by name.
  • Add configuration entries in conf/conf.conf.sample so the storage service can discover and initialize your driver.
  • Optional write support requires implementing granular interfaces like Put, Remove, or Mkdir rather than a monolithic requirement.
  • Reference existing drivers like drivers/onedrive/drive.go for production-ready patterns involving OAuth and pagination.

Frequently Asked Questions

How do I register a new storage driver in CasaOS?

Register your driver by adding a case to the factory function in internal/driver/driver.go. The function receives a name string from the configuration and returns a concrete instance of your driver, allowing the storage service in service/storage.go to manage it during runtime.

What is the minimum interface implementation required for a read-only driver?

You must implement the Meta interface for configuration and lifecycle management, the User interface for providing root folder paths and credentials, and at minimum a List method that matches the signature used by existing drivers to return []model.Obj.

Where does CasaOS store driver configuration?

Driver configuration is defined by the struct in internal/driver/config.go and populated from the global configuration file (typically conf/conf.conf.sample). The storage service passes this configuration to your driver's Init method during the initialization loop.

Can I implement only specific write operations like upload but not delete?

Yes, CasaOS uses capability-based interfaces for write operations. You can implement only the Put interface for file uploads without implementing Remove, Mkdir, or Move, making the driver functional for uploads while preventing unsupported operations in the UI.

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 →