If you've ever needed to turn a messy collection of relationships between servers, people, processes, or data into a clean visual diagram, Graphviz and its DOT language are tools worth knowing. Unlike drag-and-drop diagram editors, DOT lets you describe a graph in plain text, then renders it into an image automatically. This reference on Graphviz DOT language syntax and examples covers everything from basic node declarations to advanced layout tricks, so you can stop fighting with formatting and start describing what you actually mean.

What is the Graphviz DOT language?

DOT is a plain-text graph description language created by AT&T Labs Research. You write a short file describing nodes (things) and edges (connections between things), and Graphviz's layout engine called dot arranges them into a readable diagram. The output can be SVG, PNG, PDF, or several other formats.

Here's the simplest possible DOT graph:

digraph {
 A -> B
 B -> C
}

That tells Graphviz: there are three nodes, A points to B, and B points to C. The engine handles all positioning automatically. For software architecture documentation, this matters because you spend time thinking about what connects to what, not pixel placement.

Why use text-based graph descriptions instead of a visual editor?

Visual editors work fine for quick diagrams. But they have real limitations:

  • Version control. DOT files are plain text. You can diff them, merge them, and review changes in a pull request like any other source file.
  • Reproducibility. Given the same DOT source, Graphviz always produces the same layout. No "who moved my boxes" surprises.
  • Scale. Try editing a 200-node diagram in a drag-and-drop tool. With DOT, adding node 201 is one more line.
  • Automation. You can generate DOT programmatically from databases, configuration files, or code.

For teams that already use diagram markup languages for software architecture documentation, DOT fits naturally into existing workflows alongside tools like Mermaid.

How do you install Graphviz?

Before writing any DOT code, you need the Graphviz tools installed. Here's how on common platforms:

  • macOS: brew install graphviz
  • Ubuntu/Debian: sudo apt install graphviz
  • Windows: Download the installer from the official Graphviz download page.
  • Python (pip): pip install graphviz gives you both the Python bindings and you can pair it with the system package.

After installation, verify it works by running dot -V in your terminal. You should see a version number.

What are the basic building blocks of DOT syntax?

Graph types: digraph vs. graph

DOT has two graph types:

  • digraph Directed graph. Edges have arrows. Written with ->.
  • graph Undirected graph. Edges have no arrows. Written with --.
digraph {
 A -> B
 B -> C
}

graph {
 A -- B
 B -- C
}

The first produces arrows; the second draws plain lines. Choose based on whether direction matters in your relationship.

Nodes

Nodes are created the moment you reference them. But you can also declare them explicitly with attributes:

digraph {
 A [label="Frontend", shape=box, style=filled, fillcolor="#E8F5E9"]
 B [label="API Gateway", shape=box]
 C [label="Database", shape=cylinder]
 A -> B
 B -> C
}

Common node attributes include:

  • label The text displayed inside the node.
  • shape Shape type: box, ellipse, circle, cylinder, diamond, plaintext, and many more.
  • style Visual style: filled, dashed, bold, rounded.
  • fillcolor Background color when style=filled.
  • color Border/line color.
  • fontname Font family for the label.
  • fontsize Font size in points.

Edges

Edges connect nodes and can carry attributes too:

digraph {
 A -> B [label="HTTP", color=blue, style=dashed]
 B -> C [label="SQL query", color=red, penwidth=2]
}

Useful edge attributes:

  • label Text on the edge.
  • style solid, dashed, dotted, bold.
  • color Edge color.
  • arrowhead Arrow style: normal, vee, none, diamond, crow.
  • penwidth Line thickness.
  • constraint Set to false to let an edge not affect ranking (layout depth).

Subgraphs and clusters

Subgraphs group related nodes. When a subgraph's name starts with cluster_, Graphviz draws a box around the group:

digraph {
 subgraph cluster_frontend {
 label="Frontend"
 style=dashed
 color=gray
 React [shape=box]
 Redux [shape=box]
 React -> Redux
 }

 subgraph cluster_backend {
 label="Backend"
 style=dashed
 color=gray
 API [shape=box]
 DB [shape=cylinder]
 API -> DB
 }

 Redux -> API
}

Clusters are helpful for showing system boundaries, microservice groupings, or network zones.

How do you set global defaults and styles?

Instead of repeating attributes on every node or edge, use global statements at the top of the graph:

digraph {
 rankdir=LR
 bgcolor="#FAFAFA"
 node [shape=box, style="rounded,filled", fillcolor="#E3F2FD", fontname="Helvetica"]
 edge [color="#666666", fontsize=10]
 
 A -> B -> C
}

Key global graph attributes:

  • rankdir Layout direction: TB (top to bottom, default), LR (left to right), BT, RL.
  • bgcolor Background color of the entire graph.
  • fontname Default font for all text.
  • nodesep Minimum space between nodes in the same rank.
  • ranksep Minimum space between ranks (rows or columns of nodes).
  • splines Edge routing: ortho (right-angle edges), polyline, curved, false (straight lines).

How do you render DOT files into images?

Once you have a .dot or .gv file, render it from the command line:

dot -Tpng mygraph.dot -o mygraph.png
dot -Tsvg mygraph.dot -o mygraph.svg
dot -Tpdf mygraph.dot -o mygraph.pdf

Other layout engines besides dot are available:

  • neato Spring-model layout for undirected graphs.
  • fdp Force-directed placement, good for large sparse graphs.
  • circo Circular layout, useful for cyclic structures.
  • twopi Radial layout radiating from a center node.
  • osage Treemap-style layout.

Try neato -Tpng mygraph.dot -o output.png for network diagrams where hierarchy doesn't matter as much.

What does a real-world example look like?

Here's a practical example showing a basic microservice architecture:

digraph Microservices {
 rankdir=LR
 bgcolor="white"
 node [shape=box, style="rounded,filled", fontname="Helvetica", fontsize=11]
 edge [fontname="Helvetica", fontsize=9, color="#555555"]

 User [label="User\nBrowser", fillcolor="#FFF3E0", shape=ellipse]
 
 subgraph cluster_gateway {
 label="API Gateway"
 style=dashed
 color="#BDBDBD"
 Gateway [label="Kong\nGateway", fillcolor="#E8EAF6"]
 }

 subgraph cluster_services {
 label="Microservices"
 style=dashed
 color="#BDBDBD"
 Auth [label="Auth\nService", fillcolor="#E8F5E9"]
 Orders [label="Orders\nService", fillcolor="#E3F2FD"]
 Payments [label="Payments\nService", fillcolor="#FCE4EC"]
 }

 Postgres [label="PostgreSQL", shape=cylinder, fillcolor="#FFF9C4"]
 RabbitMQ [label="RabbitMQ", shape=trapezium, fillcolor="#F3E5F5"]

 User -> Gateway [label="HTTPS"]
 Gateway -> Auth [label="JWT validation"]
 Gateway -> Orders [label="REST"]
 Orders -> Postgres [label="reads/writes"]
 Orders -> RabbitMQ [label="publishes events"]
 Payments -> RabbitMQ [label="consumes events"]
 Payments -> Postgres [label="writes"]
}

This produces a clear left-to-right diagram with grouped services, labeled edges, and distinct shapes for different infrastructure types. It's far easier to maintain this as text than to redraw it in a GUI tool every time a service changes.

What common mistakes do people make with DOT syntax?

  • Forgetting semicolons between statements. While DOT often works without them, mixing -> chains with attribute blocks on the same line without semicolons can produce parse errors. When in doubt, end statements with ;.
  • Using -> in an undirected graph. Directed edges require digraph. Undirected graphs use --.
  • Node IDs with spaces or special characters. Wrap them in quotes: "My Node". Unquoted IDs can only contain letters, numbers, and underscores.
  • Overlapping labels. Long labels on small nodes get clipped. Use width or fontsize to adjust, or break labels with \n.
  • Ignoring layout direction. If your diagram is too tall or too wide, try switching rankdir between TB and LR.
  • Quoting attribute values inconsistently. fillcolor=#E8F5E9 works, but fillcolor="#E8F5E9" is safer, especially with spaces or special characters in values.

How does DOT compare to Mermaid for diagrams?

DOT and Mermaid diagram syntax serve similar purposes but target different strengths. DOT gives you deep control over layout, node shapes, edge routing, and visual attributes. It handles large, complex graphs with many nodes better than most alternatives. Mermaid is simpler to learn, renders natively in many Markdown platforms (including GitHub), and works well for flowcharts, sequence diagrams, and Gantt charts where you don't need fine-grained layout control.

Use DOT when:

  • You need precise control over graph appearance.
  • The graph is large or structurally complex.
  • You want to automate diagram generation from code or data.
  • You need output in multiple formats (PDF, SVG, PNG).

Use Mermaid when:

  • You want diagrams directly inside Markdown documents.
  • The graph is relatively simple.
  • You prefer a lighter syntax with less configuration.

Where can you run DOT code without installing anything?

If you want to try DOT without installing Graphviz locally, these online tools render DOT code in your browser:

  • Graphviz Online Simple editor with live preview.
  • WebGraphviz Paste DOT code and get an SVG instantly.
  • Edotor Full-featured editor with multiple layout engine options and a GUI for tweaking attributes.

These are useful for quick prototyping or sharing a diagram with someone who doesn't have Graphviz installed.

What are some handy tips for cleaner DOT diagrams?

  • Use rank=same to align nodes horizontally. { rank=same; A; B; C } forces these nodes into the same row or column.
  • Use compound=true with cluster subgraphs to route edges directly to cluster boundaries instead of individual nodes.
  • Use HTML-like labels for rich formatting: label=<<TABLE><TR><TD>Cell</TD></TR></TABLE>>.
  • Comment your DOT files with // or / / just like regular code.
  • Keep DOT source in version control. Generate images in CI/CD so diagrams are always up to date with the architecture they describe.
  • Use fontsize and fontname consistently so your diagram looks professional when exported to PDF.

Quick reference: common node shapes

  • box Rectangle
  • ellipse Oval (default)
  • circle Perfect circle
  • diamond Decision point
  • cylinder Database symbol
  • plaintext Text only, no border
  • hexagon Hexagonal
  • record Table-like with fields using curly-brace syntax
  • trapezium Trapezoid
  • parallelogram Slanted rectangle
  • component UML component symbol
  • folder Folder icon

See the full shape documentation for the complete list with visual examples.

Practical checklist before you publish a DOT diagram

  1. Run dot -Tsvg -o output.svg input.gv and open the SVG to check for clipped labels or overlapping nodes.
  2. Set fontname to a widely available font (Helvetica, Arial, or Courier) so rendering is consistent across systems.
  3. Add label attributes to edges where direction or data flow isn't obvious.
  4. Use clusters to group related nodes don't leave 50 nodes floating in a flat graph.
  5. Commit the .gv source file, not just the rendered image. The source is your diagram.
  6. Set rankdir=LR if your top-down graph is taller than it is wide.
  7. Run dot -Tpng -Gdpi=150 for higher-resolution PNG exports.
  8. Test rendering with at least two engines (dot and neato) to see which layout communicates best.