Ctrl Plane

Instances

Instance lifecycle management -- creating, scaling, suspending, and destroying tenant instances.

An instance is the core unit of work in Ctrl Plane. It represents a running application belonging to a tenant, backed by a container or virtual machine in a cloud provider.

The Instance entity

type Instance struct {
    ctrlplane.Entity

    TenantID       string                `json:"tenant_id"       db:"tenant_id"`
    Name           string                `json:"name"            db:"name"`
    Slug           string                `json:"slug"            db:"slug"`
    ProviderName   string                `json:"provider_name"   db:"provider_name"`
    ProviderRef    string                `json:"provider_ref"    db:"provider_ref"`
    Region         string                `json:"region"          db:"region"`
    State          provider.InstanceState `json:"state"           db:"state"`
    Image          string                `json:"image"           db:"image"`
    Resources      provider.ResourceSpec  `json:"resources"       db:"resources"`
    Env            map[string]string      `json:"env"             db:"env"`
    Ports          []provider.PortSpec    `json:"ports"           db:"ports"`
    Endpoints      []provider.Endpoint    `json:"endpoints"       db:"endpoints"`
    Labels         map[string]string      `json:"labels"          db:"labels"`
    CurrentRelease id.ID                  `json:"current_release,omitzero" db:"current_release"`
    SuspendedAt    *time.Time             `json:"suspended_at"    db:"suspended_at"`
}

State machine

Instances move through a well-defined set of states. The state machine validates every transition and rejects invalid ones with ctrlplane.ErrInvalidState.

provisioning -> starting -> running -> stopping -> stopped
                  |           |                      |
                  |           +-> suspending -> suspended
                  |           |
                  |           +-> destroying -> destroyed
                  |
                  +-> failed
StateMeaning
provisioningProvider is creating the underlying resource
startingResource exists, application is booting
runningApplication is healthy and serving traffic
stoppingGraceful shutdown in progress
stoppedApplication is not running but resources are retained
suspendingTransitioning to suspended state
suspendedInstance is frozen (tenant suspension or manual)
destroyingResources are being torn down
destroyedFully cleaned up, terminal state
failedSomething went wrong (can retry from this state)

Service interface

The instance.Service interface provides all instance operations:

type Service interface {
    Create(ctx context.Context, req CreateRequest) (*Instance, error)
    Get(ctx context.Context, instanceID id.ID) (*Instance, error)
    List(ctx context.Context, opts ListOptions) (*ListResult, error)
    Update(ctx context.Context, instanceID id.ID, req UpdateRequest) (*Instance, error)
    Delete(ctx context.Context, instanceID id.ID) error

    Start(ctx context.Context, instanceID id.ID) error
    Stop(ctx context.Context, instanceID id.ID) error
    Restart(ctx context.Context, instanceID id.ID) error
    Scale(ctx context.Context, instanceID id.ID, req ScaleRequest) error
    Suspend(ctx context.Context, instanceID id.ID, reason string) error
    Unsuspend(ctx context.Context, instanceID id.ID, reason string) error
}

Creating an instance

instance, err := cp.Instances.Create(ctx, instance.CreateRequest{
    TenantID:     "ten_01h455vb...",
    Name:         "api-server",
    Image:        "myapp:v1.2.0",
    ProviderName: "docker",
    Resources: provider.ResourceSpec{
        CPUMillis: 1000,
        MemoryMB:  512,
    },
    Ports: []provider.PortSpec{
        {ContainerPort: 8080, Protocol: "tcp"},
    },
    Env: map[string]string{
        "DATABASE_URL": "postgres://...",
    },
})

This triggers a chain of actions:

  1. Validate the request and check tenant quotas.
  2. Create an Instance record in state provisioning.
  3. Call provider.Provision() to create the underlying resource.
  4. Transition to starting, then running as the provider reports success.
  5. Publish an InstanceCreated event.

Scaling

Adjust CPU, memory, or disk without redeploying:

err := cp.Instances.Scale(ctx, instanceID, instance.ScaleRequest{
    Resources: provider.ResourceSpec{
        CPUMillis: 2000,
        MemoryMB:  1024,
    },
})

Lifecycle events

Every state change publishes an event through the event bus:

EventWhen
InstanceCreatedInstance is provisioned
InstanceStartedInstance reaches running state
InstanceStoppedInstance is stopped
InstanceFailedInstance enters failed state
InstanceDeletedInstance is destroyed
InstanceScaledResources are changed
InstanceSuspendedInstance is suspended
InstanceUnsuspendedInstance is resumed

On this page