Docker for Development on MacOS
Tags: docker, macos
Published: 2021-12-28 10:23:15

---
title: Docker for Development on MacOS
published: true
date: 2021-12-28 08:33:12 UTC
tags: docker, macos
canonical_url: 
---

Development environment should be a sharp tool.

Using [Homebrew](https://brew.sh/) for development environment configuration is boring. Development environment should be temporary and be reproducible whithin couple of minutes. Yes, it is about hosting dotfiles and writing scripts which do support binaries installation. For variety of versions. So modern solution is about using containers. There are many ways to use containers for development and their orchestration. What is a rapid solution for lazy devs? The [Docker Desktop](https://www.docker.com/products/docker-desktop). Easy, install and run.

However, the Docker Desktop on MacOS is awful. It is slow. Still slow and greedy for a host machine power.

How to use it?

Well, after many tries, the best solution is to run docker engine on the linux virtual machine.

Why don’t use linux directly?

Well, that does not solve the variety of versions problem. You give up to use containers at the end as the most convinitent solution.

## Install

Install [Docker Desktop](https://www.docker.com/products/docker-desktop). Run it and then disable it. Don’t start the engine.

Install [virtualbox binaries](https://www.virtualbox.org/wiki/Downloads) or use [Homebrew](https://brew.sh/). Yes, it already supports the macOS Monterey.

```bash
brew install --cask virtualbox
brew install --cask virtualbox-extension-pack
```

Install [Vagrant binaries](https://www.vagrantup.com/) or use [Homebrew](https://brew.sh/)

```bash
brew install vagrant
vagrant plugin install vagrant-vbguest
```

Done.

## Setup

Keep your infrastracture as a code.

Create two files and track them with VCS. This is your vagrant working directory.

```
.
├── Vagrantfile
└── install.sh
```

a `Vagrantfile` should be something like this

```ruby
Vagrant.configure("2") do |config|
  config.ssh.forward_agent = true

  config.vm.provider :virtualbox do |v|
    v.memory = 12288 # don't be greedy, it should be 40-70% of your host memory
    v.cpus = 4       # and this should be 60-80% of your real CPU cores
    v.gui = false
  end

  config.vm.define "docker" do |c|
    c.vm.box = "ubuntu/hirsute64"

    # Docker ports
    c.vm.network "forwarded_port", guest: 2375, host: 2375, id: "dockerd"

    # Application ports
    c.vm.network "forwarded_port", guest: 1313, host: 1313, id: "hugo"
    c.vm.network "forwarded_port", guest: 3000, host: 3000
    c.vm.network "forwarded_port", guest: 3001, host: 3001
    c.vm.network "forwarded_port", guest: 3002, host: 3002
    c.vm.network "forwarded_port", guest: 3306, host: 3306, id: "mysql"
    c.vm.network "forwarded_port", guest: 5432, host: 5432, id: "psql"
    c.vm.network "forwarded_port", guest: 6379, host: 6379, id: "redis"

    c.vm.provision :shell, privileged: false, binary: true, path: "install.sh"
  end
end
```

Modify resources allocation and application ports to make them available on the host.

The `install.sh` script automates docker engine provisioning

```bash
#!/usr/bin/env bash

echo '--- SYSTEM UPDATE ---'
sudo apt update
sudo apt install ca-certificates curl gnupg lsb-release net-tools

echo '--- INSTALL DOCKER ---'
curl -fsSL https://get.docker.com | sudo sh
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

echo '--- SETUP DOCKER SYSTEMD ---'
sudo mkdir /etc/systemd/system/docker.service.d/

cat <<EOF >>/tmp/docker.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd
EOF
sudo mv /tmp/docker.conf /etc/systemd/system/docker.service.d/docker.conf

echo '--- SETUP DOCKERD ---'
cat <<EOF >>/tmp/daemon.json
{
  "hosts": [
    "tcp://0.0.0.0:2375",
    "unix:///var/run/docker.sock"
  ]
}
EOF
sudo mv /tmp/daemon.json /etc/docker/daemon.json

echo '--- RELOAD DOCKERD ---'
sudo systemctl daemon-reload
sudo systemctl restart docker

echo '--- SETUP DOCKER PERMISSIONS---'
sudo usermod -aG docker $USER
```

Done.

## Run

Run `vagrant` and it will setup everything for you

```bash
vagrant up
```

Create a docker context.

```bash
docker context create vagrant --docker host=tcp://localhost:2375
```

Use right context and both `docker` and `docker-compose` will follow it.

```bash
docker context use vagrant
```

Here you go. Docker is ready. You may forget about docker engine while you work with your development stack.

Remote development supported by a variety of tools. Such as [VSCode Devcontainers](https://code.visualstudio.com/docs/remote/containers) and [JetBrains Remove Development](https://www.jetbrains.com/remote-development/). You may connect containers remotely using shell tools.

## Stop

Pause, when you need to restore it. If you have persistent volumes they stay the same, unchanged. The regular routine. You may restart your host to get that state.

```bash
vagrant halt
```

DANGER! Destroy, when you don’t need it anymore. That normally does not happen. The outrage case. But that’s the most convenient and fast way to reset docker engine.

```bash
vagrant destroy --force
```

## Details

Now you have a couple of docker contexts. Current is set to `vagrant`.

```
NAME TYPE DESCRIPTION DOCKER ENDPOINT ...
default moby ... unix:///var/run/docker.sock ...
vagrant * moby tcp://localhost:2375 ...
```

You may toggle between contexts. Use raw [Docker Desktop](https://www.docker.com/products/docker-desktop) when you need it. You may run more docker engines.

Docker follows context and shows you external engine resources.

```bash
docker info

Client:
 Context:    vagrant
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc., v0.7.1)
  compose: Docker Compose (Docker Inc., v2.2.1)
  scan: Docker Scan (Docker Inc., v0.14.0)

Server:
...
 Kernel Version: 5.11.0-41-generic
 Operating System: Ubuntu 21.04
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 11.69GiB
 Name: ubuntu-hirsute
...
```

Good enough. Cheers!