TRANSLATION

Network diagrams as code: Mermaid for network engineers

A working reference for documenting a network in text instead of a drawing tool. The flowchart grammar, the patterns for campus topology, the sequence and state diagrams worth knowing, and the Python that generates the whole thing from LLDP neighbor data.

Reading this, you will:
  • Write a network topology in Mermaid from scratch, with real interface names on the links.
  • Pick between a flowchart, a sequence diagram, and a state diagram for a given job.
  • Generate a topology diagram from neighbor data with plain Python.

What is a network diagram as code?

A network diagram as code is a topology written in plain text and stored in git next to the configs it describes. You declare the devices and the links, and a layout engine draws the picture. Mermaid is the version of this that already renders in GitHub, GitLab, Notion, Obsidian, and VS Code, with no plugin and no export step.

The problem it solves is drift. Network diagrams go stale the moment someone saves them, because the network keeps changing and the drawing does not. Nothing connects “I added a switch” to “I opened the drawing file and moved a box.” So the diagram on the wiki describes a network that stopped existing sometime last spring, and the network team stops trusting it.

Text fixes the root cause in two steps. First, the diagram lives in the same repository as the change, so it diffs in the pull request and a removed link is a removed line. Second, a diagram made of text is a string, which means the Python you already know can write it for you from live device data.

Everything below is the working reference: the grammar, the network patterns, and the generator.

The grammar, in about five minutes

A Mermaid flowchart is a direction, then one statement per line.

flowchart LR
  core["core-sw01"]
  acc["acc-sw01"]
  core --- acc
The smallest useful diagram
Rendering diagram…
View diagram source - it's just text (Mermaid). Diagrams-as-code is how modern network docs work; the flagship course has a free module on it.
flowchart LR
  core["core-sw01"]
  acc["acc-sw01"]
  core --- acc
The smallest useful diagram

Four things are going on there.

  • flowchart LR sets the direction. LR is left to right, TD is top down. Campus topology usually reads best as TD, and a path or a chain usually reads best as LR.
  • core is the node id. It has to be machine safe, so no dots, no slashes, no spaces. This is what you refer to in link lines.
  • ["core-sw01"] is the human label, in quotes. Quoting it means you can put a hostname with dots or a slash in it and Mermaid will not try to parse the punctuation.
  • --- is a link. Solid and undirected, which is what you want for a physical link between two devices. Use --> when the arrow means something, like traffic flow or a decision path.

The id and the label being separate is the detail people trip on first. core-sw01 is not a legal id, because the hyphen means something to the parser. So the id gets sanitized and the label keeps the real name.

edge-rtr01.corp.example.com cannot be used as an id as written. The dots and the hyphen come out of the id, leaving something like edge_rtr01, and the full name goes in the quoted label where the punctuation does no harm.

Put the interfaces on the wire

Text between pipes becomes the edge label, and that is where the diagram earns its keep over a drawing:

core ---|"Gi1/0/1 to Gi1/0/48"| acc

Link styles carry meaning too. A thick link marks an uplink, a dotted link marks a backup path:

Link styles: uplink, peer link, standby
Rendering diagram…
View diagram source - it's just text (Mermaid). Diagrams-as-code is how modern network docs work; the flagship course has a free module on it.
flowchart TD
  edge["edge-rtr01"]
  core1["core-sw01"]
  core2["core-sw02"]
  acc1["acc-sw01"]
  edge ===|"Te1/1/1"| core1
  core1 ---|"Po1"| core2
  core1 ---|"Gi1/0/1 to Gi1/0/48"| acc1
  core2 -.-|"standby"| acc1
Link styles: uplink, peer link, standby

How do I draw a campus topology?

Group devices with subgraph. One subgraph per site, per closet, or per tier, whichever way your team already talks about the network.

Three-tier campus with subgraphs
Rendering diagram…
View diagram source - it's just text (Mermaid). Diagrams-as-code is how modern network docs work; the flagship course has a free module on it.
flowchart TD
  subgraph wan["WAN edge"]
    edge["edge-rtr01"]
  end
  subgraph dc["Core"]
    core1["core-sw01"]
    core2["core-sw02"]
  end
  subgraph idf2["Floor 2 IDF"]
    acc1["acc-sw01"]
    acc2["acc-sw02"]
  end
  edge ===|"Te1/1/1"| core1
  edge ===|"Te1/1/2"| core2
  core1 ---|"Po1"| core2
  core1 ---|"Gi1/0/1"| acc1
  core1 ---|"Gi1/0/2"| acc2
  core2 ---|"Gi1/0/1"| acc1
  core2 ---|"Gi1/0/2"| acc2
Three-tier campus with subgraphs

Subgraphs need an id and a quoted label, same as nodes, and they close with end.

Before you use these on your own topology, look at the diagram above: every link in it crosses a subgraph boundary, and none of them needed extra syntax to do it. A subgraph groups nodes for the reader and does nothing else. It does not constrain what can link to what. If you expected the boundary to mean something, drop that assumption now rather than after you have drawn forty of these.

One piece of practical advice on scope. A campus with 200 access switches produces a diagram nobody can read, and no amount of layout tuning fixes it. Generate one diagram per site or per closet, and keep a separate small diagram for the core. Splitting by site is free when the diagram is generated, because it is a filter on the data rather than an afternoon of rearranging boxes.

When should I use a sequence or state diagram?

Flowcharts answer “what connects to what.” Two other diagram types answer questions a flowchart handles badly, and both are worth the ten minutes it takes to learn them.

A sequence diagram shows the order of messages between participants. It is the right shape for a protocol exchange, a failover test, or an onboarding flow:

DHCP DORA across a relay
Rendering diagram…
View diagram source - it's just text (Mermaid). Diagrams-as-code is how modern network docs work; the flagship course has a free module on it.
sequenceDiagram
  participant C as Client
  participant R as Relay agent
  participant S as DHCP server
  C->>R: DISCOVER, broadcast
  R->>S: DISCOVER, unicast with giaddr set
  S->>R: OFFER
  R->>C: OFFER
  C->>R: REQUEST
  R->>S: REQUEST
  S->>R: ACK
  R->>C: ACK
DHCP DORA across a relay

A state diagram shows the states one thing moves through and what triggers each move. It is the right shape for a lifecycle:

STP port states
Rendering diagram…
View diagram source - it's just text (Mermaid). Diagrams-as-code is how modern network docs work; the flagship course has a free module on it.
stateDiagram-v2
  [*] --> Blocking
  Blocking --> Listening: selected as designated or root port
  Listening --> Learning: forward delay expires
  Learning --> Forwarding: forward delay expires
  Forwarding --> Blocking: better BPDU received
  Blocking --> [*]: port goes down
STP port states

How do I generate a diagram from LLDP output?

Start with neighbor data normalized into a list of links. However you collect it, with Netmiko, RESTCONF, or a parser over show lldp neighbors detail, the shape you want is this:

neighbors = [
    {"local": "acc-sw01", "local_intf": "Gi1/0/48",
     "remote": "core-sw01", "remote_intf": "Gi1/0/1"},
    {"local": "acc-sw02", "local_intf": "Gi1/0/48",
     "remote": "core-sw01", "remote_intf": "Gi1/0/2"},
    {"local": "core-sw01", "local_intf": "Te1/1/1",
     "remote": "edge-rtr01", "remote_intf": "Gi0/0/0"},
]

The generator is three steps: sanitize hostnames into legal ids, declare each device once, then write one edge per link.

def node_id(hostname):
    """Turn a hostname into a Mermaid-safe id."""
    return hostname.replace(".", "_").replace("-", "_")


def to_mermaid(neighbors):
    lines = ["flowchart TD"]

    # Declare each device once, in first-seen order.
    seen = []
    for link in neighbors:
        for host in (link["local"], link["remote"]):
            if host not in seen:
                seen.append(host)
                lines.append(f'  {node_id(host)}["{host}"]')

    # One edge per link, both interface names on the label.
    for link in neighbors:
        label = f'{link["local_intf"]} to {link["remote_intf"]}'
        lines.append(
            f'  {node_id(link["local"])} ---|"{label}"| '
            f'{node_id(link["remote"])}'
        )

    return "\n".join(lines)


print(to_mermaid(neighbors))

That prints:

flowchart TD
  acc_sw01["acc-sw01"]
  core_sw01["core-sw01"]
  acc_sw02["acc-sw02"]
  edge_rtr01["edge-rtr01"]
  acc_sw01 ---|"Gi1/0/48 to Gi1/0/1"| core_sw01
  acc_sw02 ---|"Gi1/0/48 to Gi1/0/2"| core_sw01
  core_sw01 ---|"Te1/1/1 to Gi0/0/0"| edge_rtr01

Which renders as:

Generated from neighbor data
Rendering diagram…
View diagram source - it's just text (Mermaid). Diagrams-as-code is how modern network docs work; the flagship course has a free module on it.
flowchart TD
  acc_sw01["acc-sw01"]
  core_sw01["core-sw01"]
  acc_sw02["acc-sw02"]
  edge_rtr01["edge-rtr01"]
  acc_sw01 ---|"Gi1/0/48 to Gi1/0/1"| core_sw01
  acc_sw02 ---|"Gi1/0/48 to Gi1/0/2"| core_sw01
  core_sw01 ---|"Te1/1/1 to Gi0/0/0"| edge_rtr01
Generated from neighbor data

If you want to see this run against your own network before you write any code, the network diagram generator does exactly the above in your browser: paste the output, read the link table it extracted, copy the Mermaid.

Write the output to topology.mmd, commit it, and the diagram is as fresh as the last collection run. Put the collection in cron or in CI and the diagram updates itself. The engineer who owns it does not have to remember, because updating it is no longer a manual step.

When is a drawing tool still the right answer?

Diagrams as code is not a rule that every picture has to become text. Two categories are still better served by a drawing tool, and pretending otherwise wastes your time.

Physical space. Rack elevations, cable runs, floor plans, and anything where the position of a thing on the page corresponds to its position in the building. Mermaid decides placement for you, which is the whole point everywhere else and exactly wrong here.

Polished one-time deliverables. The architecture diagram in a proposal, the executive one-pager, the slide the CIO sees. These need visual control that a layout engine will not give you, and they are drawn once and thrown away, so drift is not a problem.

Those are the diagrams you maintain by hand, forever, with your own discipline as the only thing keeping them current. That is fine for two diagrams. Your whole topology is a different scale of problem.

Can AI draw my network diagram?

A model drafts Mermaid faster than you can type it, and three uses pay off right away.

  • Data to draft. Paste a neighbor table or an inventory export and ask for the flowchart. You get a renderable draft in seconds instead of ten minutes of careful typing.
  • One diagram type to another. You wrote a failover procedure as a flowchart and realize halfway through that it is really a message exchange, so it should be a sequence diagram. Converting between them is mechanical restructuring, which models do well.
  • Syntax to English. You inherit a diagram from a teammate and hit syntax you have not seen. Paste it and ask what each line does, then verify by changing a line and rerendering.

There are two failure modes, and they are not equally dangerous.

Invented syntax is the first. The model writes an arrow token that does not exist, the renderer refuses, and you find out in one second. This one is harmless.

Rendering cleanly while contradicting your data is the second. A link that is not in the neighbor table, a device on the wrong tier, an interface name transposed between two ports. The picture looks professional, so it gets committed, and now it is wrong in a way that is harder to catch than a stale diagram, because it looks fresh.

What does that failure look like?

Here is one, small enough to check by eye. The neighbor data has two links:

core-sw01  Gi1/0/1  <->  acc-sw01  Gi1/0/48
core-sw01  Gi1/0/2  <->  acc-sw02  Gi1/0/48

And here is the diagram a model produced from it.

Generated, renders cleanly, and is wrong
Rendering diagram…
View diagram source - it's just text (Mermaid). Diagrams-as-code is how modern network docs work; the flagship course has a free module on it.
flowchart TD
  core["core-sw01"]
  acc1["acc-sw01"]
  acc2["acc-sw02"]
  core ---|"Gi1/0/1 to Gi1/0/48"| acc1
  core ---|"Gi1/0/2 to Gi1/0/48"| acc2
  acc1 ---|"Gi1/0/47"| acc2
Generated, renders cleanly, and is wrong

The neighbor data reported two links. The diagram has three.

The extra link, between the two access switches, is invented. Nothing in the neighbor tables reported it. The model drew the shape an access layer usually has instead of the shape this data describes, and because that shape is normal, the picture looks right.

The cost is operational, not cosmetic. That diagram says acc-sw01 has a path that survives losing the core. It does not. Plan a maintenance window off this picture and you isolate a floor.

Corrected, with only the links the data reported:

Only the links the data reported
Rendering diagram…
View diagram source - it's just text (Mermaid). Diagrams-as-code is how modern network docs work; the flagship course has a free module on it.
flowchart TD
  core["core-sw01"]
  acc1["acc-sw01"]
  acc2["acc-sw02"]
  core ---|"Gi1/0/1 to Gi1/0/48"| acc1
  core ---|"Gi1/0/2 to Gi1/0/48"| acc2
Only the links the data reported

Two checks catch both failure modes. Render it, then compare it line by line against the data it came from. If you generated the diagram from data with code, the second check is the code review, which is a better place for it.

The cheat sheet

You want Write
Top-down flowchart flowchart TD
Left-to-right flowchart flowchart LR
A device core1["core-sw01"]
A physical link a --- b
A link with interfaces a ---|"Gi1/0/1 to Gi1/0/48"| b
An uplink, drawn thick a === b
A backup path, drawn dotted a -.- b
A directional flow, where the arrow means something a --> b
Group into a site or tier subgraph idf2["Floor 2 IDF"]end
Protocol message order sequenceDiagram
A message C->>S: DISCOVER
A lifecycle stateDiagram-v2
A transition Listening --> Learning: forward delay expires
Start or end state [*]

Where to go from here

The lesson version of this is free on RouteSwitchU, and it includes the parts a reference page cannot give you: a terminal recording of a topology drawing itself from neighbor data, a Python box you can run in the browser, and a graded lab that checks the four generator functions you would need to build this for real. It asks for a free account and nothing else.

Open the free Mermaid lesson

The lesson sits inside the AI-Assisted Network Automation course, where the same three moves (collect the data, put it in git, generate the artifact) get pointed at configuration, validation, and change records instead of pictures. Diagrams are the low-risk version. Nothing breaks if the diagram is wrong on the first try, which is why they are a good place to start.

If you want the collection side first, Discover and document the network is where the neighbor data in this article comes from, and Version the source of truth in git is what makes the diff-in-a-pull-request part work.