@@ -468,7 +468,10 @@ func (i *Incus) RestoreSnapshot(ctx context.Context, name, label string) error {
468468 return nil
469469}
470470
471- // CloneFrom copies an instance from a source snapshot into a new instance.
471+ // CloneFrom copies an instance from a source snapshot into a new instance,
472+ // then starts it. Volatile keys (notably volatile.eth0.hwaddr) are stripped
473+ // so Incus regenerates them on the destination — otherwise the clone would
474+ // boot with a duplicate MAC and fail eth0 validation.
472475func (i * Incus ) CloneFrom (ctx context.Context , source , label , newName string ) error {
473476 sourceFull := prefixed (source )
474477 newFull := prefixed (newName )
@@ -479,6 +482,15 @@ func (i *Incus) CloneFrom(ctx context.Context, source, label, newName string) er
479482 return fmt .Errorf ("getting source instance: %w" , err )
480483 }
481484
485+ // Strip volatile.* keys: these encode per-instance identity (MAC, idmap,
486+ // last_state, etc.) and must not survive into the clone. Matches the
487+ // behaviour of the `incus copy` CLI.
488+ for k := range sourceInst .Config {
489+ if strings .HasPrefix (k , "volatile." ) {
490+ delete (sourceInst .Config , k )
491+ }
492+ }
493+
482494 // Copy from snapshot.
483495 sourceInst .Name = sourceFull + "/" + label
484496 op , err := i .server .CopyInstance (i .server , * sourceInst , & incusclient.InstanceCopyArgs {
@@ -492,6 +504,13 @@ func (i *Incus) CloneFrom(ctx context.Context, source, label, newName string) er
492504 return fmt .Errorf ("waiting for copy: %w" , err )
493505 }
494506
507+ // Callers (BuildBase, create_sandbox) expect a clone to be running on
508+ // return — Ready polls for state.Running. Match the TrueNAS backend and
509+ // the post-Create flow above.
510+ if err := i .Start (ctx , newName ); err != nil {
511+ return fmt .Errorf ("starting clone: %w" , err )
512+ }
513+
495514 return nil
496515}
497516
0 commit comments