[{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/authors/","section":"authors","summary":"","title":"authors","type":"authors"},{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/categories/","section":"categories","summary":"","title":"categories","type":"categories"},{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/tags/lacp/","section":"Tags","summary":"","title":"LACP","type":"tags"},{"content":" Design Architecture deep dives, ASIC and platform trade-offs, and the state models behind SONiC. Design guides Build Containerlab topologies, vrnetlab images, MCLAG, and BGP EVPN/VXLAN fabrics you can run yourself. Build guides Automate Ansible and CONFIG_DB templating, ZTP, gNMI telemetry, and CI/CD for network changes. Automation guides Troubleshoot Bottom-up checklists, failure drills, CLI cheat sheets, and production post-mortems. Troubleshooting guides ","date":"4 September 2026","externalUrl":null,"permalink":"/","section":"Master the open network operating system.","summary":"","title":"Master the open network operating system.","type":"page"},{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/tags/mclag/","section":"Tags","summary":"","title":"MCLAG","type":"tags"},{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/series/","section":"series","summary":"","title":"series","type":"series"},{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/series/simple-mclag/","section":"series","summary":"","title":"Simple MCLAG","type":"series"},{"content":"MCLAG problems almost always come down to one of a few layers: the daemon isn\u0026rsquo;t running, the peers can\u0026rsquo;t reach each other, the session is up but members don\u0026rsquo;t match, or the hosts disagree about who their LACP partner is. This post works through those layers from the bottom up, using the lab from Part 2.\nWork bottom-up. There\u0026rsquo;s no point inspecting MAC sync if the ICCP session is down, and no point debugging ICCP if iccpd isn\u0026rsquo;t running. 1. Is iccpd running? # show feature status iccpd docker ps --filter name=iccpd If the feature is disabled, enable it with sudo config feature state iccpd enabled. If the container keeps restarting, its logs are in syslog:\nshow logging | grep -i iccp | tail -20 2. Can the peers reach each other? # ICCP is just TCP between source_ip and peer_ip. Check the path before checking the protocol:\n# from leaf1 ping -c 3 192.168.255.2 show interfaces portchannel # PortChannel0001 should be LACP(A)(Up) show vlan brief # PortChannel0001 tagged in Vlan4094 show ip interfaces | grep Vlan4094 Common causes when the ping fails:\nThe peer-link port channel is down. Look at its member ports with show interfaces status Ethernet0,Ethernet4. Ports left administratively down from the factory config are the usual culprit. Vlan4094 isn\u0026rsquo;t on the peer-link, or is missing on one side. The addresses are swapped. Each leaf\u0026rsquo;s source_ip must be its own Vlan4094 address. 3. Is the ICCP session up? # docker exec iccpd mclagdctl -i 1 dump state Abridged, a healthy leaf reports something like this:\nThe MCLAG\u0026#39;s keepalive is: OK Domain id: 1 Local Ip: 192.168.255.1 Peer Ip: 192.168.255.2 Peer Link Interface: PortChannel0001 Role: Active MCLAG Interface: PortChannel0002,PortChannel0003 What to look for:\nField Healthy If not keepalive OK Go back to step 2. You can also check the TCP session with ss -tn | grep 8888. Local / Peer IP mirror images across the two leaves Fix config mclag on the wrong side. Role one Active, one Standby Both the same means the session isn\u0026rsquo;t really established. MCLAG Interface identical list on both leaves A member configured on only one leaf behaves like a normal, single-homed port channel. To see each member port channel\u0026rsquo;s state as iccpd sees it, locally and on the peer:\ndocker exec iccpd mclagdctl -i 1 dump portlist local docker exec iccpd mclagdctl -i 1 dump portlist peer 4. What do the hosts see? # The host is the ultimate judge, because LACP doesn\u0026rsquo;t care what the switches think is configured. On host1:\ndocker exec clab-simple-mclag-host1 cat /proc/net/bonding/bond0 With MCLAG working, both slave interfaces report the same Aggregator ID and the same partner MAC:\nSlave Interface: eth1 Aggregator ID: 1 partner mac address: 52:54:00:aa:bb:cc Slave Interface: eth2 Aggregator ID: 1 partner mac address: 52:54:00:aa:bb:cc Two different Aggregator IDs is the classic MCLAG failure signature. The host sees two separate LACP partners, so it puts the links in separate aggregators and uses only one. The leaves aren\u0026rsquo;t presenting a common LACP identity, which almost always means the ICCP session (step 3) isn\u0026rsquo;t up, or the port channel isn\u0026rsquo;t an MCLAG member on one of the leaves. 5. Is traffic flowing, and are MACs synced? # # from host1 docker exec clab-simple-mclag-host1 ping -c 3 10.10.10.102 # on each leaf show mac docker exec iccpd mclagdctl -i 1 dump mac Both leaves should learn the host MACs on their local MCLAG member port channels, regardless of which link the host actually hashed traffic onto. That\u0026rsquo;s the MAC sync doing its job.\nFailure drills # A design isn\u0026rsquo;t finished until you\u0026rsquo;ve watched it fail. Run a continuous ping from host1 to host2 in one terminal:\ndocker exec -it clab-simple-mclag-host1 ping -i 0.2 10.10.10.102 Then try each of these in another terminal and note how many replies go missing.\nDrill Command What to expect Lose one host link on leaf2: sudo config interface shutdown Ethernet12 host2\u0026rsquo;s bond drops to one member; traffic continues via leaf1. Lose one peer-link member on leaf1: sudo config interface shutdown Ethernet0 PortChannel0001 stays up on Ethernet4; ICCP stays OK. Lose a whole leaf on leaf1: sudo reboot Hosts fail over to leaf2. Watch bond0 on both hosts and the ICCP session as leaf1 rejoins. Restore each drill before moving to the next. For example, sudo config interface startup Ethernet12. (Rebooting from inside the VM keeps Containerlab\u0026rsquo;s links intact. Stopping the container would tear them down.) Record the loss you measure: it\u0026rsquo;s your baseline for comparing configuration changes and SONiC releases.\nQuick reference # # daemon show feature status iccpd docker ps --filter name=iccpd # peer path ping \u0026lt;peer_ip\u0026gt; show interfaces portchannel show vlan brief # session and members docker exec iccpd mclagdctl -i 1 dump state docker exec iccpd mclagdctl -i 1 dump portlist local docker exec iccpd mclagdctl -i 1 dump portlist peer # tables docker exec iccpd mclagdctl -i 1 dump mac docker exec iccpd mclagdctl -i 1 dump arp # config as stored sonic-cfggen -d --var-json MCLAG_DOMAIN sonic-cfggen -d --var-json MCLAG_INTERFACE # logs show logging | grep -i iccp That wraps up the Simple MCLAG series. The natural next step is adding a layer-3 gateway on Vlan10.\n","date":"4 September 2026","externalUrl":null,"permalink":"/troubleshoot/simple-mlag/","section":"Troubleshoot","summary":"A bottom-up checklist for SONiC MCLAG, from iccpd to what the hosts see, plus failure drills.","title":"Simple MCLAG, Part 4: Verifying and Troubleshooting","type":"troubleshoot"},{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/tags/sonic/","section":"Tags","summary":"","title":"SONiC","type":"tags"},{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/categories/troubleshoot/","section":"categories","summary":"","title":"Troubleshoot","type":"categories"},{"content":"Post-mortems, edge-case bug fixes, CLI cheat sheets, and production recovery.\n","date":"4 September 2026","externalUrl":null,"permalink":"/troubleshoot/","section":"Troubleshoot","summary":"","title":"Troubleshoot","type":"troubleshoot"},{"content":"","date":"4 September 2026","externalUrl":null,"permalink":"/tags/troubleshooting/","section":"Tags","summary":"","title":"Troubleshooting","type":"tags"},{"content":"Open networking enthusiast and the author of runsonic.io. Writes about designing, building, automating, and troubleshooting SONiC networks. Reach out at tyler@runsonic.io.\n","date":"4 September 2026","externalUrl":null,"permalink":"/authors/tyler-kiziah/","section":"authors","summary":"","title":"Tyler Kiziah","type":"authors"},{"content":"","date":"28 August 2026","externalUrl":null,"permalink":"/tags/ansible/","section":"Tags","summary":"","title":"Ansible","type":"tags"},{"content":"Ansible playbooks, ZTP, gNMI telemetry, and CI/CD pipelines.\n","date":"28 August 2026","externalUrl":null,"permalink":"/automate/","section":"Automate","summary":"","title":"Automate","type":"automate"},{"content":"","date":"28 August 2026","externalUrl":null,"permalink":"/categories/automate/","section":"categories","summary":"","title":"Automate","type":"categories"},{"content":"","date":"28 August 2026","externalUrl":null,"permalink":"/tags/config_db/","section":"Tags","summary":"","title":"CONFIG_DB","type":"tags"},{"content":"In Part 2 we configured MCLAG with about twenty CLI commands per leaf, and the two leaves differed in only two IP addresses. That\u0026rsquo;s a template waiting to happen.\nSONiC makes this straightforward because all configuration lives in CONFIG_DB. Instead of driving the CLI, we render the CONFIG_DB tables we need as JSON and hand them to config load, which merges them into the running config.\nWhy config load? # Approach Behavior config load file.json Merges the file into CONFIG_DB. Keys you don\u0026rsquo;t mention are left alone. config reload file.json Replaces CONFIG_DB with the file and restarts services. Disruptive. config apply-patch patch.json Applies an RFC 6902 JSON Patch with validation (Generic Config Updater). A merge is the right fit here: we own the MCLAG-related keys and leave everything else, such as management, users, and syslog, untouched. The catch is that a merge never deletes. Removing a member from the template won\u0026rsquo;t remove it from the switch. We\u0026rsquo;ll come back to that.\nProject layout # mclag-ansible/ ├── ansible.cfg ├── inventory.ini ├── group_vars/ │ └── leaves.yml ├── host_vars/ │ ├── leaf1.yml │ └── leaf2.yml ├── templates/ │ └── mclag.json.j2 └── mclag.yml Inventory and variables # SONiC is Debian underneath, so Ansible manages it like any Linux host over SSH. No network-specific connection plugin is needed.\ninventory.ini [leaves] leaf1 ansible_host=clab-simple-mclag-leaf1 leaf2 ansible_host=clab-simple-mclag-leaf2 [leaves:vars] ansible_user=admin # Lab only. Use SSH keys or ansible-vault for anything real. ansible_password=admin ansible.cfg [defaults] inventory = inventory.ini host_key_checking = False Everything symmetrical goes in group_vars:\ngroup_vars/leaves.yml mclag_domain_id: 1 mclag_keepalive_vlan: 4094 mclag_keepalive_prefixlen: 30 mclag_data_vlans: [10] mclag_peer_link: name: PortChannel0001 ports: [Ethernet0, Ethernet4] mclag_members: - { name: PortChannel0002, ports: [Ethernet8], vlan: 10 } # host1 - { name: PortChannel0003, ports: [Ethernet12], vlan: 10 } # host2 Only the ICCP addresses differ per leaf:\nhost_vars/leaf1.yml mclag_source_ip: 192.168.255.1 mclag_peer_ip: 192.168.255.2 host_vars/leaf2.yml mclag_source_ip: 192.168.255.2 mclag_peer_ip: 192.168.255.1 The template # Building the JSON by string concatenation is fragile, because one stray comma breaks it. Instead, the template builds a dictionary and lets to_nice_json handle the syntax:\ntemplates/mclag.json.j2 {#- CONFIG_DB fragment for a SONiC MCLAG peer. Rendered per host. -#} {%- set cfg = { \u0026#34;FEATURE\u0026#34;: {\u0026#34;iccpd\u0026#34;: {\u0026#34;state\u0026#34;: \u0026#34;enabled\u0026#34;}}, \u0026#34;PORT\u0026#34;: {}, \u0026#34;PORTCHANNEL\u0026#34;: {}, \u0026#34;PORTCHANNEL_MEMBER\u0026#34;: {}, \u0026#34;VLAN\u0026#34;: {}, \u0026#34;VLAN_MEMBER\u0026#34;: {}, \u0026#34;VLAN_INTERFACE\u0026#34;: {}, \u0026#34;MCLAG_DOMAIN\u0026#34;: {}, \u0026#34;MCLAG_INTERFACE\u0026#34;: {}, \u0026#34;MCLAG_UNIQUE_IP\u0026#34;: {} } -%} {%- set kvlan = \u0026#34;Vlan\u0026#34; ~ mclag_keepalive_vlan -%} {#- Port channels and their member ports -#} {%- for pc in [mclag_peer_link] + mclag_members -%} {%- set _ = cfg.PORTCHANNEL.update({pc.name: {\u0026#34;admin_status\u0026#34;: \u0026#34;up\u0026#34;, \u0026#34;mtu\u0026#34;: \u0026#34;9100\u0026#34;, \u0026#34;min_links\u0026#34;: \u0026#34;1\u0026#34;}}) -%} {%- for port in pc.ports -%} {%- set _ = cfg.PORT.update({port: {\u0026#34;admin_status\u0026#34;: \u0026#34;up\u0026#34;}}) -%} {%- set _ = cfg.PORTCHANNEL_MEMBER.update({pc.name ~ \u0026#34;|\u0026#34; ~ port: {}}) -%} {%- endfor -%} {%- endfor -%} {#- VLANs: everything tagged on the peer-link, members untagged -#} {%- for vid in mclag_data_vlans + [mclag_keepalive_vlan] -%} {%- set _ = cfg.VLAN.update({\u0026#34;Vlan\u0026#34; ~ vid: {\u0026#34;vlanid\u0026#34;: vid | string}}) -%} {%- set _ = cfg.VLAN_MEMBER.update({\u0026#34;Vlan\u0026#34; ~ vid ~ \u0026#34;|\u0026#34; ~ mclag_peer_link.name: {\u0026#34;tagging_mode\u0026#34;: \u0026#34;tagged\u0026#34;}}) -%} {%- endfor -%} {%- for m in mclag_members -%} {%- set _ = cfg.VLAN_MEMBER.update({\u0026#34;Vlan\u0026#34; ~ m.vlan ~ \u0026#34;|\u0026#34; ~ m.name: {\u0026#34;tagging_mode\u0026#34;: \u0026#34;untagged\u0026#34;}}) -%} {%- set _ = cfg.MCLAG_INTERFACE.update({mclag_domain_id ~ \u0026#34;|\u0026#34; ~ m.name: {\u0026#34;if_type\u0026#34;: \u0026#34;PortChannel\u0026#34;}}) -%} {%- endfor -%} {#- ICCP interface and domain -#} {%- set _ = cfg.VLAN_INTERFACE.update({ kvlan: {}, kvlan ~ \u0026#34;|\u0026#34; ~ mclag_source_ip ~ \u0026#34;/\u0026#34; ~ mclag_keepalive_prefixlen: {} }) -%} {%- set _ = cfg.MCLAG_UNIQUE_IP.update({kvlan: {\u0026#34;unique_ip\u0026#34;: \u0026#34;enable\u0026#34;}}) -%} {%- set _ = cfg.MCLAG_DOMAIN.update({mclag_domain_id | string: { \u0026#34;source_ip\u0026#34;: mclag_source_ip, \u0026#34;peer_ip\u0026#34;: mclag_peer_ip, \u0026#34;peer_link\u0026#34;: mclag_peer_link.name }}) -%} {{ cfg | to_nice_json }} The playbook # mclag.yml - name: Configure MCLAG on SONiC leaves hosts: leaves gather_facts: false become: true tasks: - name: Render the CONFIG_DB fragment ansible.builtin.template: src: templates/mclag.json.j2 dest: /etc/sonic/mclag.json mode: \u0026#34;0644\u0026#34; register: fragment - name: Merge the fragment into CONFIG_DB ansible.builtin.command: config load /etc/sonic/mclag.json -y when: fragment.changed or (force_load | default(false) | bool) - name: Persist the running config ansible.builtin.command: config save -y when: fragment.changed or (force_load | default(false) | bool) - name: Wait for the ICCP session to come up ansible.builtin.command: docker exec iccpd mclagdctl -i {{ mclag_domain_id }} dump state register: mclag_state changed_when: false retries: 12 delay: 10 until: \u0026#34;\u0026#39;keepalive is: OK\u0026#39; in mclag_state.stdout\u0026#34; Run it with --diff to see exactly what changes on each leaf:\nansible-playbook mclag.yml --diff A second run reports no changes. The rendered file is identical, so the config load and config save tasks are skipped.\nsshpass must be installed on the control node for password authentication. With SSH keys, drop ansible_password from the inventory. Limitations to know about # Merges don\u0026rsquo;t delete. If you remove PortChannel0003 from mclag_members, the playbook won\u0026rsquo;t remove it from the switch. For a lab, that\u0026rsquo;s fine. For production, either generate a JSON Patch with explicit remove operations and use config apply-patch, or adopt a \u0026ldquo;full config\u0026rdquo; model that renders the entire config_db.json. Drift isn\u0026rsquo;t detected. The playbook compares the template to the file it last wrote, not to live CONFIG_DB. If someone changes the switch by hand, re-run with -e force_load=true to push the intended state again. Order matters on first boot. Make sure no factory IPs or VLAN memberships remain on the member ports (Part 2 covers this). Otherwise the port channel membership won\u0026rsquo;t take effect. Next: Part 4: Verifying and troubleshooting MCLAG.\n","date":"28 August 2026","externalUrl":null,"permalink":"/automate/simple-mlag/","section":"Automate","summary":"Render a CONFIG_DB fragment per leaf with Ansible and merge it with config load.","title":"Simple MCLAG, Part 3: Automating It with Ansible","type":"automate"},{"content":"Containerlab topologies, VRNETLAB image builds, MC-LAG, and BGP EVPN/VXLAN fabrics.\n","date":"21 August 2026","externalUrl":null,"permalink":"/build/","section":"Build","summary":"","title":"Build","type":"build"},{"content":"","date":"21 August 2026","externalUrl":null,"permalink":"/categories/build/","section":"categories","summary":"","title":"Build","type":"categories"},{"content":"","date":"21 August 2026","externalUrl":null,"permalink":"/tags/containerlab/","section":"Tags","summary":"","title":"Containerlab","type":"tags"},{"content":"In Part 1 we designed a two-leaf MCLAG pair with dual-homed hosts. Now we build it: a Containerlab topology using SONiC virtual switches, then the MCLAG configuration, one CLI command at a time. Doing it by hand once makes the automated version much easier to reason about.\nPrerequisites # A Linux host with Docker, Containerlab, and KVM (/dev/kvm). On macOS, use a Linux VM with nested virtualization. A SONiC VS image packaged with vrnetlab so it runs as the sonic-vm kind. The bonding kernel module on the Docker host, so the Linux hosts can build LACP bonds: sudo modprobe bonding Build the SONiC image # Download a sonic-vs.img.gz from the SONiC build artifacts for the release you want. Then package it with vrnetlab. Follow the README in the sonic/ directory of the vrnetlab repo, because the exact file naming it expects can change between versions:\ngit clone https://github.com/srl-labs/vrnetlab.git cd vrnetlab/sonic # place the decompressed image here, named as the README describes, then: make docker images | grep sonic Note the image tag make produced. You\u0026rsquo;ll need it in the topology file.\nThe topology file # Save this as simple-mclag.clab.yml. Replace the image tag with the one you built.\nname: simple-mclag topology: kinds: sonic-vm: image: vrnetlab/sonic_sonic-vs:\u0026lt;your-tag\u0026gt; nodes: leaf1: kind: sonic-vm leaf2: kind: sonic-vm host1: kind: linux image: ghcr.io/hellt/network-multitool exec: - ip link add bond0 type bond mode 802.3ad lacp_rate fast - ip link set eth1 down - ip link set eth2 down - ip link set eth1 master bond0 - ip link set eth2 master bond0 - ip link set bond0 up - ip addr add 10.10.10.101/24 dev bond0 host2: kind: linux image: ghcr.io/hellt/network-multitool exec: - ip link add bond0 type bond mode 802.3ad lacp_rate fast - ip link set eth1 down - ip link set eth2 down - ip link set eth1 master bond0 - ip link set eth2 master bond0 - ip link set bond0 up - ip addr add 10.10.10.102/24 dev bond0 links: # peer-link (PortChannel0001) - endpoints: [\u0026#34;leaf1:eth1\u0026#34;, \u0026#34;leaf2:eth1\u0026#34;] - endpoints: [\u0026#34;leaf1:eth2\u0026#34;, \u0026#34;leaf2:eth2\u0026#34;] # host1 (PortChannel0002) - endpoints: [\u0026#34;leaf1:eth3\u0026#34;, \u0026#34;host1:eth1\u0026#34;] - endpoints: [\u0026#34;leaf2:eth3\u0026#34;, \u0026#34;host1:eth2\u0026#34;] # host2 (PortChannel0003) - endpoints: [\u0026#34;leaf1:eth4\u0026#34;, \u0026#34;host2:eth1\u0026#34;] - endpoints: [\u0026#34;leaf2:eth4\u0026#34;, \u0026#34;host2:eth2\u0026#34;] Deploy it:\nsudo containerlab deploy -t simple-mclag.clab.yml The SONiC VMs take a few minutes to boot. Once containerlab inspect shows them as healthy, log in with SSH (vrnetlab\u0026rsquo;s default for SONiC is admin / admin):\nssh admin@clab-simple-mclag-leaf1 Interface mapping # Containerlab\u0026rsquo;s ethN interfaces map to SONiC front-panel ports like this:\nContainerlab SONiC eth1 Ethernet0 eth2 Ethernet4 eth3 Ethernet8 eth4 Ethernet12 Confirm the mapping on your image with show interfaces status before going further.\nClean up the factory config # A port can\u0026rsquo;t join a port channel while it has an IP address or belongs to a VLAN. Depending on the image, the default config may contain either. Check both:\nshow ip interfaces show vlan brief Remove anything on Ethernet0–Ethernet12. For example:\nsudo config interface ip remove Ethernet0 10.0.0.0/31 sudo config vlan member del 1000 Ethernet0 Then make sure the four ports are administratively up:\nfor p in Ethernet0 Ethernet4 Ethernet8 Ethernet12; do sudo config interface startup $p done Configure leaf1 # 1. Enable the ICCP daemon # MCLAG is implemented by the iccpd container, which is disabled on many images:\nsudo config feature state iccpd enabled show feature status iccpd 2. Port channels # # peer-link sudo config portchannel add PortChannel0001 sudo config portchannel member add PortChannel0001 Ethernet0 sudo config portchannel member add PortChannel0001 Ethernet4 # host-facing MCLAG members sudo config portchannel add PortChannel0002 sudo config portchannel member add PortChannel0002 Ethernet8 sudo config portchannel add PortChannel0003 sudo config portchannel member add PortChannel0003 Ethernet12 3. VLANs # The peer-link carries both VLANs tagged. The host port channels are untagged access ports in Vlan10.\nsudo config vlan add 10 sudo config vlan add 4094 sudo config vlan member add 10 PortChannel0001 sudo config vlan member add 4094 PortChannel0001 sudo config vlan member add -u 10 PortChannel0002 sudo config vlan member add -u 10 PortChannel0003 4. The MCLAG domain # Arguments, in order: domain ID, local (source) IP, peer IP, peer-link interface.\nsudo config mclag add 1 192.168.255.1 192.168.255.2 PortChannel0001 sudo config mclag member add 1 PortChannel0002,PortChannel0003 5. The ICCP interface # Mark Vlan4094 as a unique-IP interface, then give it its address:\nsudo config mclag unique-ip add Vlan4094 sudo config interface ip add Vlan4094 192.168.255.1/30 6. Save # sudo config save -y Configure leaf2 # leaf2 gets the same configuration with the two ICCP addresses swapped:\nsudo config feature state iccpd enabled sudo config portchannel add PortChannel0001 sudo config portchannel member add PortChannel0001 Ethernet0 sudo config portchannel member add PortChannel0001 Ethernet4 sudo config portchannel add PortChannel0002 sudo config portchannel member add PortChannel0002 Ethernet8 sudo config portchannel add PortChannel0003 sudo config portchannel member add PortChannel0003 Ethernet12 sudo config vlan add 10 sudo config vlan add 4094 sudo config vlan member add 10 PortChannel0001 sudo config vlan member add 4094 PortChannel0001 sudo config vlan member add -u 10 PortChannel0002 sudo config vlan member add -u 10 PortChannel0003 sudo config mclag add 1 192.168.255.2 192.168.255.1 PortChannel0001 sudo config mclag member add 1 PortChannel0002,PortChannel0003 sudo config mclag unique-ip add Vlan4094 sudo config interface ip add Vlan4094 192.168.255.2/30 sudo config save -y What ended up in CONFIG_DB # Every config command above writes to CONFIG_DB, and config save writes it out to /etc/sonic/config_db.json. The MCLAG-specific tables on leaf1 look like this:\n{ \u0026#34;MCLAG_DOMAIN\u0026#34;: { \u0026#34;1\u0026#34;: { \u0026#34;source_ip\u0026#34;: \u0026#34;192.168.255.1\u0026#34;, \u0026#34;peer_ip\u0026#34;: \u0026#34;192.168.255.2\u0026#34;, \u0026#34;peer_link\u0026#34;: \u0026#34;PortChannel0001\u0026#34; } }, \u0026#34;MCLAG_INTERFACE\u0026#34;: { \u0026#34;1|PortChannel0002\u0026#34;: { \u0026#34;if_type\u0026#34;: \u0026#34;PortChannel\u0026#34; }, \u0026#34;1|PortChannel0003\u0026#34;: { \u0026#34;if_type\u0026#34;: \u0026#34;PortChannel\u0026#34; } }, \u0026#34;MCLAG_UNIQUE_IP\u0026#34;: { \u0026#34;Vlan4094\u0026#34;: { \u0026#34;unique_ip\u0026#34;: \u0026#34;enable\u0026#34; } } } That structure is exactly what we\u0026rsquo;ll template in Part 3.\nQuick check # From either leaf:\nshow interfaces portchannel docker exec iccpd mclagdctl -i 1 dump state All three port channels should be up, and the state dump should report the keepalive as OK. Then, from host1:\ndocker exec -it clab-simple-mclag-host1 ping -c 3 10.10.10.102 If any of these checks fail, Part 4 walks through what each one is telling you.\n","date":"21 August 2026","externalUrl":null,"permalink":"/build/simple-mlag/","section":"Build","summary":"Stand up the lab in Containerlab and configure SONiC MCLAG by hand from the CLI.","title":"Simple MCLAG, Part 2: Building the Lab and Configuring SONiC","type":"build"},{"content":"","date":"21 August 2026","externalUrl":null,"permalink":"/tags/vrnetlab/","section":"Tags","summary":"","title":"vrnetlab","type":"tags"},{"content":"","date":"14 August 2026","externalUrl":null,"permalink":"/categories/design/","section":"categories","summary":"","title":"Design","type":"categories"},{"content":"Architectural deep dives, vendor/ASIC trade-offs, Redis DB state models, and SAI capabilities.\n","date":"14 August 2026","externalUrl":null,"permalink":"/design/","section":"Design","summary":"","title":"Design","type":"design"},{"content":"","date":"14 August 2026","externalUrl":null,"permalink":"/tags/layer-2/","section":"Tags","summary":"","title":"Layer 2","type":"tags"},{"content":"Multi-Chassis Link Aggregation (MCLAG) lets a server bond two NICs to two different switches and still see a single LACP partner. You get switch-level redundancy without spanning tree blocking half your links.\nThis series builds the smallest MCLAG design that is still useful: two SONiC leaves and two dual-homed hosts. In this part we design it: the topology, the addressing, and the decisions behind them. Later parts build it by hand, automate it, and break it on purpose.\nThe moving parts # Term What it is in this lab MCLAG domain The pair of leaves acting as one LACP partner. We use domain 1. Peer-link PortChannel0001 between leaf1 and leaf2. It carries host traffic that must cross between peers, plus the ICCP session. ICCP Inter-Chassis Communication Protocol. In SONiC it runs in the iccpd container over TCP port 8888, syncing state (LACP, MAC, ARP) between the peers. MCLAG member A host-facing port channel that is configured on both leaves: PortChannel0002 (host1) and PortChannel0003 (host2). Unique IP A VLAN interface that has a different IP on each peer. We use one for the ICCP VLAN. Topology # Both leaves use the same port numbering, which keeps the configuration symmetrical:\nLeaf port Connects to Port channel Ethernet0, Ethernet4 the other leaf PortChannel0001 (peer-link) Ethernet8 host1 PortChannel0002 (MCLAG member) Ethernet12 host2 PortChannel0003 (MCLAG member) Addressing # Item leaf1 leaf2 ICCP VLAN Vlan4094 Vlan4094 ICCP address (unique IP) 192.168.255.1/30 192.168.255.2/30 Host VLAN Vlan10 (L2 only) Vlan10 (L2 only) Host Address on bond0 host1 10.10.10.101/24 host2 10.10.10.102/24 Vlan10 has no IP on either leaf. Keeping the host VLAN pure layer 2 lets us focus on MCLAG itself. Adding a gateway is a follow-on post.\nDesign decisions # Run ICCP over a VLAN on the peer-link # ICCP needs IP reachability between the peers. The simplest option that needs no extra cabling is a dedicated VLAN (4094) tagged on the peer-link, with a /30 between the two leaves.\nThat VLAN interface has a different address on each leaf, so it must be marked as a unique IP interface. By default, MCLAG assumes VLAN interfaces are mirrored on both peers.\nTrade-off: with ICCP on the peer-link, losing the whole peer-link also drops the ICCP session. That\u0026rsquo;s why the peer-link is a two-member bundle. In production, consider running ICCP over a separate routed path (for example, loopbacks reachable via the spines) so that a peer-link failure and a peer failure look different. Make the peer-link a port channel # The peer-link carries ICCP and any traffic that lands on the \u0026ldquo;wrong\u0026rdquo; leaf, such as flooded frames or traffic to a host whose link to the other leaf is down. Size it for a realistic failure case, not the steady state. Even in a lab, two members show how the bundle behaves when one fails.\nKeep member names identical on both leaves # Each host\u0026rsquo;s port channel has the same name on both leaves (PortChannel0002 for host1, PortChannel0003 for host2). Symmetrical configuration is easier to read and diff, and much easier to template in Part 3.\nHosts run plain LACP # The hosts need nothing MCLAG-specific: a Linux bond0 in 802.3ad mode. If MCLAG is working, both leaves present the same LACP partner, and the host puts both links in one aggregator. If MCLAG is broken, the host sees two different partners. That symptom is the first thing to check in Part 4.\nWhat we\u0026rsquo;re not covering (yet) # A layer-3 gateway on Vlan10. That brings in the MAC/ARP sync and gateway behavior that deserves its own post. EVPN multihoming. It\u0026rsquo;s the standards-based alternative to MCLAG in VXLAN fabrics. Uplinks to spines. This lab is a single leaf pair. Next up: Part 2: Building the lab and configuring MCLAG.\n","date":"14 August 2026","externalUrl":null,"permalink":"/design/simple-mlag/","section":"Design","summary":"The smallest useful SONiC MCLAG design: two leaves, a peer-link, and dual-homed hosts.","title":"Simple MCLAG, Part 1: Designing the Topology","type":"design"},{"content":"Evaluating SONiC, or already running it and hitting the edges? We can help with:\nDesign. Architecture reviews, platform and ASIC trade-offs, MCLAG vs. EVPN multihoming, and migration plans from proprietary NOSes. Build. Containerlab and vrnetlab environments that mirror your production fabric, so changes get tested before they ship. Automate. Ansible and CONFIG_DB templating, ZTP, gNMI telemetry, and CI/CD pipelines for network changes. Troubleshoot. Root-cause analysis for production issues, from LACP and BGP state through SAI and the ASIC. Get in touch # Email tyler@runsonic.io with a few lines about your environment and what you\u0026rsquo;re trying to do. We\u0026rsquo;ll reply to set up a call.\n","externalUrl":null,"permalink":"/consulting/","section":"Master the open network operating system.","summary":"","title":"Work With Us","type":"page"}]