NetPilot
Back to Blog
Tutorial••7 min

Run SONiC in containerlab: sonic-vs Step by Step

Get the community docker-sonic-vs image, deploy two SONiC switches with containerlab, and bring up a link and BGP. Includes what the image leaves out, like sudo and a running bgpd, and how to work with it.

D
David Kim
DevOps Engineer

To run SONiC in containerlab, load the community docker-sonic-vs image and use the sonic-vs kind. The topology is five lines. The part the docs leave you to discover is how the image behaves once it is running, so this tutorial covers both.

Everything below ran on containerlab v0.82.0 on Ubuntu 24.04, on the 202511 branch build of docker-sonic-vs. containerlab is an independent open source project. NetPilot is a contributor and integrator, and the sonic-vs kind comes from the containerlab project.

NetPilot runs the same image on a cloud lab VM. The image is preloaded and the fixes in this post are already done, so a prompt gets you a working fabric. The rest of this post is for running it on your own host.

sonic-vs or sonic-vm

containerlab has two community SONiC kinds. This tutorial uses the container.

sonic-vssonic-vm
FormatContainerVM in a vrnetlab container
Imagedocker-sonic-vs.gzsonic-vs.img.gz, built with vrnetlab
BootAbout 30 seconds to ports up, measuredAbout 1 minute, per the docs
Shelldocker execSSH, admin:admin per the docs
startup-configNoYes, per the docs

Commercial builds have kinds of their own, such as dell_sonic.

Step 1: Get the image

You need Docker and containerlab on a Linux host, and no KVM, because sonic-vs is a plain container. The image is a build artifact, not a registry pull:

curl -L -o docker-sonic-vs.gz "https://sonic-build.azurewebsites.net/api/sonic/artifacts?branchName=202511&platform=vs&target=target/docker-sonic-vs.gz"
docker load -i docker-sonic-vs.gz
Loaded image: docker-sonic-vs:latest

The file was 210 MB and the loaded image 816 MB. This URL follows the newest build on the branch, so your build number will differ from ours.

Step 2: Write the topology

Two switches, one link. It is also in the public repo.

name: sonic-two-switches
 
topology:
  kinds:
    sonic-vs:
      image: docker-sonic-vs:latest
  nodes:
    sw1:
      kind: sonic-vs
    sw2:
      kind: sonic-vs
  links:
    - endpoints: ["sw1:eth1", "sw2:eth1"]

Links use ethN, and eth1 is the wire behind the SONiC port Ethernet0, eth2 behind Ethernet4. Use ethN in the topology and EthernetN on the switch. A link named Ethernet0 deploys without an error and the port never comes up.

Step 3: Deploy

sudo containerlab deploy -t two-switches.clab.yml
│ clab-sonic-two-switches-sw1 │ sonic-vs               │ running │ 172.20.20.2       │
│ clab-sonic-two-switches-sw2 │ sonic-vs               │ running │ 172.20.20.3       │

Deploy took under 4 seconds. The switch was not ready yet. In the first checks after deploy, the port table was empty, and the ports showed up about 30 seconds later. Wait, then open a shell:

docker exec -it clab-sonic-two-switches-sw1 bash

You are root inside the switch. There is no admin user and no password.

Ports boot admin down. On sw1:

config interface ip add Ethernet0 10.0.0.0/31
config interface startup Ethernet0

On sw2:

config interface ip add Ethernet0 10.0.0.1/31
config interface startup Ethernet0

Check the port, and do not be thrown by the first line:

show interfaces status Ethernet0
/bin/sh: 1: sudo: not found
  Interface        Lanes    Speed    MTU    FEC         Alias    Vlan    Oper    Admin    Type    Asym PFC
-----------  -----------  -------  -----  -----  ------------  ------  ------  -------  ------  ----------
  Ethernet0  25,26,27,28      10G   9100    N/A  fortyGigE0/0  routed      up       up     N/A         N/A

Then ping across it from sw1:

ping -c 3 10.0.0.1
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.446 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.348 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=0.303 ms
 
--- 10.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2032ms

Step 5: Start BGP

bgpd is stopped on this image, so start it on both switches:

supervisorctl start bgpd

Then configure each side in vtysh. On sw1:

vtysh
configure terminal
router bgp 65001
 no bgp ebgp-requires-policy
 neighbor 10.0.0.1 remote-as 65002
end

On sw2, swap the numbers: router bgp 65002 and neighbor 10.0.0.0 remote-as 65001. Then:

vtysh -c 'show bgp summary'
Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
10.0.0.1        4      65002         3         3        0    0    0 00:00:08            0        0 N/A

SONiC FRR: configure BGP takes this to a leaf-spine fabric with ECMP and BFD.

What the image leaves out

docker-sonic-vs is built to test SONiC, so a few habits from a hardware switch do not carry over. Each of these came from the run above:

You tryYou getDo this instead
sudo config ...sudo: command not foundDrop sudo, you are root
show ip interfacesTraceback, No such file or directory: 'sudo'ip -br addr
show versionKeyError: 'debian_version'cat /etc/sonic/sonic_version.yml
show ip bgp summaryNo such command 'bgp'vtysh -c 'show bgp summary'
show ip routeUnable to get summary from bgpvtysh -c 'show ip route'
vtysh -c 'show bgp summary' on a fresh nodebgpd is not runningsupervisorctl start bgpd

Saving config, and what happens next

Save has two halves, and both work:

config save -y
vtysh -c write

Keep your expectations straight, though. docker restart is not a reboot here: the restarted node lost its links and its services did not come back, because containerlab starts them after deploy. containerlab destroy removes the containers and their files with them.

So keep config in files. The public BGP and EVPN labs bind a configs folder into each switch and run a script with exec, so one deploy gives you a configured fabric about 60 seconds later:

topology:
  kinds:
    sonic-vs:
      image: docker-sonic-vs:latest
      binds:
        - configs:/configs:ro
  nodes:
    leaf1:
      kind: sonic-vs
      exec:
        - bash /configs/leaf1.sh

The script waits for its last port, then runs the same config and vtysh lines you typed above.

When you are done:

sudo containerlab destroy -t two-switches.clab.yml --cleanup

When you would rather not run the host

Ask the NetPilot agent:

Build two SONiC switches connected by one link, with eBGP between them, and show me the neighbor state.

The image is already on the lab VM, sudo, show version, show ip interfaces and show ip route work, BGP is running when the deploy returns, and you can open a terminal on either switch to check by hand.

FAQ

How do I run SONiC in containerlab?

Download docker-sonic-vs.gz from the SONiC build pipeline, load it with docker load, and use the sonic-vs kind in a topology file. Deploy returns in seconds, the switch needs about 30 more seconds to create its ports, and you get a root shell with docker exec.

What is the difference between sonic-vs and sonic-vm in containerlab?

sonic-vs is SONiC as a container, so it starts fast and uses about 150 MB of memory per node at idle. sonic-vm runs the SONiC VM image inside a vrnetlab wrapper. The containerlab docs give it about 1 minute to boot, admin:admin SSH login and startup-config support.

Why does sudo fail on docker-sonic-vs?

The image has no sudo binary, and you are already root. Run config and show directly. Some show commands shell out to sudo, so they print sudo: not found, and show ip interfaces fails outright. ip -br addr gives the same answer.

Does SONiC config survive a docker restart in containerlab?

No. In our test a restarted sonic-vs node lost its links and its services did not come back, because containerlab starts them after deploy. Keep config in files and apply it at deploy, as the public labs do with exec.

Next steps

Try NetPilot Free

Build enterprise-grade network labs in seconds with AI assistance

Get Started Free