joel_kleier

Running Vagrant using podman as a provider on MacOS M1

vagrant podman macos m1

VirtualBox, a staple of Vagrant’s default provisioning mechanism, doesn’t support ARM and has zero plans to. Docker has excellent support, but may not be the right tool (because of licensing, etc).

Podman is compatible alternative to Docker, can be aliased to the ‘docker’ command, and doesn’t come with the baggage of Docker. It uses qemu, and has excellent support for ARM.

system setup

This is what you need to do to prepare your system:

  1. get vagrant installed
  2. get podman installed
  3. alias podman to docker (for muscle memory and scripts that rely on ‘docker’ specifically)

Vagrant’s docker plugin does have special code for handling some podman-specific output, but it does require that the output of ‘docker –version’ includes the word ‘podman’ in it. It also checks to make sure that ‘docker’ is a command on the PATH, the process for which might not resolve the alias properly. Using a symlink instead of an alias will pass the PATH check, but will fail the ‘docker –version’ check.

In that case, a simple fix is to put this script somewhere on your PATH as docker:

#!/bin/bash
podman "$@"

init podman

Init your podman machine for vagrant usage:

$ podman machine init vagrant
$ podman machine start vagrant

note: podman can only have one running machine at a time, but creating a named machine for vagrant, specifically, allows you to start/stop it as you need, and keep all the vagrant specific contextual stuff separate, assuming that you want to.

create a Dockerfile that is equivalent to a vagrant box

Next you need a Dockerfile for vagrant to use as it’s target:

#
# NOTE: this is primarily derived from the Dockerfile found in this article:
# https://dev.to/taybenlor/running-vagrant-on-an-m1-apple-silicon-using-docker-3fh4
#
FROM amazonlinux:2

RUN yum -y install systemd systemd-libs initscripts
RUN yum -y update; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;

VOLUME [ "/sys/fs/cgroup" ]

RUN yum -y install openssh-server openssh-clients sudo
RUN sed -i -e 's/\(UsePAM \)yes/\1 no/' /etc/ssh/sshd_config
RUN ssh-keygen -A
RUN useradd --create-home -s /bin/bash vagrant
RUN echo -n 'vagrant:vagrant' | chpasswd
RUN echo 'vagrant ALL = NOPASSWD: ALL' > /etc/sudoers.d/vagrant
RUN chmod 440 /etc/sudoers.d/vagrant
RUN mkdir -p /home/vagrant/.ssh
RUN chmod 700 /home/vagrant/.ssh
# this is the insecure vagrant ssh (pub) key published here: https://github.com/hashicorp/vagrant/tree/main/keys
RUN echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key" > /home/vagrant/.ssh/authorized_keys
RUN chmod 600 /home/vagrant/.ssh/authorized_keys
RUN chown -R vagrant:vagrant /home/vagrant/.ssh
RUN sed -i -e 's/Defaults.*requiretty/#&/' /etc/sudoers
RUN systemctl enable sshd.service

RUN mkdir /var/run/sshd
EXPOSE 22

CMD ["/usr/sbin/sshd", "-D"]

create a Vagrantfile for your project

And you need a Vagrantfile for vagrant to use:

Vagrant.configure("2") do |config|
  # for a "normal" provider, like VirtualBox, using a vm
  config.vm.box = "ubuntu/bionic64"

  # for the docker/podman provider, specifically
  config.vm.provider "docker" do |d, override|
    # need to tell vagrant not to try and manage a box of any kind
    override.vm.box = nil

    override.ssh.insert_key = true

    d.build_dir = "."
    d.dockerfile = "Dockerfile.vagrant"
    d.has_ssh = true
    d.privileged = true

    # need to tell podman to forward the ssh port
    override.vm.network "forwarded_port", guest: 22, host: 2222
  end

  # open up to the folder that should be mounted to a folder on the podman
  # machine, which might be itself mapped to a volume on the local host system
  # depending on your podman machine configuration
  config.ssh.extra_args = ["-t", "cd /vagrant; bash --login"]

  # for an app running in the vagrant controlled container
  #config.vm.network "forwarded_port", guest: 8000, host: 8000

  config.vm.provision "shell", inline: <<-SHELL
    # fun init stuff here
  SHELL
end

run vagrant

Finally, assuming all went well, you can run a vagrant config using docker to emulate a VM, like so:

$ vagrant up --provider=docker
$ vagrant ssh