How the CasaOS Relay Service Works for Data Relay: A Technical Deep Dive

The CasaOS relay service is a thin data-access layer that stores and retrieves relationships between user-defined custom identifiers and their underlying Docker containers using GORM.

The CasaOS relay service (implemented as RelyService in the IceWhaleTech/CasaOS repository) provides a lightweight persistence mechanism for mapping high-level application identifiers to actual Docker container IDs. This architectural component enables the rest of the system to locate and interact with containers using stable, user-friendly names rather than volatile container hashes. By leveraging GORM as its database abstraction layer, the service automatically inherits transaction handling, connection pooling, and support for both SQLite and MySQL backends.

Core Architecture and Interface Design

The relay service follows a clean interface-based design that separates the contract definition from the concrete implementation.

The RelyService Interface Definition

In service/rely.go (lines 18-22), the RelyService interface defines the three core operations available to callers:

type RelyService interface {
    Create(rely model.RelyDBModel)
    GetInfo(id string) model.RelyDBModel
    Delete(id string)
}

This minimal interface provides everything needed to manage the lifecycle of a relay mapping: creating records, retrieving container information by custom ID, and cleaning up obsolete entries.

Database Model Structure

The underlying data structure is defined in service/model/o_rely.go (lines 7-22) as the RelyDBModel struct:

type RelyDBModel struct {
    ID                uint `gorm:"primarykey"`
    CustomId          string
    ContainerCustomId string
    ContainerId       string
    Type              int
    CreatedAt         time.Time
    UpdatedAt         time.Time
    DeletedAt         gorm.DeletedAt `gorm:"index"`
}

This GORM model maps directly to the o_rely table, storing the custom identifier (CustomId), the actual Docker container ID (ContainerId), and a computed container custom ID (ContainerCustomId). The Type field supports future extensibility, with available constants defined in types/rely.go (line 3), including RELY_TYPE_MYSQL for potential multi-backend support.

Data Flow and CRUD Operations

The relyService implementation (lines 24-51 in service/rely.go) wraps a *gorm.DB instance and executes standard CRUD operations against the o_rely table.

Service Initialization and Dependency Injection

When CasaOS bootstraps its service layer via NewService in service/service.go (lines 59-61), the relay service is instantiated with the same database connection used throughout the application:

rely: NewRelyService(db),

This ensures the relay service participates in the same connection pool and transaction context as other data services.

Creating Relay Records

The Create method receives a populated RelyDBModel and persists it using GORM's Create method:

func (r *relyService) Create(rely model.RelyDBModel) {
    r.db.Create(&rely)
}

This inserts a new row into the o_rely table, establishing the mapping between the user-defined custom ID and the Docker container identifier.

Querying and Deleting Mappings

Retrieval operations use the GetInfo method, which queries by custom_id and returns the first matching record:

func (r *relyService) GetInfo(id string) model.RelyDBModel {
    var m model.RelyDBModel
    r.db.Where("custom_id = ?", id).First(&m)
    return m
}

Deletion is handled similarly via the Delete method, which removes rows matching the provided custom ID:

func (r *relyService) Delete(id string) {
    var c model.RelyDBModel
    r.db.Where("custom_id = ?", id).Delete(&c)
}

Integration with the Repository Pattern

The relay service is exposed to the rest of the application through the central Repository interface defined in service/service.go (lines 33-40 and 109-111). This aggregation pattern allows higher-level components—such as REST API controllers in route/v2—to access relay functionality via MyService.Rely().

The repository structure (lines 59-61) shows how the service is wired:

type repository struct {
    // ... other services
    rely RelyService
}

func (r *repository) Rely() RelyService {
    return r.rely
}

This design decouples the persistence logic from the HTTP handlers, ensuring that controllers only interact with the interface contract rather than direct database implementations.

Working with the Relay Service: Code Examples

Below are practical examples demonstrating how to interact with the CasaOS relay service programmatically.

Initializing the Service

Normally instantiated by the main application bootstrap, but can be created manually for testing:

import (
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "github.com/IceWhaleTech/CasaOS/service"
)

func initRelyService() service.RelyService {
    db, _ := gorm.Open(sqlite.Open("casaos.db"), &gorm.Config{})
    // Migrate the schema (ensures o_rely exists)
    db.AutoMigrate(&service.model.RelyDBModel{}) // model path is service/model
    return service.NewRelyService(db)
}

Creating a Relay Mapping

func addRelay(svc service.RelyService, customID, containerID string) {
    relay := service.model.RelyDBModel{
        CustomId:          customID,
        ContainerCustomId: "casaos-" + customID,
        ContainerId:       containerID,
        Type:              service.types.RELY_TYPE_MYSQL, // currently 0
    }
    svc.Create(relay)
}

Retrieving Relay Information

func getRelay(svc service.RelyService, customID string) *service.model.RelyDBModel {
    info := svc.GetInfo(customID)
    return &info
}

Deleting a Relay Record

func deleteRelay(svc service.RelyService, customID string) {
    svc.Delete(customID)
}

Accessing via Global Repository

// Assume MyService has been initialised by the main application
relayInfo := MyService.Rely().GetInfo("my-custom-app")
fmt.Printf("Container ID: %s\n", relayInfo.ContainerId)

Summary

  • The CasaOS relay service acts as a thin data-access layer between user-defined identifiers and Docker container IDs, implemented in service/rely.go.
  • It exposes three core operations (Create, GetInfo, Delete) through a clean interface definition located at lines 18-22 of service/rely.go.
  • Data persistence uses GORM with the RelyDBModel struct (defined in service/model/o_rely.go lines 7-22) mapping to the o_rely table.
  • The service is integrated via the Repository pattern in service/service.go (lines 59-61), allowing access through MyService.Rely().
  • It supports extensible relay types through constants in types/rely.go (line 3), though currently the default type is used for standard Docker container mappings.

Frequently Asked Questions

What is the primary purpose of the CasaOS relay service?

The CasaOS relay service provides a persistent mapping layer that links user-defined custom identifiers to actual Docker container IDs. This allows the system and API consumers to reference containers using stable, human-readable names rather than volatile Docker hashes, while the service handles the translation to physical container references.

Which database table stores the relay mappings?

All relay records are stored in the o_rely table, defined by the RelyDBModel struct in service/model/o_rely.go (lines 7-22). This table includes fields for CustomId, ContainerId, ContainerCustomId, and a Type field for future extensibility, along with standard GORM timestamps.

How does the relay service handle different database types?

The relay service itself is database-agnostic, relying on GORM for all persistence operations. It accepts a *gorm.DB instance during initialization (as seen in service/service.go lines 59-61), which means it automatically inherits whatever database driver the main CasaOS application configures—whether SQLite for local deployments or MySQL for distributed setups. The RELY_TYPE_MYSQL constant in types/rely.go (line 3) suggests future support for type-specific relay logic, though currently the service uses a unified approach.

Where is the relay service initialized in the CasaOS architecture?

The relay service is instantiated within the NewService factory function in service/service.go (lines 59-61), where it receives the shared database connection. It is then exposed through the Repository interface at lines 109-111, making it available to HTTP controllers and other services via the Rely() method on the global service instance.

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 →