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, andDrop(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)andGetInfo(ctx context.Context) (string, string, string, error). - Optional Writer Interfaces: Implement
Writer,Mkdir,Move,Rename,Copy,Remove, orPutonly 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:
internal/driver/driver.go– Core interface definitions (Meta,Reader,User)internal/driver/config.go– Configuration struct used by all driversmodel/storage.go– Generic storage models (StorageA,Obj)drivers/onedrive/drive.go– Full-featured reference driverdrivers/dropbox/drive.go– Alternative API style exampleservice/storage.go– Driver initialization and management logicconf/conf.conf.sample– Configuration schema examples
Testing Your Custom Storage Driver
- Unit Testing: Create
drivers/mycloud/driver_test.goto exerciseInit,List, and any write methods in isolation. - Integration Testing: Run
go test ./...from the project root. The storage service will attempt to load your driver if the configuration block is present. - 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 ininternal/driver/driver.go. - Register the driver in the
Newfactory function withininternal/driver/driver.goto enable instantiation by name. - Add configuration entries in
conf/conf.conf.sampleso 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.gofor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →