An alternative Docker installation with Multipass on macOS without using Docker for Mac
Tags: docker, multipass, macos
Published: 2022-01-29 18:44:56

Last week I received an email from the Docker Team which said that Docker for Mac (the software which also comes with a GUI) will be forbidden for commercial use when the company has more than 250 employees AND makes more than $10 million per year. To use it commercially the company has to get licenses for every developer using it, starting at $5/month. This made me think what an alternative could be for devs that don’t want to use Docker for Mac anymore, since I read a lot of posts that many devs don’t even need it. Most of them interact via CLI anyway. I stumbled across [a nice article from Josh Gorneau](https://medium.com/@gourneau_38759/docker-engine-with-multipass-on-macos-dc44ff09ffb2) where he uses multipass to host his Docker VM. In this case, it is a Ubuntu 20.04 installation. So a couple of commands will be similar to Josh’s article such as the VM configuration used in this post. So let’s start with setting up multipass.

## Install Multipass
Multipass is a project made available by Canonical[^1] also develops and publish the Ubuntu[^2] Linux distribution. So install it either from the [website](https://multipass.run/) or via [homebrew](https://brew.sh/).
If you don’t have the Docker CLI yet, install it too
```
brew install multipass docker
```

## Creating an SSH Key
Since we will connect to our VM via SSH we need a new SSH key that will be imported while setting up the VM. To do so we can use `ssh-keygen` like in the following. I will create a new one since I use just one for every service I connect to. Follow the prompts to generate a key. Mine will be called `docker-multipass`. Since macOS and Ubuntu are pretty similar in creating SSH keys you can also follow the instructions over at [DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-set-up-ssh-keys-on-ubuntu-20-04). 

```
➜  ~ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/user/.ssh/id_rsa): /Users/user/.ssh/docker-multipass
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/user/.ssh/docker-multipass.
Your public key has been saved in /Users/user/.ssh/docker-multipass.pub.
The key fingerprint is:
SHA256:LjhUL0bZ8lXf9iIrOPF1EXjAJguNu3YbvUVH2tUHcak user@domain
The key's randomart image is:
+---[RSA 3072]----+
|         o .oo+o+|
|       oo o.+..==|
|      = .o.+ .++=|
|     o +...  Eoo+|
|    . o S. .o.o..|
|   . o oo+o..+.. |
|    o ..+.oo.o   |
|     . . ....    |
|                 |
+----[SHA256]-----+
```

To get the generated public key which will be imported to the Ubuntu VM on the setup you can use `cat /Users/user/.ssh/docker-multipass.pub`. Copy the output since you will need it in the next step.

## The VM Config
In the following, we will create a config that will be used to set up the VM. This is done with a cloud-init configuration. Cloud-init[^3] is a configuration tool from Ubuntu that helps set up virtual machines. More Information about it can be found here: [Using cloud-init with Multipass | Ubuntu](https://ubuntu.com/blog/using-cloud-init-with-multipass). The config is taken from Josh’s article linked above.

Create a `docker.yaml` file in your current directory and paste in the following configuration:
```yaml
---
users:
  - name: ubuntu
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh-authorized-keys:
      - ssh-rsa AAAAB3...

package_update: true
packages:
  - docker.io
  - avahi-daemon
  - apt-transport-https
  - ca-certificates
  - curl
  - gnupg
  - lsb-release
runcmd:
  - sudo curl -fsSL https://get.docker.com | sudo bash
  - sudo systemctl enable docker
  - sudo systemctl enable -s HUP ssh
  - sudo groupadd docker
  - sudo usermod -aG docker ubuntu
```

### The configuration sections explained
* `users` is an Array that defines what users should be set up while creating the VM. Here you will also paste your SSH key created in the last step.
* `package_update` tells the Ubuntu VM to run `apt-get update` before installing any packages in the following step to make sure the newest packages are available for installation
* `packages` as the name suggests tells Ubuntu which packages to install to proceed
* `runcmd` will be run after installing the packages before. In this case, it will download Docker, enabling it via systemctl, creating the docker group and afterwards adding our newly created user to this group. This will make sure that our *ubuntu* is allowed to interact with Docker.

## Launching the Ubuntu VM
```
multipass launch -c 4 -m 8G -d 20G -n docker 20.04 --cloud-init docker.yaml
```

* `-c` this specifies how many CPUs the VM can use. In this case 4
* `-m` defines the amount of RAM
* `-d` is for setting the disk space used by the VM
* `-n` sets the VM’s name

### Handling possible permission problems with a non-root user
```
launch failed: multipass socket access denied
Please check that you have read/write permissions to '/var/run/multipass_socket'
```

When you use a macOS user which is not a local admin like I do you have to give this user the permissions to read/write to multipass’s socket to be able to function. So use the `chmod` command to give your user the permissions needed. Otherwise, you will not be able to start multipass instances.

```
sudo chmod a+rw /var/run/multipass_socket
```

After that, the socket should be usable for the non-root user. 

## Log into VM
After launching the multipass Ubuntu VM in the last step check if it is possible to ssh into the newly created VM.

```
ssh ubuntu@docker.local
```

### Permission denied (publickey)
When the error `ubuntu@docker.local: Permission denied (publickey).` will be thrown check if you provide the right ssh key when connecting. Without specifying a key *ssh* will use your main ssh key which is most often `id_rsa`. To use the key created above add an entry to your `.ssh/config` like below.

```.ssh/config
Host docker.local
  HostName docker.local
  User ubuntu
  IdentityFile ~/.ssh/docker-multipass
```

If you can connect to your newly created VM close the connection with `exit` and proceed to the next step where we will now use Docker from our host machine to interact with the Docker installed in the virtual machine.

## Time to use Docker
Now that everything is set up check if Docker is answering. Since we use a custom Docker host we have to provide the cli where we want to execute the commands. This is where `DOCKER_HOST` comes into play, so use it like in the example below.

```
DOCKER_HOST="ssh://ubuntu@docker.local" docker version
```

### Save the Docker Host environment variable in your terminal session
Since always putting the `DOCKER_HOST` variable in front of the commands can be annoying you can export it in your terminal session or save it in your `.bashrc` or `.zshrc` to have it in every terminal session in the future.

```
export DOCKER_HOST="ssh://ubuntu@docker.local"
```

## Have fun using Docker
When everything is set up and your Docker is usable from within your terminal you can now start any container you like.
```
docker run -p 9898:9898 stefanprodan/podinfo
```

### Exposing Ports to the Host machine
Right now (January 22) multipass does not automatically port-forward the exposed ports from within the VM. To set this up for a port use ssh with the `-L` argument which will block the port on your host machine which is exposed on the connected machine.

```
ssh -L <Host-Port>:localhost:<VM-Port> ubuntu@docker.local
```

I hope this article helped you set up and use Docker without using the Docker for Mac software.

Thank you for reading, 
Niklas


## Resources
[^1]: [Canonical](https://canonical.com/)
[^2]: [Ubuntu](https://ubuntu.com/)
[^3]: [cloud-init](https://cloud-init.io/)