Skip to content

Create smaller VM qcow2 disk images#7

Merged
Nino-K merged 4 commits intorancher-sandbox:mainfrom
mook-as:reduce-image-size
Dec 10, 2025
Merged

Create smaller VM qcow2 disk images#7
Nino-K merged 4 commits intorancher-sandbox:mainfrom
mook-as:reduce-image-size

Conversation

@mook-as
Copy link
Contributor

@mook-as mook-as commented Nov 13, 2025

This reduces the disk image size by replacing some things with in-house equivalents that only does what we need:

  • Replace kiwi's partition resizing dracut module with one specific to our setup
  • Use a different kernel package that doesn't include kernel modules for things that won't be connected to a VM
  • Drop cloud-init for a small-ish golang program

The combination of the first and third allows us to drop python from the image. But I'm not sure doing a full replacement of cloud-init is a good thing to do here.

FWIW, looking at the tar output (because that's easier), /usr/local/bin has:

-rwxr-xr-x root/root     4.2Mi 2025-11-12 15:30 usr/local/bin/vm-switch
-rwxr-xr-x root/root     2.7Mi 2025-11-12 15:30 usr/local/bin/wsl-proxy
-rwxr-xr-x root/root      57Mi 2015-10-20 17:00 usr/local/bin/buildkitd
-rwxr-xr-x root/root      50Mi 2025-11-12 15:32 usr/local/bin/rancher-desktop-guest-agent
-rwxr-xr-x 1001/483       45Mi 2024-07-11 12:41 usr/local/bin/cri-dockerd
-rwxr-xr-x root/root        55 2025-11-12 16:13 usr/local/bin/nerdctl
-rwxr-xr-x root/root      29Mi 2015-10-20 17:00 usr/local/bin/buildctl
-rwxr-xr-x root/root     1.1Ki 2025-10-29 10:56 usr/local/bin/wsl-exec
-rwxr-xr-x root/root     4.1Mi 2025-11-12 15:52 usr/local/bin/rd-init
-rwxr-xr-x root/root     3.5Mi 2025-11-12 15:30 usr/local/bin/network-setup

So that amount isn't going away, though we may need to figure out how to slim down rancher-desktop-guest-agent. We probably have too many copies of the Kubernetes client around.

This lets us potentially drop python dependencies once we get rid of
cloud-init.

Signed-off-by: Mark Yen <mark.yen@suse.com>
Switch to kernel-default-base to avoid pulling in kernel modules that will
never be used in a VM.

Signed-off-by: Mark Yen <mark.yen@suse.com>
This drops cloud-init and switches to a minimal golang implementation that
is just enough to read what lima outputs.  This lets us drop all of python
in the image.

We also switch to systemd-networkd instead of NetworkManager because it is
slightly easier to configure in this scenario.

Signed-off-by: Mark Yen <mark.yen@suse.com>
@mook-as mook-as requested a review from Nino-K December 8, 2025 17:49
if err != nil {
return fmt.Errorf("failed to start unit %s: %w", unit, err)
}
slog.InfoContext(ctx, "restarted systemd unit", "unit", unit, "result", <-ch)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to consider adding context cancellation statement

select {
case result := <-ch:
    slog.InfoContext(ctx, "restarted systemd unit", "unit", unit, "result", result)
case <-ctx.Done():
    return fmt.Errorf(...)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, added.

// Notify ready before we reload the other units; otherwise we end up
// blocking startup due to a loop with systemd-networkd.
if _, err := daemon.SdNotify(true, daemon.SdNotifyReady); err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this one return fmt.Errorf("failed to notify systemd: %w", err)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that makes sense; changed.

)

// Load /mnt/lima-cidata/meta-data
func LoadMetadata(ctx context.Context) ([]string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can only assume that we are forced to adhere to function signature that returns ([]string, error) because []string is not used at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return nil, fmt.Errorf("failed to unmarshal meta-data file: %w", err)
}
slog.InfoContext(ctx, "setting host name", "hostname", metaData.LocalHostName)
if err := unix.Sethostname([]byte(metaData.LocalHostName)); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the hostname already validated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't make much sense for us to validate it (more than would otherwise be done); we don't have any specific requirements other than whatever the kernel supports. This is generally generated by Lima anyway, so we don't actually care.

Note that since this is golang, we don't use the native libc and just use the syscall directly; this means we end up calling the implementation, which doesn't appear to do much checking.

https://github.com/torvalds/linux/blob/7d0a66e4bb9081d75c82ec4957c50034cb0ea449/kernel/sys.c#L1419-L1443

LocalHostName string `yaml:"local-hostname"`
}

file, err := os.Open("/mnt/lima-cidata/meta-data")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This /mnt/lima-cidata/meta-data might be clearer as a constant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


func LoadNetworkConfig(ctx context.Context) ([]string, error) {
hasChanges := false
file, err := os.Open("/mnt/lima-cidata/network-config")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be a constant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also done.

err := os.WriteFile(
fmt.Sprintf("/etc/sudoers.d/90-lima-user-%s", userEntry.Name),
[]byte(userEntry.Name + " " + userEntry.Sudo),
0o644)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be 0o440 or 0o400 read only by root/group?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The monitoring-plugins-zypper package contains a /etc/sudoers.d/check_zypper that's 0400, I'll follow that.

Signed-off-by: Mark Yen <mark.yen@suse.com>
Copy link
Contributor Author

@mook-as mook-as left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes made in 4f411d9

if err != nil {
return fmt.Errorf("failed to start unit %s: %w", unit, err)
}
slog.InfoContext(ctx, "restarted systemd unit", "unit", unit, "result", <-ch)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, added.

// Notify ready before we reload the other units; otherwise we end up
// blocking startup due to a loop with systemd-networkd.
if _, err := daemon.SdNotify(true, daemon.SdNotifyReady); err != nil {
return err
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that makes sense; changed.

)

// Load /mnt/lima-cidata/meta-data
func LoadMetadata(ctx context.Context) ([]string, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LocalHostName string `yaml:"local-hostname"`
}

file, err := os.Open("/mnt/lima-cidata/meta-data")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return nil, fmt.Errorf("failed to unmarshal meta-data file: %w", err)
}
slog.InfoContext(ctx, "setting host name", "hostname", metaData.LocalHostName)
if err := unix.Sethostname([]byte(metaData.LocalHostName)); err != nil {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't make much sense for us to validate it (more than would otherwise be done); we don't have any specific requirements other than whatever the kernel supports. This is generally generated by Lima anyway, so we don't actually care.

Note that since this is golang, we don't use the native libc and just use the syscall directly; this means we end up calling the implementation, which doesn't appear to do much checking.

https://github.com/torvalds/linux/blob/7d0a66e4bb9081d75c82ec4957c50034cb0ea449/kernel/sys.c#L1419-L1443


func LoadNetworkConfig(ctx context.Context) ([]string, error) {
hasChanges := false
file, err := os.Open("/mnt/lima-cidata/network-config")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also done.

err := os.WriteFile(
fmt.Sprintf("/etc/sudoers.d/90-lima-user-%s", userEntry.Name),
[]byte(userEntry.Name + " " + userEntry.Sudo),
0o644)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The monitoring-plugins-zypper package contains a /etc/sudoers.d/check_zypper that's 0400, I'll follow that.

@mook-as mook-as requested a review from Nino-K December 9, 2025 21:26
@Nino-K Nino-K merged commit c57f78e into rancher-sandbox:main Dec 10, 2025
5 checks passed
@mook-as mook-as deleted the reduce-image-size branch February 9, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants