Use podman with Lima

Introduction

If you landed on this blog post, you are most likely migrating from Docker Desktop to another container tool. If you have chosen Podman Desktop as your alternative, this is the right place for you to learn how to take advantage of the compatibility of such tool with lima, the most famous tool to quickly and effortlessly run a virtualized Linux Machine.

This guide is based on macOS, but it can be of inspiration also for people trying to achieve the same thing on Windows (or why not, on Linux itself).

What is lima?

The term lima is a shorter for “linux machine”, and it is a CLI utility that can be quickly installed running:

brew install lima

This tool allows to specify, through a .yaml configuration file, how the Linux VM should look like. For example:

  • which Linux distribution to use;

  • cores, RAM and disk to allocate to the VM;

  • which commands to run at instantiation time as sudo user;

  • which commands to run at instantiation time as normal user;

  • how to create the connection from the VM to the host machine (through SSH tunneling or through socket).

lima comes with some predefined templates that the user can override. In our case we will be using a fedora template and then add on top our own customisations.

What is Podman?

podman is a container engine built by RedHat and publicly available for everyone. The tool is very similar to docker, especially in terms of CLI commands. However the podman engine can run in a unique mode called rootless, meaning that it doesn’t require any root permission to run (as opposed to docker, where the daemon runs as root).

If you have installed Podman Desktop on your machine, you may have already seen that it comes with:

  • a prepackaged VM (called podman-machine-default);

  • a set of extensions, one of which is the Lima extension. podman can indeed smoothly speak with other lima VMs once they are set.

Create a lima VM for podman

Here I share a template to create a lima VM which will be set to use runc as container runtime (as opposed to podman-machine-default which uses crun). This machine could be useful if you, for example, have some issues with the —-memory-swappiness option, which unfortunately will not work with crun .

The template is the following:

minimumLimaVersion: 1.1.0

# VM settings (adapt them to your needs)
cpus: 4
memory: "16GiB"
disk: "100GiB"

# base templates
base:
  - template://_images/fedora-41
  - template://_default/mounts

# disable containerd to be sure podman is the only container
# engine running in the system
containerd:
  system: false
  user: false

# scripts to setup your VM
provision:
    # script being run as sudo user
  - mode: system
    script: |
      #!/bin/bash
      set -eux -o pipefail
      command -v podman >/dev/null 2>&1 && test -e /etc/lima-podman && exit 0

      # Install podman AND runc
      dnf -y install --best podman runc podman-docker && touch /etc/lima-podman

      # Configure runc as default runtime
      mkdir -p /etc/containers
      printf '%s\n' \
        '[engine]' \
        'runtime = "runc"' \
        '' \
        '[engine.runtimes]' \
        'runc = ["/usr/bin/runc"]' \
        'crun = ["/usr/bin/crun"]' \
        > /etc/containers/containers.conf
    # script being run as normal user
  - mode: user
    script: |
      #!/bin/bash
      set -eux -o pipefail

      # Copy runtime config to user directory
      mkdir -p ~/.config/containers
      cp /etc/containers/containers.conf ~/.config/containers/

      # Your existing service enable
      systemctl --user enable --now podman.socket

# script performed to consider the VM healthy
probes:
  - script: |
      #!/bin/bash
      set -eux -o pipefail
      if ! timeout 30s bash -c "until command -v podman >/dev/null 2>&1; do sleep 3; done"; then
        echo >&2 "podman is not installed yet"
        exit 1
      fi
    hint: See "/var/log/cloud-init-output.log" in the guest

# connection mechanism between host and VM
portForwards:
  - guestSocket: "/run/user/{{.UID}}/podman/podman.sock"
    hostSocket: "{{.Dir}}/sock/podman.sock"

# message print at startup
message: |
  To run `podman` on the host (assumes podman-remote is installed), run the following commands:
  ------
  podman system connection add {{.Name}} "unix://{{.Dir}}/sock/podman.sock"
  podman system connection default {{.Name}}
  podman{{if eq .HostOS "linux"}} --remote{{end}} run quay.io/podman/hello
  ------

Take the template above and create a template:

vim ~/.lima/_config/podman.yaml

At this point, create the VM by running:

limactl create --name=podman ~/.lima/_config/podman.yaml

The name can be different, but if you name it podman as in the example you will see it also in Podman Desktop appearing, since the Lima extension expects the Lima VM to be called either podman or docker.

Once the VM has been created, start it with:

limactl start podman

If everything goes fine, you should now have a full Linux VM running on your machine. You can double check it by running:

limactl list
# example of output showing the podman machine up and running
NAME      STATUS     SSH                VMTYPE    ARCH       CPUS    MEMORY    DISK      DIR
podman    Running    127.0.0.1:57384    vz        aarch64    4       16GiB     100GiB    ~/.lima/podman

You can even log into the machine by running:

limactl shell podman

Connect podman with the Lima VM

At this point, you’re almost done. The only thing missing is to connect podman on the host side with the just created VM. To do it, run:

podman system connection add podman "unix:///$HOME/.lima/podman/sock/podman.sock"
podman system connection default podman

At this point, you’re good to go!

You can test now that everything runs as expected by running:

podman run --rm hello-world

Bonus 1: make podman compatible with docker

If you are used with the docker CLI in your scripts/programs, there is no need to switch them to podman since podman is fully compatible with docker.

If you want to achieve this, copy the following script as /usr/local/bin/docker:

#!/bin/sh
[ -e /etc/containers/nodocker ] || \
echo "Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg." >&2
exec podman "$@"

You can also go further and create a docker.sock with the following command:

sudo ln -s $HOME/.lima/podman/sock/podman.sock /var/run/docker.sock

Bonus 2: easily switch between the Lima VM and podman-machine-default

If you want to quickly switch the connection between our newly created Lima VM and the podman-machine-default VM, you can create copy these 2 quick commands in your .bashrc/.zshrc:

# let podman connect with the lima VM
podman-use-lima() {
    podman system connection default podman
    sudo rm /var/run/docker.sock
    sudo ln -s $HOME/.lima/podman/sock/podman.sock /var/run/docker.sock
}

# let podman connect to the podman-machine-default-root VM (standard podman VM)
podman-use-default() {
    podman system connection default podman-machine-default-root
    sudo rm /var/run/docker.sock
    sudo ln -s $HOME/.local/share/containers/podman/machine/podman.sock /var/run/docker.sock
}

And then simply run in your bash podman-use-lima to use the lima VM, or podman-use-default to switch back to the default VM.

Conclusion

In this blog we have covered how to quickly create a Lima VM that can be used with podman. The advantages of such VM can be manifold:

  • much more customisable than the default VM coming with Podman Desktop;

  • portable on multiple platforms;

  • fully backward-compatible with docker.

0
Subscribe to my newsletter

Read articles from Pasquale Convertini directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Pasquale Convertini
Pasquale Convertini

Programmer, hacker, blogger. I am also active on these platforms. "Every hacker has her fixation. You hack people. I hack time." (Whiterose)