
Creating Diagrams in Python with Diagram-as-Code Tools
Creating diagrams for system architecture, network topology, or application workflows has traditionally been a tedious task involving manual drawing tools and constant updates. Diagram-as-code tools revolutionize this process by allowing developers to generate professional diagrams using simple Python scripts, making documentation more maintainable, version-controllable, and automatically updatable. This guide will walk you through the most popular Python diagram-as-code libraries, show you how to create various types of technical diagrams, and help you choose the right tool for your specific needs.
How Diagram-as-Code Tools Work
Diagram-as-code tools transform textual descriptions or programmatic definitions into visual representations. Instead of dragging and dropping icons in a GUI application, you write code that describes the components and their relationships. The tool then renders this into formats like PNG, SVG, or PDF.
The core concept revolves around three main approaches:
- Declarative syntax: Tools like Diagrams use Python objects to represent infrastructure components
- Domain-specific languages: Tools like PlantUML use specialized markup languages
- Graph-based definitions: Libraries like Graphviz use node and edge relationships
Python-based solutions typically leverage rendering engines like Graphviz, Cairo, or custom SVG generators to produce the final output. The major advantage is that your diagrams become part of your codebase, enabling version control, automated generation, and integration with CI/CD pipelines.
Popular Python Diagram-as-Code Libraries
Library | Best For | Output Formats | Learning Curve | GitHub Stars |
---|---|---|---|---|
Diagrams | Cloud architecture diagrams | PNG, JPG, SVG, PDF | Low | 35k+ |
PlantUML (Python wrapper) | UML diagrams, sequence diagrams | PNG, SVG, PDF, LaTeX | Medium | Multiple implementations |
Graphviz (Python bindings) | Generic graphs, flowcharts | PNG, SVG, PDF, PostScript | Medium-High | 1.5k+ |
Mermaid (Python integration) | Flowcharts, gantt charts | PNG, SVG, PDF | Low-Medium | 65k+ (JS project) |
Step-by-Step Implementation Guide
Setting Up the Diagrams Library
The Diagrams library is the most popular choice for infrastructure diagrams. Here’s how to get started:
# Install required dependencies
pip install diagrams
# On Ubuntu/Debian, you might need Graphviz
sudo apt-get install graphviz
# On macOS
brew install graphviz
# On Windows, download from https://graphviz.org/download/
Creating Your First Architecture Diagram
from diagrams import Diagram
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import ELB
from diagrams.aws.storage import S3
with Diagram("Web Service Architecture", show=False, direction="TB"):
lb = ELB("Load Balancer")
with Cluster("Web Servers"):
web_servers = [EC2("web1"), EC2("web2"), EC2("web3")]
db = RDS("Database")
storage = S3("Static Files")
lb >> web_servers >> db
web_servers >> storage
This code generates a clean architectural diagram showing a typical web application setup. The show=False
parameter prevents the diagram from opening automatically, and direction="TB"
sets a top-to-bottom layout.
Advanced Diagrams with Custom Styling
from diagrams import Diagram, Cluster, Edge
from diagrams.aws.compute import ECS, Lambda
from diagrams.aws.database import Dynamodb
from diagrams.aws.integration import SQS
from diagrams.aws.network import APIGateway
graph_attr = {
"fontsize": "16",
"bgcolor": "white"
}
with Diagram("Microservices Architecture",
show=False,
graph_attr=graph_attr,
filename="microservices"):
api = APIGateway("API Gateway")
with Cluster("Processing"):
processors = [
ECS("User Service"),
ECS("Order Service"),
Lambda("Payment Function")
]
queue = SQS("Message Queue")
db = Dynamodb("NoSQL Database")
api >> processors
processors >> Edge(color="red", style="dashed") >> queue
processors >> db
Working with Different Diagram Types
Network Topology Diagrams
from diagrams import Diagram, Cluster
from diagrams.generic.network import Router, Switch, Firewall
from diagrams.generic.compute import Rack
with Diagram("Network Topology", show=False):
internet = Router("Internet")
with Cluster("DMZ"):
firewall = Firewall("Firewall")
with Cluster("Internal Network"):
switch = Switch("Core Switch")
servers = [Rack("Server Rack 1"), Rack("Server Rack 2")]
internet >> firewall >> switch >> servers
Database Relationship Diagrams
from diagrams import Diagram, Edge
from diagrams.generic.database import SQL
with Diagram("Database Schema", show=False):
users = SQL("Users")
orders = SQL("Orders")
products = SQL("Products")
order_items = SQL("Order Items")
users >> Edge(label="1:N") >> orders
orders >> Edge(label="1:N") >> order_items
products >> Edge(label="1:N") >> order_items
Using PlantUML with Python
For more complex UML diagrams, PlantUML offers extensive capabilities. You can integrate it with Python using the plantuml
package:
pip install plantuml
import plantuml
# Define PlantUML diagram syntax
uml_code = """
@startuml
class User {
-id: int
-name: string
-email: string
+login()
+logout()
}
class Order {
-id: int
-date: datetime
-total: decimal
+calculate_total()
}
User ||--o{ Order
@enduml
"""
# Generate diagram
server = plantuml.PlantUML(url='http://www.plantuml.com/plantuml/img/')
image_data = server.processes(uml_code)
with open('class_diagram.png', 'wb') as f:
f.write(image_data)
Real-World Use Cases and Examples
CI/CD Pipeline Documentation
Automatically generate pipeline diagrams that stay synchronized with your actual infrastructure:
from diagrams import Diagram, Cluster, Edge
from diagrams.generic.compute import Rack
from diagrams.programming.framework import React
from diagrams.onprem.vcs import Git
with Diagram("CI/CD Pipeline", show=False):
developer = React("Developer")
repo = Git("Repository")
with Cluster("CI/CD"):
build = Rack("Build Server")
test = Rack("Test Server")
deploy = Rack("Deploy Server")
with Cluster("Environments"):
staging = Rack("Staging")
prod = Rack("Production")
developer >> repo >> build >> test >> deploy
deploy >> staging >> Edge(label="manual approval") >> prod
Monitoring and Alerting Architecture
from diagrams import Diagram, Cluster
from diagrams.onprem.monitoring import Prometheus, Grafana
from diagrams.onprem.logging import Elasticsearch, Kibana
from diagrams.generic.compute import Rack
with Diagram("Monitoring Stack", show=False):
with Cluster("Applications"):
apps = [Rack(f"App {i}") for i in range(1, 4)]
with Cluster("Monitoring"):
prometheus = Prometheus("Prometheus")
grafana = Grafana("Grafana")
with Cluster("Logging"):
elastic = Elasticsearch("Elasticsearch")
kibana = Kibana("Kibana")
apps >> prometheus >> grafana
apps >> elastic >> kibana
Best Practices and Common Pitfalls
Best Practices
- Version control your diagrams: Store diagram code alongside your infrastructure code
- Use meaningful names: Avoid generic labels like “server1” – use descriptive names like “user-auth-service”
- Organize with clusters: Group related components to improve readability
- Consistent styling: Define graph attributes once and reuse them across projects
- Automate generation: Include diagram generation in your CI/CD pipeline
Common Issues and Solutions
Problem: Graphviz not found error
# Solution: Ensure Graphviz is in your PATH
export PATH="/usr/local/bin:$PATH" # macOS
# Or on Windows, add C:\Program Files\Graphviz\bin to system PATH
Problem: Overlapping nodes in complex diagrams
# Solution: Use graph attributes to control spacing
graph_attr = {
"splines": "ortho",
"nodesep": "1.0",
"ranksep": "1.0"
}
with Diagram("My Diagram", graph_attr=graph_attr):
# Your diagram code
Problem: Icons not displaying correctly
# Solution: Verify icon paths and use fallbacks
from diagrams.generic.blank import Blank
# Use generic icons as fallbacks
try:
from diagrams.custom.myservice import MyService
service = MyService("Custom Service")
except ImportError:
service = Blank("Custom Service")
Performance Considerations and Optimization
Large diagrams can take significant time to render. Here are optimization strategies:
Diagram Size | Render Time | Optimization Strategy |
---|---|---|
< 20 nodes | < 2 seconds | No optimization needed |
20-50 nodes | 2-10 seconds | Use clustering, simplify layouts |
50+ nodes | 10+ seconds | Split into multiple diagrams, use subgraphs |
# Optimization example: Using subgraphs for large architectures
from diagrams import Diagram, Cluster
def create_service_cluster(name, count):
"""Helper function to create service clusters"""
return [EC2(f"{name}-{i}") for i in range(count)]
with Diagram("Optimized Large Architecture", show=False):
with Cluster("Frontend"):
frontend = create_service_cluster("web", 3)
with Cluster("Backend"):
backend = create_service_cluster("api", 5)
# Connect clusters instead of individual nodes
frontend >> backend
Integration with Documentation Systems
Integrate diagram generation with popular documentation tools:
# Makefile for automated documentation
generate-docs:
python generate_diagrams.py
mkdocs build
# Or with Sphinx
sphinx-build:
python generate_diagrams.py
sphinx-build -b html docs docs/_build
For GitHub Actions integration:
# .github/workflows/docs.yml
name: Generate Documentation
on: [push]
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
sudo apt-get install graphviz
pip install diagrams
- name: Generate diagrams
run: python scripts/generate_diagrams.py
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
The diagram-as-code approach transforms technical documentation from a burden into an integrated part of your development workflow. By treating diagrams as code, you ensure they remain accurate, up-to-date, and maintainable throughout your project’s lifecycle. Start with simple architectures using the Diagrams library, then expand to more complex visualizations as your needs grow.
For comprehensive documentation and examples, check out the official Diagrams documentation and the PlantUML official site.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.