# Diagrams User Guide — Diagram as Code

**Diagrams** lets you draw cloud system architecture **in Python code**. You write a short Python script that declares nodes (services like EC2, RDS, a Kubernetes Pod) and connects them with arrows, and Diagrams renders a polished architecture image. It was built for **prototyping** a new system design without reaching for a drawing tool, and because your diagram is code, you can keep it under version control and review changes the same way you review everything else.

> [!NOTE]
> Diagrams does **not** create, control, or provision any real cloud resources, and it does **not** generate CloudFormation or Terraform. It draws pictures. Think of it as a programmable whiteboard for system architecture.

This guide covers everything you need as a user: installation, the four building blocks (`Diagram`, `Node`, `Cluster`, `Edge`), the connection operators, ready-to-run recipes for AWS / GCP / Kubernetes / on-premises stacks, the full catalog of providers, and troubleshooting for the errors you are most likely to hit.

---

## 1. Quick Start

Once Diagrams and Graphviz are installed (see [Installation](#2-installation)), create a file named `diagram.py`:

```python
# diagram.py
from diagrams import Diagram
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import ELB

with Diagram("Web Service", show=False):
    ELB("lb") >> EC2("web") >> RDS("userdb")
```

Run it like any Python script:

```shell
$ python diagram.py
```

```text
$ ls
diagram.py  web_service.png
```

You now have `web_service.png` in your working directory: a load balancer pointing to a web server pointing to a database. That is the whole idea — **declare components, connect them with arrows, run the file**.

> [!TIP]
> The first argument to `Diagram(...)` ("Web Service") becomes both the image title and the output filename (lowercased, spaces replaced with underscores → `web_service.png`). Pass `show=False` while you iterate so the image doesn't pop open in your viewer on every run.

---

## 2. Installation

Installing Diagrams is a **two-step** process. Diagrams is a Python package, but the actual rendering is done by **Graphviz**, a separate native program. You must install Graphviz first.

### Requirements

| Requirement | Version | Notes |
|---|---|---|
| Python | **3.9 or higher** | Check with `python --version`. |
| Graphviz | Any recent release | The rendering engine. Must be on your `PATH`. |

> [!IMPORTANT]
> Graphviz is **not** a Python library you can `pip install` and forget — it is a system program that provides the `dot` command. If it is missing, Diagrams runs but rendering fails. Install it first.

### Step 1 — Install Graphviz

=== "macOS (Homebrew)"

    ```shell
    brew install graphviz
    ```

=== "Windows (Chocolatey)"

    ```shell
    choco install graphviz
    ```

=== "Windows (Winget)"

    ```shell
    winget install Graphviz.Graphviz -i
    ```

=== "Debian / Ubuntu"

    ```shell
    sudo apt-get install graphviz
    ```

Confirm it is on your `PATH`:

```shell
$ dot -V
```

```text
dot - graphviz version 9.0.0 (20230911.1827)
```

### Step 2 — Install Diagrams

=== "pip"

    ```shell
    pip install diagrams
    ```

=== "pipenv"

    ```shell
    pipenv install diagrams
    ```

=== "poetry"

    ```shell
    poetry add diagrams
    ```

=== "uv"

    ```shell
    uv tool install diagrams
    ```

Verify the install from Python:

```shell
$ python -c "import diagrams; print('ok')"
```

```text
ok
```

> [!WARNING]
> Avoid installing Diagrams with `pipx` if you intend to `import` provider nodes in your own scripts. Because the Diagrams CLI simply executes your file in its own environment, a `pipx`-isolated install can lead to `ModuleNotFoundError: No module named 'diagrams.cli'` or missing provider modules. Use `pip`, `pipenv`, `poetry`, or `uv` into the environment your script runs in. See [Troubleshooting](#9-troubleshooting).

---

## 3. Core Concepts

A Diagrams script is built from exactly four objects. Learn these and you can draw anything.

| Object | What it is | Used as |
|---|---|---|
| `Diagram` | The whole canvas / drawing context. | A `with` block. Everything lives inside it. |
| `Node` | A single component (a service, server, queue…). | Created by importing a provider class, e.g. `EC2("web")`. |
| `Cluster` | A labeled box that groups related nodes. | A nested `with` block. |
| `Edge` | A connection between nodes, with optional label/color/style. | Placed between nodes with the connection operators. |

Every node and cluster must be created **inside** a `with Diagram(...)` block. Diagrams uses an implicit global context, so you never pass the diagram around by hand.

> [!CAUTION]
> Creating a node or cluster outside a diagram context raises `EnvironmentError: Global diagrams context not set up`. Always nest your nodes inside `with Diagram(...)`.

### The Diagram context

```python
from diagrams import Diagram
from diagrams.aws.compute import EC2

with Diagram("Simple Diagram"):
    EC2("web")
```

Running this writes `simple_diagram.png` and (by default) opens it.

### Nodes: provider, type, name

A node has three parts: a **provider** (`aws`), a **resource type** (`compute`), and a **name** you give it (`"web"`):

```python
from diagrams.aws.compute import EC2

EC2("web")   # provider=aws, type=compute, class=EC2, label="web"
```

You import the class from `diagrams.<provider>.<category>` and call it with a label. The full provider list is in [section 7](#7-provider-catalog).

---

## 4. Connecting Nodes — The Operators

Data flow is expressed with three operators. This is the heart of the Diagrams syntax.

| Operator | Direction | Meaning |
|---|---|---|
| `>>` | Left → right | Points an arrow **forward** from the left node to the right node. |
| `<<` | Right → left | Points an arrow **backward**. |
| `-` | None | Connects two nodes with an **undirected** line (no arrowhead). |

```python
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 Services", show=False):
    ELB("lb") >> EC2("web") >> RDS("userdb") >> S3("store")
    ELB("lb") >> EC2("web") >> RDS("userdb") << EC2("stat")
    (ELB("lb") >> EC2("web")) - EC2("web") >> RDS("userdb")
```

> [!WARNING]
> When you mix `-` with `>>`/`<<` on the same line, Python's operator precedence can produce surprising results. Wrap the directed part in parentheses, as in `(ELB("lb") >> EC2("web")) - EC2("web")` above.

> [!NOTE]
> The rendered order is the **reverse** of declaration order. If layout looks flipped from what you expected, this is why.

### Grouping nodes with lists

Connecting many nodes one-by-one is noisy. Put nodes in a Python list to fan-out or fan-in to all of them at once:

```python
from diagrams import Diagram
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.aws.network import ELB

with Diagram("Grouped Workers", show=False, direction="TB"):
    ELB("lb") >> [EC2("worker1"),
                  EC2("worker2"),
                  EC2("worker3"),
                  EC2("worker4"),
                  EC2("worker5")] >> RDS("events")
```

> [!CAUTION]
> You **cannot** connect two lists directly (`[a, b] >> [c, d]`) — Python does not allow shift operations between two lists. Connect a single node to a list, or a list to a single node.

---

## 5. Clusters — Grouping Components

A `Cluster` draws a labeled, shaded box around a set of nodes. Use it for a database tier, a service group, a VPC, or anything you'd circle on a whiteboard.

```python
from diagrams import Cluster, Diagram
from diagrams.aws.compute import ECS
from diagrams.aws.database import RDS
from diagrams.aws.network import Route53

with Diagram("Simple Web Service with DB Cluster", show=False):
    dns = Route53("dns")
    web = ECS("service")

    with Cluster("DB Cluster"):
        db_primary = RDS("primary")
        db_primary - [RDS("replica1"),
                      RDS("replica2")]

    dns >> web >> db_primary
```

> [!TIP]
> `Group` is an alias for `Cluster` — `from diagrams import Group` works identically. Use whichever name reads better in your script.

### Nested clusters

Clusters nest to any depth. Each nesting level gets a slightly different background shade automatically, so the hierarchy is visible:

```python
from diagrams import Cluster, Diagram
from diagrams.aws.compute import ECS, EKS, Lambda
from diagrams.aws.database import Redshift
from diagrams.aws.integration import SQS
from diagrams.aws.storage import S3

with Diagram("Event Processing", show=False):
    source = EKS("k8s source")

    with Cluster("Event Flows"):
        with Cluster("Event Workers"):
            workers = [ECS("worker1"),
                       ECS("worker2"),
                       ECS("worker3")]

        queue = SQS("event queue")

        with Cluster("Processing"):
            handlers = [Lambda("proc1"),
                        Lambda("proc2"),
                        Lambda("proc3")]

    store = S3("events store")
    dw = Redshift("analytics")

    source >> workers >> queue >> handlers
    handlers >> store
    handlers >> dw
```

> [!NOTE]
> There is no depth limit to nesting clusters.

---

## 6. Edges — Labels, Colors, and Styles

When you need an arrow to carry meaning — a label, a color, a dashed line — use the `Edge` object between two nodes. An `Edge` supports three high-level attributes: **label**, **color**, and **style** (plus any raw Graphviz edge attribute).

```python
from diagrams import Cluster, Diagram, Edge
from diagrams.onprem.analytics import Spark
from diagrams.onprem.compute import Server
from diagrams.onprem.database import PostgreSQL
from diagrams.onprem.inmemory import Redis
from diagrams.onprem.aggregator import Fluentd
from diagrams.onprem.monitoring import Grafana, Prometheus
from diagrams.onprem.network import Nginx
from diagrams.onprem.queue import Kafka

with Diagram("Advanced Web Service (colored)", show=False):
    ingress = Nginx("ingress")

    metrics = Prometheus("metric")
    metrics << Edge(color="firebrick", style="dashed") << Grafana("monitoring")

    with Cluster("Service Cluster"):
        grpcsvc = [Server("grpc1"), Server("grpc2"), Server("grpc3")]

    with Cluster("Database HA"):
        primary = PostgreSQL("users")
        primary - Edge(color="brown", style="dotted") - PostgreSQL("replica") << Edge(label="collect") << metrics
        grpcsvc >> Edge(color="black") >> primary

    aggregator = Fluentd("logging")
    aggregator >> Edge(label="parse") >> Kafka("stream") >> Edge(color="black", style="bold") >> Spark("analytics")

    ingress >> Edge(color="darkgreen") << grpcsvc >> Edge(color="darkorange") >> aggregator
```

| `Edge` attribute | Type | Example |
|---|---|---|
| `label` | str | `Edge(label="collect")` |
| `color` | str (color name or hex) | `Edge(color="firebrick")` |
| `style` | str (`solid`, `dashed`, `dotted`, `bold`) | `Edge(style="dashed")` |
| `forward` / `reverse` | bool | arrowhead direction (usually set via `>>` / `<<`) |
| `**attrs` | any Graphviz edge attribute | `Edge(minlen="2", headport="w")` |

> [!TIP]
> For dense diagrams, reduce visual clutter with the "merged edges" trick: set `graph_attr={"concentrate": "true", "splines": "spline"}` on the `Diagram`. Note that `concentrate` only works with the `spline` curve style (not `ortho`) and the default `dot` layout engine.

---

## 7. Provider Catalog

Diagrams ships **20 cloud/infrastructure providers** plus three special-purpose modules. Each provider groups its nodes into **resource-type categories**, and you import a node from `diagrams.<provider>.<category>`.

| Provider | Import root | Categories | Use it for |
|---|---|---|---|
| AWS | `diagrams.aws` | 26 (compute, database, network, storage, security, analytics, integration, ml, …) | Amazon Web Services |
| Azure | `diagrams.azure` | 32 | Microsoft Azure |
| GCP | `diagrams.gcp` | 13 (compute, database, analytics, ml, network, storage, …) | Google Cloud Platform |
| Kubernetes | `diagrams.k8s` | 12 (compute, network, storage, rbac, controlplane, …) | Kubernetes objects |
| Alibaba Cloud | `diagrams.alibabacloud` | 10 | Alibaba Cloud |
| Oracle Cloud | `diagrams.oci` | 9 | Oracle Cloud Infrastructure |
| IBM | `diagrams.ibm` | 14 | IBM Cloud |
| Firebase | `diagrams.firebase` | 5 | Firebase services |
| DigitalOcean | `diagrams.digitalocean` | 4 | DigitalOcean |
| Elastic | `diagrams.elastic` | 8 | Elastic / ELK stack |
| OpenStack | `diagrams.openstack` | 22 | OpenStack |
| Outscale | `diagrams.outscale` | 4 | Outscale |
| On-Premises | `diagrams.onprem` | 31 (compute, database, network, monitoring, ci, queue, …) | Self-hosted / open-source stacks |
| SaaS | `diagrams.saas` | 15 (cdn, chat, identity, logging, …) | SaaS products |
| Programming | `diagrams.programming` | 4 (language, framework, flowchart, runtime) | Languages & frameworks |
| GIS | `diagrams.gis` | 17 | Geographic information systems |
| Generic | `diagrams.generic` | 9 (os, device, network, storage, …) | Vendor-neutral shapes |

Plus three special modules:

- **`diagrams.custom`** — bring your own icon (see [section 8](#8-custom-nodes)).
- **`diagrams.c4`** — C4-model software-architecture diagrams (see [section 8](#c4-model-diagrams)).
- **`diagrams.base`** — internal base nodes (you normally won't import these directly).

> [!NOTE]
> There are roughly **2,400 node classes** across all providers — far too many to list here. The authoritative, browsable index of every available node (with its icon) lives at **[diagrams.mingrammer.com](https://diagrams.mingrammer.com)** under the per-provider node pages (for example, the AWS node list). Import paths always follow `diagrams.<provider>.<category>`.

### Finding the right class name

Many long service names have short aliases. For example, in AWS:

| Full class | Alias |
|---|---|
| `SimpleStorageServiceS3` | `S3` |
| `ElasticContainerService` | `ECS` |
| `ElasticKubernetesService` | `EKS` |
| `ElasticLoadBalancing` | `ELB` |
| `KeyManagementService` | `KMS` |
| `SimpleQueueServiceSqs` | `SQS` |

Both the full name and the alias work. Common aliases exist across all major providers (e.g. `GKE` for Google's Kubernetes Engine, `PVC` for a Kubernetes PersistentVolumeClaim).

---

## 8. Recipes

Copy-paste these and adapt the node names. All use `show=False` so they save quietly.

### Clustered web service (AWS)

```python
from diagrams import Cluster, Diagram
from diagrams.aws.compute import ECS
from diagrams.aws.database import ElastiCache, RDS
from diagrams.aws.network import ELB, Route53

with Diagram("Clustered Web Services", show=False):
    dns = Route53("dns")
    lb = ELB("lb")

    with Cluster("Services"):
        svc_group = [ECS("web1"), ECS("web2"), ECS("web3")]

    with Cluster("DB Cluster"):
        db_primary = RDS("userdb")
        db_primary - [RDS("userdb ro")]

    memcached = ElastiCache("memcached")

    dns >> lb >> svc_group
    svc_group >> db_primary
    svc_group >> memcached
```

### Message-collecting pipeline (GCP)

```python
from diagrams import Cluster, Diagram
from diagrams.gcp.analytics import BigQuery, Dataflow, PubSub
from diagrams.gcp.compute import AppEngine, Functions
from diagrams.gcp.database import BigTable
from diagrams.gcp.iot import IotCore
from diagrams.gcp.storage import GCS

with Diagram("Message Collecting", show=False):
    pubsub = PubSub("pubsub")

    with Cluster("Source of Data"):
        [IotCore("core1"), IotCore("core2"), IotCore("core3")] >> pubsub

    with Cluster("Targets"):
        with Cluster("Data Flow"):
            flow = Dataflow("data flow")
        with Cluster("Data Lake"):
            flow >> [BigQuery("bq"), GCS("storage")]
        with Cluster("Event Driven"):
            with Cluster("Processing"):
                flow >> AppEngine("engine") >> BigTable("bigtable")
            with Cluster("Serverless"):
                flow >> Functions("func") >> AppEngine("appengine")

    pubsub >> flow
```

### Exposed pod with 3 replicas (Kubernetes)

```python
from diagrams import Diagram
from diagrams.k8s.clusterconfig import HPA
from diagrams.k8s.compute import Deployment, Pod, ReplicaSet
from diagrams.k8s.network import Ingress, Service

with Diagram("Exposed Pod with 3 Replicas", show=False):
    net = Ingress("domain.com") >> Service("svc")
    net >> [Pod("pod1"),
            Pod("pod2"),
            Pod("pod3")] << ReplicaSet("rs") << Deployment("dp") << HPA("hpa")
```

### Programmatic node generation (Kubernetes, with a loop)

You can use ordinary Python — loops, comprehensions, variables — to build diagrams:

```python
from diagrams import Cluster, Diagram
from diagrams.k8s.compute import Pod, StatefulSet
from diagrams.k8s.network import Service
from diagrams.k8s.storage import PV, PVC, StorageClass

with Diagram("Stateful Architecture", show=False):
    with Cluster("Apps"):
        svc = Service("svc")
        sts = StatefulSet("sts")

        apps = []
        for _ in range(3):
            pod = Pod("pod")
            pvc = PVC("pvc")
            pod - sts - pvc
            apps.append(svc >> pod >> pvc)

    apps << PV("pv") << StorageClass("sc")
```

> [!TIP]
> Because diagrams are just Python, you can drive node creation from a config file, an inventory list, or a Terraform state export — anything you can turn into a list.

### Custom nodes (your own icon)

The `Custom` node lets you use any image as a node icon — useful for a product Diagrams doesn't ship an icon for:

```python
from urllib.request import urlretrieve

from diagrams import Cluster, Diagram
from diagrams.aws.database import Aurora
from diagrams.custom import Custom
from diagrams.k8s.compute import Pod

rabbitmq_url = "https://jpadilla.github.io/rabbitmqapp/assets/img/icon.png"
rabbitmq_icon = "rabbitmq.png"
urlretrieve(rabbitmq_url, rabbitmq_icon)

with Diagram("Broker Consumers", show=False):
    with Cluster("Consumers"):
        consumers = [Pod("worker"), Pod("worker"), Pod("worker")]

    queue = Custom("Message queue", rabbitmq_icon)

    queue >> consumers >> Aurora("Database")
```

`Custom(label, icon_path)` takes the path to a local image file. Download or place the icon next to your script first.

### C4-model diagrams

The `diagrams.c4` module draws [C4-model](https://c4model.com/) software architecture diagrams (Person / System / Container / Database) instead of vendor icons:

```python
from diagrams import Diagram
from diagrams.c4 import Person, Container, Database, System, SystemBoundary, Relationship

with Diagram("C4 Diagram", show=False):
    customer = Person("Customer", "A user of the system")

    with SystemBoundary("Internet Banking System"):
        web = Container("Web App", "Python/Flask", "Delivers the web pages")
        api = Container("API App", "Python/FastAPI", "Provides banking functionality")
        db = Database("Database", "PostgreSQL", "Stores user accounts")

    mainframe = System("Mainframe Banking System", external=True)

    customer >> Relationship("Uses") >> web
    web >> Relationship("Makes API calls to") >> api
    api >> Relationship("Reads/writes") >> db
    api >> Relationship("Uses") >> mainframe
```

C4 helpers: `Person(name, description, external=False)`, `System(name, description, external=False)`, `Container(name, technology, description)`, `Database(name, technology, description)`, `SystemBoundary(name)`, and `Relationship(label)`.

---

## 9. The `diagrams` CLI

Installing the package also installs a `diagrams` command. It runs one or more diagram files in sequence — handy in CI or a Makefile:

```shell
$ diagrams diagram1.py diagram2.py
```

| Argument | Required | Description |
|---|---|---|
| `path` (one or more) | Yes | A Python file containing diagrams code. |

> [!NOTE]
> The CLI simply executes each file you pass, exactly as `python <file>` would. It has no flags beyond the file paths. It is a convenience for batch-rendering several diagram scripts at once.

---

## 10. Diagram Options Reference

All options are keyword arguments to `Diagram(...)`.

| Option | Default | Allowed values | Purpose |
|---|---|---|---|
| `name` | `""` | any string | Title shown on the image; also the default filename. |
| `filename` | derived from `name` | any string (no extension) | Output filename without extension. |
| `direction` | `"LR"` | `TB`, `BT`, `LR`, `RL` | Data-flow direction (top-bottom, bottom-top, left-right, right-left). |
| `outformat` | `"png"` | `png`, `jpg`, `svg`, `pdf`, `dot` (or a list) | Output format(s). Pass a list to emit several at once. |
| `curvestyle` | `"spline"` | `ortho`, `curved`, `spline`, `polyline` | Edge routing style. |
| `show` | `True` | `True` / `False` | Open the image after rendering. |
| `strict` | `False` | `True` / `False` | Merge duplicate (multi-) edges. |
| `autolabel` | `False` | `True` / `False` | Prefix each node label with its class name. |
| `theme` | `"neutral"` | `neutral`, `pastel`, `blues`, `greens`, `orange` | Color theme for clusters and edges. |
| `graph_attr` | `{}` | dict | Raw Graphviz [graph attributes](https://www.graphviz.org/doc/info/attrs.html). |
| `node_attr` | `{}` | dict | Raw Graphviz node attributes. |
| `edge_attr` | `{}` | dict | Raw Graphviz edge attributes. |

### Multiple output formats at once

```python
from diagrams import Diagram
from diagrams.aws.compute import EC2

with Diagram("Multi Output", outformat=["jpg", "png", "dot"], show=False):
    EC2("web")
```

### Choosing a color theme

```python
from diagrams import Cluster, Diagram
from diagrams.aws.compute import EC2

with Diagram("Themed", theme="blues", show=False):
    with Cluster("Tier 1"):
        EC2("web")
```

> [!NOTE]
> The `theme` and `autolabel` options are recent additions to the library. If you installed an older release from PyPI and these keywords raise `TypeError`, upgrade with `pip install -U diagrams`.

### Rendering inside Jupyter

Bind the diagram to a variable and reference it as the last cell expression to render inline:

```python
from diagrams import Diagram
from diagrams.aws.compute import EC2

with Diagram("Simple Diagram") as diag:
    EC2("web")
diag
```

---

## 11. Troubleshooting

### Error: `Global diagrams context not set up`

```text
EnvironmentError: Global diagrams context not set up
```

**Cause:** You created a `Node` (e.g. `EC2("web")`) or a `Cluster` outside of a `with Diagram(...)` block.
**Solution:** Move all node and cluster creation inside the diagram context.
**Prevention:** Treat `with Diagram(...)` as mandatory — nothing diagrams-related happens outside it.

### Error: `"<value>" is not a valid theme`

```text
ValueError: "dark" is not a valid theme. Choose from: neutral, pastel, blues, greens, orange
```

**Cause:** An unsupported value passed to `theme=`.
**Solution:** Use one of `neutral`, `pastel`, `blues`, `greens`, `orange`.

### Error: `"<value>" is not a valid direction` / `curvestyle` / `output format`

**Cause:** An out-of-range value for `direction`, `curvestyle`, or `outformat`.
**Solution:** Use an allowed value from the [options table](#10-diagram-options-reference): direction ∈ {TB, BT, LR, RL}; curvestyle ∈ {ortho, curved, spline, polyline}; outformat ∈ {png, jpg, svg, pdf, dot}.

### Error: `ModuleNotFoundError: No module named 'diagrams.cli'`

**Cause:** Diagrams was installed in an isolated environment (commonly via `pipx`) that doesn't match the environment your script imports from.
**Solution:** Reinstall with `pip install diagrams` (or `pipenv`/`poetry`/`uv`) into the same environment you run your script in.
**Prevention:** Don't use `pipx` for a library you intend to `import`.

### Error: graphviz `ExecutableNotFound` / nothing renders

```text
graphviz.backend.ExecutableNotFound: failed to execute 'dot'
```

**Cause:** Graphviz (the native program) isn't installed or isn't on your `PATH`.
**Solution:** Install Graphviz (see [Step 1](#step-1--install-graphviz)) and confirm `dot -V` works in the same shell.

### Edges look broken or float off the nodes on dense diagrams

**Cause:** With many nodes, the `ortho`/`spline` routing in the underlying Graphviz engine can produce broken or detached edges (a known Graphviz limitation).
**Solution:** Try a different `curvestyle` (e.g. `"curved"` or `"polyline"`), reduce edge density using clusters and the "merged edges" trick (`graph_attr={"concentrate": "true", "splines": "spline"}`), or split a large diagram into several smaller ones.

### Icons / logos don't appear

**Cause:** Certain releases shipped with broken icon paths.
**Solution:** Upgrade to the latest version (`pip install -U diagrams`). Verify icons appear on a tiny one-node diagram before debugging a large one.

### Mixing `-` and `>>` gives unexpected connections

**Cause:** Python operator precedence resolves arithmetic (`-`) and shift (`>>`/`<<`) operators in an order you didn't intend.
**Solution:** Add parentheses around the directed segment: `(a >> b) - c`.

---

## 12. FAQ

**Q: Does Diagrams provision real cloud resources?**
No. It only draws pictures. It does not create infrastructure and does not generate CloudFormation or Terraform.

**Q: What image formats can I export?**
`png` (default), `jpg`, `svg`, `pdf`, and `dot`. Pass a list to `outformat` to produce several at once.

**Q: Why didn't an image open after I ran my script?**
Either you passed `show=False`, or your OS image viewer isn't configured. The file is still written to disk — check your working directory for `<name>.png`.

**Q: How do I control where the file is saved and what it's named?**
Use `filename="path/to/name"` (no extension). The extension is determined by `outformat`.

**Q: Can I use an icon for a service Diagrams doesn't include?**
Yes — use `from diagrams.custom import Custom` and pass a path to your own image: `Custom("My Service", "myicon.png")`.

**Q: How do I find the exact class name for a service?**
Browse the per-provider node index at [diagrams.mingrammer.com](https://diagrams.mingrammer.com). Import path is always `diagrams.<provider>.<category>`.

**Q: Can I generate diagrams in CI without a display?**
Yes. Set `show=False` so no viewer is launched, and run `python diagram.py` (or `diagrams diagram.py`) as a build step. You only need Graphviz installed on the runner.

**Q: Can I group nodes into a labeled box?**
Yes — that's a `Cluster` (aliased as `Group`). Clusters can be nested to any depth.

**Q: Which Python version do I need?**
Python 3.9 or higher.

**Q: How do I change the layout direction?**
Pass `direction="TB"` (top-to-bottom), `"BT"`, `"LR"` (default), or `"RL"` to `Diagram(...)`.

**Q: Can I keep my diagrams in version control?**
That's the whole point of "Diagram as Code." Commit your `.py` files; regenerate images on demand or in CI.

---

_Generated June 2026 from mingrammer/diagrams at 23f06b4. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
