Instance is the most complex resource due to device handling and UID/GID shifting for volumes.
type InstanceConfig struct {
Type incusApi.InstanceType // container or vm
Full bool // fetch full instance details
Image string // image name (required)
Resources []Resource // dependencies that must be ensured
Devices []InstanceDevice // pre-creation devices
PostDevices []InstanceDevice // post-creation devices (need UID/GID)
Config map[string]string // instance config
ExtraDevices map[string]map[string]string // raw Incus devices
}
Devices are configuration structs attached to instances:
const (
InstanceDeviceTypeProxy = "proxy"
InstanceDeviceTypeDisk = "disk"
InstanceDeviceTypeNic = "nic"
)
type InstanceDevice struct {
Name string
Config InstanceDeviceConfig
}
type InstanceDeviceConfig struct {
DeviceType string
Network Resource // for nic
Proxy InstanceDeviceProxyConfig // for proxy
Disk InstanceDeviceDiskConfig // for disk
Extensions map[string]string // raw keys: whole config for custom devices, overrides for typed
}
Port forwarding:
InstanceDeviceProxyConfig{
ListenType: "tcp",
ListenAddr: "0.0.0.0",
ListenPort: 8080,
ConnectType: "tcp",
ConnectAddr: "127.0.0.1",
ConnectPort: 80,
Nat: true,
}
Storage volumes or bind mounts:
// Named volume
InstanceDeviceDiskConfig{
StorageVolumeConfig: &StorageVolumeConfig{...},
Source: "myvolume",
Path: "/data",
Shift: true,
}
// Bind mount (StorageVolumeConfig is nil)
InstanceDeviceDiskConfig{
Source: "/host/path",
Path: "/container/path",
ReadOnly: true,
Shift: true,
}
Network attachment:
InstanceDeviceConfig{
DeviceType: InstanceDeviceTypeNic,
Network: network, // reference to Network resource
}
Attached at instance creation:
Attached after instance creation:
Post-devices require UID/GID from the created instance to configure proper ownership.
1. Check if instance exists
|-- Found: store reference, extract UID/GID, return
|-- Not found + Create=false: return ErrNotFound
2. Validate bind mounts (reject if remote connection)
3. Check all Resources are ensured
|-- Any not ensured: return ErrDependencyNotEnsured
4. Build pre-devices map
|-- Convert Devices + ExtraDevices to Incus format
|-- Add root disk if not in profile
5. Get image from resource store
6. Create instance from cache image
|-- CreateInstanceFromImage(cache, imgInfo, req)
7. Extract UID/GID from created instance
|-- Read oci.uid, oci.gid from config
8. Process post-devices (volumes)
|-- For each PostDevice where DeviceType=disk:
|-- If StorageVolumeConfig != nil:
|-- Set Shifted=true, UID, GID on volume
|-- StorageVolume.Ensure()
|-- Convert to Incus device format
9. Update instance with post-devices
|-- UpdateInstance() with new devices map
OCI images contain user metadata:
oci.uid = 1000
oci.gid = 1000
When creating storage volumes for the instance:
volConfig.Shifted = true
volConfig.UID = inst.UID
volConfig.GID = inst.GID
This ensures files in the volume are owned by the correct user inside the container.
Bind mounts only work with local (Unix socket) connections:
if dev.Config.Disk.StorageVolumeConfig == nil && client.IsRemote() {
return ErrBindMountRemote
}
The source path must be accessible to the Incus server.
err := instance.Ensure(client.OptionCreate())
Fetches existing or creates new. Cascades to dependencies via Resources field.
err := instance.Start()
Calls UpdateInstanceState with action "start". No-op if already running.
err := instance.Stop(client.OptionForce())
Calls UpdateInstanceState with action "stop". Force bypasses graceful shutdown.
err := instance.Delete(client.OptionForce())
Deletes the instance. Clears internal state.
When Config.Full = true, Ensure fetches additional data:
if r.Config.Full {
// Fetch image alias
r.IncusImageAlias = image.IncusAlias
// Fetch full instance with state and snapshots
r.IncusInstanceFull, _, _ = client.GetInstanceFull(name)
}
Used by the list command to display detailed information.
Dependencies are passed via InstanceConfig.Resources:
instanceConfig := &InstanceConfig{
Image: imageName,
Resources: []Resource{image, network1, network2},
Devices: devices,
}
Instance.Ensure() checks all Resources are ensured before creating. It does not cascade Ensure calls - Stack.Run() ensures dependencies are ensured first via priority-based ordering. Resources with lower priority values (images, networks) are ensured before higher priority values (instances).