Let me start by saying that I usually run Containerlab on a dedicated Ubuntu 22.04 VM, which sits on top of Proxmox. All my labs run on this setup. However, I recently wanted to try running Containerlab directly on my MacBook (M3 Pro with 18GB of RAM) for a few reasons. For example, I might need to run labs while I’m away, work offline, or use a MacBook at work where I can’t access my home network. So, I decided to test whether I could run Cisco IOL and Arista EOS on macOS. The answer is yes, and here’s how you can do it.
As always, if you find this post helpful, press the ‘clap’ button on the left. It means a lot to me and helps me know you enjoy this type of content.
If you’re new to Containerlab and trying to understand what it is, I highly recommend checking out my introductory post, which is linked below. It covers the basics and will help you get started.
Prerequisites
This blog post assumes you’re somewhat familiar with Docker, VSCode, and running containers on macOS. You’ll need both Docker Desktop and VSCode installed to follow along. If you know how DevContainers work, that’s a plus, but we’ll cover the basics as we go.
Containerlab has excellent documentation on running it on macOS, including all the technical details. Roman also created a great video explaining how it works and the different ways to set it up. Both links are included below for reference.
- Containerlab doc - https://containerlab.dev/macos/
- Roman's video - https://www.youtube.com/watch?v=Xue1pLiO0qQ
It’s worth noting that there are multiple ways to run Containerlab on macOS. For example, you could create a Linux VM on macOS and run Containerlab there, but this requires managing the VM. If you prefer not to use a VM, there are two options with DevContainers. Docker-in-Docker and Docker-outside-Docker. This post focuses on Docker-outside-Docker
- Docker-in-Docker - The container runs its own Docker daemon inside it.
- Docker-outside-Docker - The container uses the Docker daemon from the host system (your Mac).
What Are DevContainers?
DevContainers, or Development Containers, are a way to create a consistent and isolated development environment using Docker containers. They allow you to package all the tools, dependencies, and configurations needed for a project into a container, which can be easily shared and run on any system. With VSCode’s DevContainers extension, you can seamlessly work inside these containers, making it easier to maintain a clean and portable setup. This approach is especially useful for avoiding conflicts between different projects or environments.
How Does Containerlab Run on a DevContainer?
When using a DevContainer, Containerlab runs inside the containerized environment while leveraging the Docker daemon on your host system (Docker-outside-Docker). This means the container handles the networking and lab setup, but the actual containers for your network devices (like Cisco IOL or Arista EOS) are managed by Docker on your Mac.
In this setup, the DevContainer has Containerlab installed, but your Mac doesn’t need it. The network device images (like Cisco IOL or Arista EOS) run on the host system’s Docker daemon, while the DevContainer can see and manage them.
Importing the Images
For this example, I’m using Arista cEOS and Cisco IOL. You’ll need to obtain these images yourself. The Arista image can be downloaded directly from their website, and I’ve explained the process in detail in my introductory post, so I’ll assume you already have them. Here is a quick summary anyway.
For Arista, make sure you download the ARM version of the image and then import it as a Docker image.
docker import cEOSarm-lab-4.33.1-EFT3.tar.tar ceos:4.33.1
docker inspect c4d1b7398564 | grep Archi
"Architecture": "arm64",
For Cisco IOL, I typically use vrnetlab on my Linux server to create Docker images from the .bin
files. However, this process might not work directly on macOS. Since I already had the Docker images on my Ubuntu Linux VM, I exported them from there and imported them onto my Mac. Here’s a quick overview of the steps.
- Export the Docker image from the Linux VM using
docker save
- Transfer the image file to your Mac using
scp
- Import the image into Docker on your Mac using
docker load
Here’s an example of how to import the Cisco IOL images. The process is exactly the same for other images, like Arista cEOS or any other network device images you might be using.
#on linux vm
docker save -o cisco_iol.tar vrnetlab/cisco_iol
#on mac
scp suresh@10.10.10.41:/home/suresh/cisco_iol.tar .
docker load -i cisco_iol.tar
Once you’ve completed the import process, you can verify that the images are available by running docker images
. This command will list all the Docker images on your system, including the ones you just imported.
docker images | grep -E 'iol|eos'
ceos 4.33.1 c4d1b7398564 2 hours ago 2.76GB
vrnetlab/cisco_iol l2-17.12.01 6b711e2baee6 5 weeks ago 607MB
vrnetlab/cisco_iol 17.12.01 f51066aaf4da 5 weeks ago 704MB
Setting Up DevContainer
Let’s start by creating a directory on your Mac where we’ll store the Containerlab topology file. Open this empty directory in VSCode. Inside VSCode, install the Dev Containers extension if you haven’t already. (This is just a one-time task)
Next, at the root of the directory you created (I named mine clabv1
), create a new directory called .devcontainer
. Inside this directory, create a file named devcontainer.json
and add the following contents.
I used the configuration from this repository, and Roman also explains this in his video. Here’s what the devcontainer.json
file should look like.
{
"image": "ghcr.io/srl-labs/containerlab/devcontainer-dood-slim",
"runArgs": [
"--network=host",
"--pid=host",
"--privileged"
],
"mounts": [
"type=bind,src=/var/lib/docker,dst=/var/lib/docker",
"type=bind,src=/lib/modules,dst=/lib/modules"
],
"workspaceFolder": "${localWorkspaceFolder}",
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind,consistency=cached"
}
This devcontainer.json
file sets up a DevContainer using the ghcr.io/srl-labs/containerlab/devcontainer-dood-slim
image, which is preconfigured for Containerlab. It runs with host networking, host process namespace, and privileged mode, giving it the necessary permissions to interact with the host system.
Please note that if you watch the linked YouTube video, the devcontainer.json
file under the mounts
section contains three entries. However, the repository now only has two entries. If you use the two entries with the Docker image version 0.60.1
, the DevContainer will fail to start with an error message.
docker: Error response from daemon:
invalid mount config for type "bind":
bind source path does not exist: /run/docker/netns.
To avoid this issue, I decided to use the latest version of the Docker image, and the problem was resolved.
Next, create a topology file just as you normally would. There’s no need to specify anything different for this setup, the file should look like any other Containerlab topology file. I named mine topology.yml
.
---
name: clab_mac
mgmt:
network: mgmt
ipv4-subnet: 192.168.100.0/24
topology:
nodes:
iol-1:
kind: cisco_iol
image: vrnetlab/cisco_iol:17.12.01
mgmt-ipv4: 192.168.100.61
iol-2:
kind: cisco_iol
image: vrnetlab/cisco_iol:17.12.01
mgmt-ipv4: 192.168.100.62
ceos-01:
kind: ceos
image: ceos:4.33.1
mgmt-ipv4: 192.168.100.63
iol-l2:
kind: cisco_iol
image: vrnetlab/cisco_iol:l2-17.12.01
type: l2
mgmt-ipv4: 192.168.100.70
links:
- endpoints: ["iol-1:Ethernet0/1","iol-l2:Ethernet0/1"]
- endpoints: ["iol-2:Ethernet0/1","iol-l2:Ethernet0/2"]
- endpoints: ["ceos-01:eth1","iol-l2:Ethernet0/3"]
Finally, it’s time to start the DevContainer. At the bottom left of VSCode, click the icon (refer to the screenshot) and select Reopen in Container. Once you do this, VSCode will rebuild and reopen your workspace inside the DevContainer.
Now, you’ll be placed inside the container’s shell, this is not your Mac’s shell. At this stage, your lab is not yet deployed. If you open Docker Desktop, you’ll see this container up and running. To confirm everything is working, you can run containerlab version
to check the installation.
Please note that if you reopen this directory in VSCode, such as after restarting your laptop, you’ll automatically get a prompt to open it in the DevContainer. If you miss the pop-up, you can always follow the previous method to reopen it manually.
Starting Your Lab
Now, you’re ready to start your lab. Simply run the containerlab deploy
command as shown below.
At this point, your lab will start, and your Cisco and Arista nodes will power on. You can verify the status of your lab by checking Docker Desktop to see the containers running. Once the lab is up, you can connect to your devices and begin testing or configuring as needed.
Testing and Verification
To test the setup, I SSHed (from the Dev Container terminal) into one Arista device and one Cisco device, configured IP addresses on their interfaces, and tried pinging between them. As expected, the ping was successful, confirming that the lab was functioning correctly.
ceos-01#conf ter
ceos-01(config)#interface eth1
ceos-01(config-if-Et1)#no shut
ceos-01(config-if-Et1)#ip address 10.15.10.5/24
! IP configuration will be ignored while interface Ethernet1 is not a routed port.
ceos-01(config-if-Et1)#no switchport
ceos-01(config-if-Et1)#end
ceos-01#show ip interface brief
Address
Interface IP Address Status Protocol MTU Owner
----------------- ----------------------- ------------ -------------- ---------- -------
Ethernet1 10.15.10.5/24 up up 1500
Management0 192.168.100.63/24 up up 1500
ceos-01#
ceos-01#
iol-1#conf ter
Enter configuration commands, one per line. End with CNTL/Z.
iol-1(config)#interface ethernet0/1
iol-1(config-if)#ip address 10.15.10.6 255.255.255.0
iol-1(config-if)#no shut
iol-1(config-if)#end
iol-1#show ip interface brief
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 192.168.100.61 YES TFTP up up
Ethernet0/1 10.15.10.6 YES manual up up
Ethernet0/2 unassigned YES unset administratively down down
Ethernet0/3 unassigned YES unset administratively down down
iol-1#ping 10.15.10.5
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.15.10.5, timeout is 2 seconds:
.!!!!
Success rate is 80 percent (4/5), round-trip min/avg/max = 1/1/1 ms
Once you are done with your lab, as always, you can destroy them with a single command.
Accessing the Node Directly from the Mac
Initially, I couldn’t figure out how to access the nodes directly from my Mac and had to use the DevContainer to initiate SSH sessions. Thanks to Daniel’s help, I learned that exposing the ports in the topology file solves this issue. By adding the ports
section, as shown below, you can map the container’s SSH port to a port on your Mac, allowing direct access.
ceos-01:
kind: ceos
image: ceos:4.33.1
mgmt-ipv4: 192.168.100.63
ports:
- 2122:22
This maps port 22
(SSH) inside the container to port 2122
on your Mac. Now, you can SSH directly from your Mac using ssh -p 2122 user@192.168.100.63
. I only made this change for the Arista image, but the same approach works for other devices.
#192.168.0.26 is my Mac's IP
ssh -p 2122 admin@192.168.0.26
(admin@192.168.0.26) Password:
Last login: Tue Jan 28 21:40:06 2025 from 192.168.100.1
ceos-01>en
ceos-01#
ceos-01#exit
Connection to 192.168.0.26 closed.