# How CasaOS Handles the Database Migration Tool: Architecture and Implementation

> Discover how CasaOS manages database migrations with its plugin architecture. Learn about the four-method interface for version detection and lifecycle steps like PreMigrate, Migrate, and PostMigrate.

- Repository: [IceWhale/CasaOS](https://github.com/IceWhaleTech/CasaOS)
- Tags: architecture
- Published: 2026-06-28

---

**CasaOS isolates database migration logic behind a plugin-style framework that uses a four-method interface to detect version mismatches and execute PreMigrate, Migrate, and PostMigrate lifecycle steps only when necessary.**

CasaOS implements a modular database migration strategy that separates complex schema changes from routine updates. The system uses a dedicated migration tool binary that implements the `MigrationTool` interface to handle non-trivial upgrades while relying on GORM's AutoMigrate for standard schema synchronization.

## The MigrationTool Interface Contract

At the core of CasaOS's migration framework lies the `MigrationTool` interface defined in [[`interfaces/migrationTool.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/interfaces/migrationTool.go)](https://github.com/IceWhaleTech/CasaOS/blob/main/interfaces/migrationTool.go). This contract abstracts any concrete migration implementation, allowing the system to evolve its database schema without hard-coding version checks throughout the codebase.

The interface declares four lifecycle methods:

```go
type MigrationTool interface {
    IsMigrationNeeded() (bool, error) // Does the current DB version need a migration?
    PreMigrate() error               // Prepare the environment (e.g., backup)
    Migrate() error                  // Perform the actual schema/data changes
    PostMigrate() error              // Clean-up, re-index, etc.
}

```

**`IsMigrationNeeded()`** returns a boolean indicating whether the current database state requires intervention. **`PreMigrate()`** handles preparatory work such as creating backups or validating prerequisites. **`Migrate()`** executes the actual schema or data transformations. **`PostMigrate()`** performs cleanup operations like re-indexing or optimizing tables after changes complete.

## How the Migration Tool Binary Orchestrates Execution

The executable responsible for running migrations resides in [[`cmd/migration-tool/main.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/cmd/migration-tool/main.go)](https://github.com/IceWhaleTech/CasaOS/blob/main/cmd/migration-tool/main.go). This binary coordinates the selection and execution of migration tools through a structured discovery process.

The binary builds a slice named `migrationTools` containing objects that satisfy the `MigrationTool` interface. It iterates over this collection, invoking `IsMigrationNeeded()` on each candidate. The first tool returning `true` becomes the `selectedMigrationTool`. If no tool reports a required migration, the program logs that `selectedMigrationTool is null` and exits gracefully.

When a migration is required, the binary executes the three lifecycle steps in strict order:

1. `PreMigrate()` - Prepare the environment
2. `Migrate()` - Apply schema or data changes
3. `PostMigrate()` - Perform cleanup

This sequential approach ensures that complex migrations involving data massaging or schema rewrites occur atomically before the main CasaOS service starts.

## The Dummy Implementation and Extensibility

CasaOS provides a default no-op implementation in [[`cmd/migration-tool/migration_dummy.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/cmd/migration-tool/migration_dummy.go)](https://github.com/IceWhaleTech/CasaOS/blob/main/cmd/migration-tool/migration_dummy.go). This placeholder ensures the binary functions correctly when no real migration code exists for the current version.

```go
type migrationTool struct{}
func (u *migrationTool) IsMigrationNeeded() (bool, error) { return false, nil }
func (u *migrationTool) PreMigrate() error               { return nil }
func (u *migrationTool) Migrate() error                  { return nil }
func (u *migrationTool) PostMigrate() error              { return nil }
func NewMigrationDummy() interfaces.MigrationTool { return &migrationTool{} }

```

The dummy implementation always returns `false` for `IsMigrationNeeded()`, effectively bypassing the migration pipeline. To add a concrete migration, developers create a new struct implementing the interface and register it in the `migrationTools` slice alongside the dummy.

## Integration with GORM AutoMigrate

While the migration tool handles complex upgrades, CasaOS relies on GORM's `AutoMigrate` for routine schema synchronization. The database connection initializes in [[`pkg/sqlite/db.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/pkg/sqlite/db.go)](https://github.com/IceWhaleTech/CasaOS/blob/main/pkg/sqlite/db.go), where the code calls `db.AutoMigrate(&model.App{}, …)` immediately after opening the SQLite file.

This creates a two-tier migration strategy:

- **GORM AutoMigrate**: Handles automatic table creation and simple column additions during service startup
- **Migration Tool Framework**: Serves as a fallback for non-trivial upgrades requiring data transformation, conditional logic, or schema rewrites that cannot be expressed through GORM's automatic synchronization

## Creating a Custom Migration Step

To implement a new database migration, create a file following the pattern of [`migration_dummy.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/migration_dummy.go) and implement the four required methods. For example, adding a version 2 migration that introduces an `owner_id` column and backfills data:

```go
// cmd/migration-tool/migration_v2.go
package main

import (
    "github.com/IceWhaleTech/CasaOS-Common"
    "github.com/IceWhaleTech/CasaOS/pkg/sqlite"
    "gorm.io/gorm"
)

type migrationV2 struct {
    db *gorm.DB
}

func (m *migrationV2) IsMigrationNeeded() (bool, error) {
    // Check the existence of the new column or a version flag in a meta table
    return !sqlite.HasColumn(m.db, "apps", "owner_id"), nil
}
func (m *migrationV2) PreMigrate() error { return nil } // optional backup
func (m *migrationV2) Migrate() error {
    return m.db.Migrator().AddColumn(&model.App{}, "owner_id")
}
func (m *migrationV2) PostMigrate() error {
    // Populate owner_id for existing rows
    return m.db.Model(&model.App{}).Where("owner_id IS NULL").Update("owner_id", 0).Error
}
func NewMigrationV2(db *gorm.DB) interfaces.MigrationTool {
    return &migrationV2{db: db}
}

```

Register the new migration in [[`cmd/migration-tool/main.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/cmd/migration-tool/main.go)](https://github.com/IceWhaleTech/CasaOS/blob/main/cmd/migration-tool/main.go) by adding it to the `migrationTools` slice:

```go
migrationTools := []interfaces.MigrationTool{
    NewMigrationDummy(),
    NewMigrationV2(sqliteDB), // ← new step becomes visible to the tool
}

```

When CasaOS upgrades, running the `casaos-migration-tool` binary will automatically detect the version mismatch through `IsMigrationNeeded()` and apply the V2 changes before the service starts.

## Summary

- **CasaOS uses a plugin-style `MigrationTool` interface** defined in [`interfaces/migrationTool.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/interfaces/migrationTool.go) to abstract database migration logic away from core service code.
- **The migration tool binary** in [`cmd/migration-tool/main.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/cmd/migration-tool/main.go) selects the first tool where `IsMigrationNeeded()` returns true and executes PreMigrate, Migrate, and PostMigrate in sequence.
- **A dummy implementation** provides a safe no-op default when no migrations are required, ensuring the binary exits gracefully.
- **GORM AutoMigrate** handles routine schema updates automatically, while the migration tool framework exists specifically for complex data transformations and schema rewrites.
- **Adding new migrations** requires implementing the four-method interface and registering the constructor in the binary's migration tool slice.

## Frequently Asked Questions

### What is the MigrationTool interface in CasaOS?

The `MigrationTool` interface is a Go contract defined in [`interfaces/migrationTool.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/interfaces/migrationTool.go) that specifies four methods: `IsMigrationNeeded()`, `PreMigrate()`, `Migrate()`, and `PostMigrate()`. This interface allows CasaOS to treat database migrations as pluggable components, enabling the system to support multiple migration strategies without modifying the core orchestration logic.

### How does CasaOS decide which migration to run?

The migration tool binary iterates over a slice of `MigrationTool` implementations in [`cmd/migration-tool/main.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/cmd/migration-tool/main.go). It calls `IsMigrationNeeded()` on each tool in order, and the first tool returning `true` becomes the selected migration. This selection mechanism ensures that only necessary migrations execute, and they run in a predictable priority order based on their position in the slice.

### What is the difference between the migration tool and GORM AutoMigrate?

**GORM AutoMigrate**, called in [`pkg/sqlite/db.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/pkg/sqlite/db.go), automatically creates tables and adds missing columns during service startup, handling simple schema changes. The **migration tool framework** handles complex scenarios requiring data manipulation, conditional logic, or destructive schema changes that cannot be safely performed by GORM's automatic synchronization. CasaOS runs the migration tool before starting the service, then relies on AutoMigrate for final synchronization.

### How do I add a new database migration to CasaOS?

Create a new file in `cmd/migration-tool/` implementing the `MigrationTool` interface with your specific logic for detecting and applying changes. Implement `IsMigrationNeeded()` to check for the specific schema version or missing columns, then add your constructor to the `migrationTools` slice in [`cmd/migration-tool/main.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/cmd/migration-tool/main.go). The binary will automatically include your migration in the selection process during the next upgrade cycle.