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 graphvizgives 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:
digraphDirected graph. Edges have arrows. Written with->.graphUndirected 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:
labelThe text displayed inside the node.shapeShape type:box,ellipse,circle,cylinder,diamond,plaintext, and many more.styleVisual style:filled,dashed,bold,rounded.fillcolorBackground color whenstyle=filled.colorBorder/line color.fontnameFont family for the label.fontsizeFont 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:
labelText on the edge.stylesolid,dashed,dotted,bold.colorEdge color.arrowheadArrow style:normal,vee,none,diamond,crow.penwidthLine thickness.constraintSet tofalseto 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:
rankdirLayout direction:TB(top to bottom, default),LR(left to right),BT,RL.bgcolorBackground color of the entire graph.fontnameDefault font for all text.nodesepMinimum space between nodes in the same rank.ranksepMinimum space between ranks (rows or columns of nodes).splinesEdge 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:
neatoSpring-model layout for undirected graphs.fdpForce-directed placement, good for large sparse graphs.circoCircular layout, useful for cyclic structures.twopiRadial layout radiating from a center node.osageTreemap-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 undirectedgraph. Directed edges requiredigraph. 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
widthorfontsizeto adjust, or break labels with\n. - Ignoring layout direction. If your diagram is too tall or too wide, try switching
rankdirbetweenTBandLR. - Quoting attribute values inconsistently.
fillcolor=#E8F5E9works, butfillcolor="#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=sameto align nodes horizontally.{ rank=same; A; B; C }forces these nodes into the same row or column. - Use
compound=truewith 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
fontsizeandfontnameconsistently so your diagram looks professional when exported to PDF.
Quick reference: common node shapes
boxRectangleellipseOval (default)circlePerfect circlediamondDecision pointcylinderDatabase symbolplaintextText only, no borderhexagonHexagonalrecordTable-like with fields using curly-brace syntaxtrapeziumTrapezoidparallelogramSlanted rectanglecomponentUML component symbolfolderFolder icon
See the full shape documentation for the complete list with visual examples.
Practical checklist before you publish a DOT diagram
- Run
dot -Tsvg -o output.svg input.gvand open the SVG to check for clipped labels or overlapping nodes. - Set
fontnameto a widely available font (Helvetica, Arial, or Courier) so rendering is consistent across systems. - Add
labelattributes to edges where direction or data flow isn't obvious. - Use clusters to group related nodes don't leave 50 nodes floating in a flat graph.
- Commit the
.gvsource file, not just the rendered image. The source is your diagram. - Set
rankdir=LRif your top-down graph is taller than it is wide. - Run
dot -Tpng -Gdpi=150for higher-resolution PNG exports. - Test rendering with at least two engines (
dotandneato) to see which layout communicates best.
Online Diagram Markup Language Editor with Live Preview
Best Diagram Markup Languages for Software Architecture Documentation
Plantuml vs Mermaid vs Graphviz: Developer's Diagram Language Comparison Guide
How Ansi and Iso Flowchart Symbols Differ
Flowchart Symbol Conventions for Business Process Mapping
Network Topology Diagram Codes Explained for Ccna Students