Installing Docker & Kubernetes
Step 1 – Install Homebrew
If you don’t have Homebrew installed, it’s worth doing that first. It’s a great package manager for the Mac. For full details head over to Homebrew Site, buts its as simple using the following command
1 | $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" |
Step 2 – Install Virtual Box
As we are installing on an Arm64 M1 Mac we cannot use Hombrew at the moment. Instead head to Virtual Box and download the developer preview for macOS. Once downloaded run the installer to add Virutal Box to your system
Step 3 – Install Docker
No we can install Docker for the Mac. Again this is as simple as heading to the Docker website and downloading the Mac version of Docker Desktop.
At this points its also worth creating an account in Docker Hub as you will be using this to download Images and push newly created images to its repo.

Step 4 – Install Kubernetes
As the ultimate goal of this tutorial is to run our docker images on a simple ( but functional ) Kubernetes Cluster on our Mac, lets also install Kubernetes too
- Install kubectl – a command line tool for communicating with a Kubernetes cluster’s control plane, using the Kubernetes API.
- brew install kubectl
- Install minikube – a local Kubernetes, focusing on making it easy to learn and develop for Kubernetes.
- curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-arm64
- sudo install minikube-darwin-arm64 /usr/local/bin/minikube
Start Kubernetes
1 2 3 4 5 6 7 8 9 10 11 12 | $ minikube start 😄 minikube v1.29.0 on Darwin 13.2.1 (arm64) ✨ Using the docker driver based on existing profile 👍 Starting control plane node minikube in cluster minikube 🚜 Pulling base image ... 🔄 Restarting existing docker container for "minikube" ... 🐳 Preparing Kubernetes v1.26.1 on Docker 20.10.23 ... 🔗 Configuring bridge CNI (Container Networking Interface) ... 🔎 Verifying Kubernetes components... ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5 🌟 Enabled addons: storage-provisioner, default-storageclass 🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default |
Check its running
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | $ kubectl api-versions admissionregistration.k8s.io/v1 apiextensions.k8s.io/v1 apiregistration.k8s.io/v1 apps/v1 authentication.k8s.io/v1 authorization.k8s.io/v1 autoscaling/v1 autoscaling/v2 batch/v1 certificates.k8s.io/v1 coordination.k8s.io/v1 discovery.k8s.io/v1 events.k8s.io/v1 flowcontrol.apiserver.k8s.io/v1beta2 flowcontrol.apiserver.k8s.io/v1beta3 networking.k8s.io/v1 node.k8s.io/v1 policy/v1 rbac.authorization.k8s.io/v1 scheduling.k8s.io/v1 storage.k8s.io/v1 storage.k8s.io/v1beta1 v1 |
Stop Kubernetes
We don’t need K8 running for now, we’ll use it later in the tutorial once we are comfortable running Docker
1 2 3 4 | $ minikube stop ✋ Stopping node "minikube" ... 🛑 Powering off "minikube" via SSH ... 🛑 1 node stopped. |
Learning Docker
There is an excellent tutorial on the Docker website to follow which takes you through all the key elements of running and using Docker containers. This section is my run-through of that tutorial highlighting any differences when running in Mac Arm hardware. This is typically important when building images to upload to the repo as you are building on Arm64, but likely to be deploying to Intel X86 and this needs you to use the BuildX framework.
Head over to Getting Started and read through the foundation info to understand what a container and image is
Getting Started
First we are going to use the getting-started image to create our first container. The command for this is
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | $ docker run -d -p 80:80 docker/getting-started Unable to find image 'docker/getting-started:latest' locally latest: Pulling from docker/getting-started 261da4162673: Pull complete a60aada4c44a: Pull complete 2f61404bb4b8: Pull complete fa3f58a317be: Pull complete 476bb2a1cc22: Pull complete 33a28b928e89: Pull complete a879581b8e12: Pull complete d0193f05f10f: Pull complete 14f901bbf056: Pull complete Digest: sha256:d79336f4812b6547a53e735480dde67f8f8f7071b414fbd9297609ffb989abc1 Status: Downloaded newer image for docker/getting-started:latest bc51c52a64e3c6cc49032aea53f24eebfc985f30590cea974c3674d3ec4fe91d |
If you have not use this image before, it will pull all the required dependencies. Once downloaded it will create a new container using this image. By default, Docker assigns a 2-word name to each container made up of 2 random works separated by a hyphen. We can see the container and ensure it is running by opening Docker Desktop

Here we can see that the getting-started Container is called ‘eager_meninsky’ and its status is running. We can also go straight to the container by clicking on the link 80:80 or using the URL http://localhost/ in any browser. This opens the web page running on the web server running on the newly created container

We can also use the command line to check if the container is running. We use the ‘docker ps’ command for this as follows

This shows some useful information, specifically the CONTAINER ID, which in this case is ‘bc51c52a64e3’, The id is useful when using the command line options. So for example we can stop ( and restart ) our container with the ‘docker stop ID’ and ‘docker start ID’ as follows

The rest of the tutorial is typically run by following the pages on the website. The tutorial walks you through
- Creating application ( a simple node.js web app )
- Update the app and rebuild the image
- Sharing an image on Docker Hub
- At this point when running Docker on a M1 Arm64 Macbook Pro we have a conflict on hardware. Docker Hub and Docker playground expect an Intel x86 based image but by default our Docker builds Arm64 images.
- There is an excellent article on Medium on how to build multiple hardware versions, which boils down to the following steps
- Step 1
- We will need to create a “builder”. I will call it mybuilder.
- docker buildx create –name mybuilder
- Step 2
- Then we tell buildx to use mybuilder.
- docker buildx use mybuilder
- Step 3
- We can inspect mybuilder just to be sure.
- docker buildx inspect –bootstrap
- As we can see, linux/arm64 and linux/amd64 are both listed (image by author).
- Step 4
- Build the image (assuming you are in the directory where your Dockerfile is).
- docker buildx build –tag [image-tag] -o type=image –platform=linux/arm64,linux/amd64 .
- Step 5 ( Alternate )
- We can also build and push to the Docker Hub directly (assuming already logged in to Docker).
- docker buildx build –push –tag [docker-hubid/image-tag] –platform=linux/arm64,linux/amd64 .
- Step 1
Docker Commands
The following list is not ALL Docker commands, just the ones that I use the most frequently or are the most important to know
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | # Access Docker docker login docker logout # Manage Images docker search docker pull docker push docker rmi docker image docker build docker save docker load # Create Containers docker run docker volume # Manage Containers docker ps docker ps -a docker rm docker container prune docker start docker stop docker cp docker exec docker rename docker commit # Info & Stats docker logs docker stats docker top docker version docker inspect docker diff docker port docker history docker network |