Python - Generate flows using pydot
Use Python and the pydot library to easily generate diagrams-as-code like ETL flowcharts, class diagrams, and decision trees. Includes Docker setup and code examples.
This article is over a year old but was reviewed on July 30, 2026 — the content is still accurate.
This article is part of the Diagrams as code series:
- Docker - Diagrams as code
- Build a mind map using Docker and Markdown
- Rendering a JSON file as a mind map
- How to generate a graph from compose.yaml
- Drawdb-app - Render your database model as png, markdown, mermaid, ...
- Python - Generate flows using pydot
- Getting a more attractive mindmap with Markmap and Quarto
- Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid

This article introduces Pydot, a Python diagram-as-code library that renders diagrams (ETL flows, class diagrams, data flows, decision trees) from Python code into images. Using a small Docker image, a .py script describing the diagram is run with docker run ... python etl.py to produce a PNG, with further examples sourced from the Graphviz documentation.
Pydot is a diagram-as-code generator, i.e. you write lines of code and, thanks to a magic process, you can render the code as an image.
This blog has two other articles in the same family: Docker - Diagrams as code, using the diagrams library and its cloud-provider icons, and Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid, where the diagram is generated from your compose.yaml instead of hand-written.
For guys just like me who are terrible at visuals, this is gold.
Let's say you have to describe an IT process such as ETL (data is loaded, one or other transformation rule is applied and the result is loaded into a database, for example); you can easily imagine drawing some rectangles using a tool like https://app.diagrams.net/ (formerly known as draw.io), or use a smarter way to do it.
Let's see how in this article.
Pydot can generate an image like the one below:

First, we'll need a Python Docker container. Please create a file called Dockerfile with this content:
FROM python:3.13-slim
# Prevents Python from writing pyc files.ENV PYTHONDONTWRITEBYTECODE=1
# hadolint ignore=DL3008RUN set -e -x \ && apt-get update -yqq \ && apt-get install -y --no-install-recommends graphviz \ && apt-get clean \ && rm -rf /tmp/* /var/lib/apt/lists/*
# hadolint ignore=DL3013RUN pip install --no-cache-dir pydotThis Dockerfile sets up a lightweight Python 3.13 environment optimized for container use. It disables Python's bytecode generation to reduce disk space, installs the graphviz system package needed for graph visualization, and adds the pydot Python library to interact with those graphs.
And create the image by running docker build --tag pydot ..
We don't need more except ... our flow.
To build an ETL image like the one above, please create a new file called f.i. etl.py with this content:
graph = pydot.Dot(graph_type='digraph', rankdir='TB')
node_csv = pydot.Node('CSV', shape='box', style='filled', fillcolor='lightblue')node_json = pydot.Node('JSON', shape='box', style='filled', fillcolor='lightblue')
node_extract = pydot.Node('Extract', shape='box')
node_clean = pydot.Node('Clean', shape='box')node_transform = pydot.Node('Transform', shape='box')
node_load = pydot.Node('Load', shape='box', style='filled', fillcolor='lightgreen')
graph.add_edge(pydot.Edge(node_csv, node_extract))graph.add_edge(pydot.Edge(node_json, node_extract))graph.add_edge(pydot.Edge(node_extract, node_clean))graph.add_edge(pydot.Edge(node_clean, node_transform))graph.add_edge(pydot.Edge(node_transform, node_load))
node_enrich = pydot.Node('Get more data', shape='box')graph.add_edge(pydot.Edge(node_transform, node_enrich))graph.add_edge(pydot.Edge(node_enrich, node_load))
graph.write_png('etl.png')This code builds a visual diagram of an ETL (Extract-Transform-Load) data pipeline using the pydot library. It creates nodes representing different stages of data processing—starting with CSV and JSON sources, flowing through extraction, cleaning, and transformation steps, with an optional enrichment branch, and ending at a load stage. Finally, it saves this diagram as a PNG image file.
And, now, the easy part, render the Python etl.py file as an image by running docker run --rm -it -v "${PWD}":/diagram -w /diagram pydot python etl.py.
And bingo, you've now an image called etl.png in your directory.
Some other examples
Search on the Internet for some examples and you'll find f.i. this site: https://graphviz.readthedocs.io/en/stable/examples.html.
You'll find there Python examples and, too, the rendered image.
OOP classes
graph = pydot.Dot(graph_type='digraph')
# Add nodesnode_person = pydot.Node('Person', shape='box')node_student = pydot.Node('Student', shape='box')node_teacher = pydot.Node('Teacher', shape='box')
# Add nodes to the graphgraph.add_node(node_person)graph.add_node(node_student)graph.add_node(node_teacher)
# Add edges (inheritance)graph.add_edge(pydot.Edge(node_person, node_student))graph.add_edge(pydot.Edge(node_person, node_teacher))
# Render the graphgraph.write_png('class_diagram.png')This code creates a visual diagram showing a class hierarchy using pydot, which is a Python wrapper around Graphviz. It defines three nodes (Person, Student, and Teacher), adds them to a directed graph, draws arrows showing that Student and Teacher both inherit from Person, and finally saves the diagram as a PNG image file.
Data flow
graph = pydot.Dot(graph_type='digraph')
# Data sourcessource1 = pydot.Node('Source 1', shape='box')source2 = pydot.Node('Source 2', shape='box')graph.add_node(source1)graph.add_node(source2)
# Process nodesprocess1 = pydot.Node('Process 1', shape='oval')process2 = pydot.Node('Process 2', shape='oval')graph.add_node(process1)graph.add_node(process2)
# Data storedata_store = pydot.Node('Data Store', shape='cylinder')graph.add_node(data_store)
# Edges representing data flowgraph.add_edge(pydot.Edge(source1, process1))graph.add_edge(pydot.Edge(source2, process1))graph.add_edge(pydot.Edge(process1, process2))graph.add_edge(pydot.Edge(process1, data_store))graph.add_edge(pydot.Edge(data_store, process2))
# Render the graphgraph.write_png('data_flow_diagram.png')This code creates a data flow diagram using pydot, which is a Python library for working with Graphviz. It defines five nodes representing data sources, processes, and a data store, connects them with directional edges to show how data moves between them, and then saves the resulting diagram as a PNG image file.
Decision flow
graph = pydot.Dot(graph_type='digraph')
# Add nodesnode_start = pydot.Node('Start', shape='oval')node_process1 = pydot.Node('Process 1', shape='box')node_decision = pydot.Node('Decision', shape='diamond')node_process2 = pydot.Node('Process 2', shape='box')node_end = pydot.Node('End', shape='oval')
# Add edgesgraph.add_edge(pydot.Edge(node_start, node_process1))graph.add_edge(pydot.Edge(node_process1, node_decision))graph.add_edge(pydot.Edge(node_decision, node_process2, label='Yes'))graph.add_edge(pydot.Edge(node_decision, node_end, label='No'))graph.add_edge(pydot.Edge(node_process2, node_end))
# Write the graph to a filegraph.write_png('flowchart.png')This code creates a flowchart diagram using pydot, a Python library for working with Graphviz. It defines five nodes (Start, Process 1, Decision, Process 2, and End) with different shapes, connects them with edges to form a flow, and renders the final diagram as a PNG image file.
Decision tree
graph = pydot.Dot(graph_type='digraph')
# Add nodesnode_root = pydot.Node('Root', shape='box')node_left = pydot.Node('Left Child', shape='box')node_right = pydot.Node('Right Child', shape='box')node_left_left = pydot.Node('Left-Left Grandchild', shape='ellipse')node_left_right = pydot.Node('Left-Right Grandchild', shape='ellipse')node_right_left = pydot.Node('Right-Left Grandchild', shape='ellipse')node_right_right = pydot.Node('Right-Right Grandchild', shape='ellipse')
# Add nodes to the graphgraph.add_node(node_root)graph.add_node(node_left)graph.add_node(node_right)graph.add_node(node_left_left)graph.add_node(node_left_right)graph.add_node(node_right_left)graph.add_node(node_right_right)
# Add edgesgraph.add_edge(pydot.Edge(node_root, node_left, label='Feature 1 < 5'))graph.add_edge(pydot.Edge(node_root, node_right, label='Feature 1 >= 5'))graph.add_edge(pydot.Edge(node_left, node_left_left, label='Feature 2 < 3'))graph.add_edge(pydot.Edge(node_left, node_left_right, label='Feature 2 >= 3'))graph.add_edge(pydot.Edge(node_right, node_right_left, label='Feature 3 < 10'))graph.add_edge(pydot.Edge(node_right, node_right_right, label='Feature 3 >= 10'))
# Render the graphgraph.write_png('decision_tree.png')This code builds a visual representation of a decision tree using pydot, a Python interface to Graphviz. It creates a tree structure with a root node that splits into left and right children based on feature thresholds, and then those children split further into grandchildren. Finally, it renders the entire tree as a PNG image file.

This article is part of the Diagrams as code series:
- Docker - Diagrams as code
- Build a mind map using Docker and Markdown
- Rendering a JSON file as a mind map
- How to generate a graph from compose.yaml
- Drawdb-app - Render your database model as png, markdown, mermaid, ...
- Python - Generate flows using pydot
- Getting a more attractive mindmap with Markmap and Quarto
- Documentation as Code - Transform Your Infrastructure into Beautiful Diagrams with Python and Mermaid





