D2 Diagramming Reference
Declarative diagramming with D2. Shapes, containers, sequence diagrams, and Antora integration via Kroki.
Basics
Shape declarations and connections
server: Web Server
database: PostgreSQL {
shape: cylinder
}
server -> database: queries
Shapes are declared by name. Braces add properties. Arrows (->, ←, <→) create connections.
Connection labels and styling
client -> lb: HTTPS {
style.stroke-dash: 3
}
lb -> app1: HTTP
lb -> app2: HTTP
app1 -> db
app2 -> db
Shapes
Shape types — rectangle, cylinder, circle, diamond, and more
api: REST API {shape: hexagon}
db: Database {shape: cylinder}
decision: Approve? {shape: diamond}
queue: Message Queue {shape: queue}
cloud: AWS {shape: cloud}
user: Admin {shape: person}
docs: Manual {shape: page}
pkg: Package {shape: package}
Containers (Nesting)
Nested containers for logical grouping
network: Network Layer {
fw: Firewall
lb: Load Balancer
fw -> lb
}
application: App Layer {
api: API Server
worker: Background Worker
}
network.lb -> application.api
Containers group related components. Access nested shapes with dot notation.
Layouts
Layout engine selection via CLI
d2 --layout=elk diagram.d2 output.svg # Hierarchical (default)
d2 --layout=dagre diagram.d2 output.svg # Directed acyclic graph
Grid layout for structured placement
grid: {
grid-rows: 2
grid-columns: 3
cell1
cell2
cell3
cell4
cell5
cell6
}
Styling with Catppuccin Mocha
Catppuccin Mocha palette for consistent theming
vars: {
d2-config: {
theme-id: 200
}
}
server: Web Server {
style: {
fill: "#1e1e2e" # Base
stroke: "#89b4fa" # Blue
font-color: "#cdd6f4" # Text
}
}
database: PostgreSQL {
shape: cylinder
style: {
fill: "#1e1e2e"
stroke: "#a6e3a1" # Green
font-color: "#cdd6f4"
}
}
server -> database: queries {
style: {
stroke: "#f5c2e7" # Pink
}
}
Sequence Diagrams
Sequence diagram with actors and messages
shape: sequence_diagram
client: Client
server: API Server
db: Database
client -> server: POST /login
server -> db: SELECT user
db -> server: user record
server -> client: 200 OK + JWT
Icons and Images
Icon from a URL
k8s: Kubernetes {
icon: https://icons.terrastruct.com/tech/kubernetes.svg
}
CLI Commands
Render to SVG and PNG
d2 diagram.d2 diagram.svg
d2 --format png diagram.d2 diagram.png
Watch mode — re-renders on file change
d2 --watch diagram.d2 diagram.svg
# Opens browser with live reload
Dark theme rendering
d2 --theme 200 diagram.d2 dark.svg # Dark Mauve theme
d2 --dark-theme 200 diagram.d2 out.svg # Auto dark/light
Quick Render from Terminal
d2 requires a file path — no stdin support. Heredoc to a temp file bridges the gap.
Prototype a diagram inline
cat << 'EOF' > /tmp/diagram.d2
direction: right
client -> switch: 802.1X
switch -> ise: RADIUS {
style.stroke: "#a6e3a1"
style.animated: true
}
ise -> dc: LDAPS
EOF
d2 --theme=200 /tmp/diagram.d2 /tmp/diagram.svg && xdg-open /tmp/diagram.svg
Single-quoted 'EOF' prevents shell expansion — {braces} and $vars pass through literally to D2. The && chain opens the browser only on successful render.
Watch mode — live reload as you edit the temp file
cat << 'EOF' > /tmp/diagram.d2
a -> b -> c
EOF
d2 --watch --theme=200 /tmp/diagram.d2 /tmp/diagram.svg
# Opens browser — saves to /tmp/diagram.d2 trigger re-render
Promote prototype to project
cp /tmp/diagram.d2 docs/modules/ROOT/examples/diagrams/network/new-diagram.d2
d2 --theme=200 \
docs/modules/ROOT/examples/diagrams/network/new-diagram.d2 \
docs/modules/ROOT/images/diagrams/d2/network/new-diagram.svg
Batch render a category
for f in docs/modules/ROOT/examples/diagrams/network/*.d2; do
out="docs/modules/ROOT/images/diagrams/d2/network/$(basename "${f%.d2}.svg")"
d2 --theme=200 "$f" "$out" && printf " → %s\n" "$out"
done
Integration with Antora (Kroki)
D2 diagram block in AsciiDoc via Kroki
[d2,target=network-diagram,format=svg]
....
server -> database: SQL
server -> cache: Redis
....
Kroki renders the D2 source during make build. The diagram appears inline as SVG. Requires Kroki running (handled by make).
See Also
-
Kroki — diagram rendering server
-
Diagrams as Code — tool comparison and selection guide