Keep architecture diagrams synchronized with source code.
diagram-sync is a CLI tool that automatically generates images from diagram source files. Drop it into any repo and run npx diagram-sync — it finds every supported diagram file, renders it to an image, and mirrors the output under a diagrams/ folder. No config required.
npx diagram-syncEngineering teams update code but forget to re-export architecture diagrams. The result is stale documentation — READMEs, wikis, and onboarding docs that no longer reflect reality.
diagram-sync eliminates the manual export step entirely. You edit the diagram source file. It generates the image automatically — locally or in CI/CD.
Honest note: This tool removes export friction. It does not force developers to keep source diagrams accurate. That is a people problem, not a tooling problem.
# one-off, no install required
npx diagram-sync
# or install globally
npm install -g diagram-sync
# or as a dev dependency
npm install --save-dev diagram-sync- Add a supported diagram source file anywhere in your repo
- Run
npx diagram-syncfrom the project root - Find the generated image in
diagrams/mirroring the source path
| Source | Generated |
|---|---|
docs/ |
diagrams/ |
Once committed, the generated images are plain files in your repository — embed them in READMEs, wikis, internal docs, or link to them from anywhere on the internet via their raw GitHub URL.
- Recursively scans your repo for diagram source files
- Skips
node_modules,.git,dist,build,diagrams - Derives the output path from the source file location — no input/output directories to configure
- Generates images using the installed diagram tool for each provider — defaults to SVG, configurable via
--formatflag or config file
diagram-sync supports any diagram source that is committed to Git, maintained as a file, and can be converted to an image via a CLI — regardless of whether it was created by hand, a GUI tool, or AI.
| Provider | Extensions | Guide |
|---|---|---|
| PlantUML | .puml, .plantuml |
Setup & CI/CD |
| Mermaid | .mmd, .mermaid |
Setup & CI/CD |
| Graphviz | .dot, .gv |
Setup & CI/CD |
| Draw.io | .drawio, .dio |
Setup & CI/CD |
| D2 | .d2 |
Setup & CI/CD |
| Excalidraw | .excalidraw |
Setup & CI/CD |
| BPMN | .bpmn |
Setup & CI/CD |
npx diagram-sync"scripts": {
"diagrams": "npx diagram-sync"
}npm run diagramsPass explicit paths — skips discovery entirely. Used by CI to regenerate only the files changed in a push or PR.
npx diagram-sync --files docs/providers/d2/network.d2 docs/providers/plantuml/system.pumlProcesses only files modified since the last commit (git diff HEAD + untracked). Useful locally to avoid regenerating every diagram in a large repo.
npx diagram-sync --changedConfig is optional — no config file needed to get started. Add diagram-sync.config.json to your project root to control formats per provider or scope which providers run.
{
"format": "svg",
"jobs": [
{
"name": "architecture",
"type": "plantuml",
"format": "png",
"background": "#FFFFFF"
},
{
"name": "flows",
"type": "mermaid"
}
]
}npx diagram-sync --config diagram-sync.config.jsonOverride format at runtime without a config file:
npx diagram-sync --format svgFormat resolution order: --format flag → job format → global format → default svg.
| Field | Type | Description |
|---|---|---|
name |
string |
Label for the job (used in logs) |
type |
string |
Diagram provider (plantuml, mermaid, graphviz, drawio, d2, excalidraw, bpmn) |
format |
string |
Output format for this job (e.g. png, svg, pdf) — overrides global format |
background |
string |
Background color for PlantUML diagrams — defaults to #FFFFFF. Accepts hex codes or named colors (e.g. transparent, #F5F5F5) |
Generates a preview on every PR and commits images to main on merge.
Remove the install steps and path filters for providers you don't use — most teams only need 1–2.
name: Generate and Commit Diagrams
# Remove path entries for providers you don't use.
# The workflow only triggers when a matching source file changes.
on:
pull_request:
paths:
# PlantUML — remove if not using .puml / .plantuml files
- '**/*.puml'
- '**/*.plantuml'
# Mermaid — remove if not using .mmd / .mermaid files
- '**/*.mmd'
- '**/*.mermaid'
# Graphviz — remove if not using .dot / .gv files
- '**/*.dot'
- '**/*.gv'
# Draw.io — remove if not using .drawio / .dio files
- '**/*.drawio'
- '**/*.dio'
# D2 — remove if not using .d2 files
- '**/*.d2'
# Excalidraw — remove if not using .excalidraw files
- '**/*.excalidraw'
# BPMN — remove if not using .bpmn files
- '**/*.bpmn'
push:
branches: [main]
paths:
# PlantUML — remove if not using .puml / .plantuml files
- '**/*.puml'
- '**/*.plantuml'
# Mermaid — remove if not using .mmd / .mermaid files
- '**/*.mmd'
- '**/*.mermaid'
# Graphviz — remove if not using .dot / .gv files
- '**/*.dot'
- '**/*.gv'
# Draw.io — remove if not using .drawio / .dio files
- '**/*.drawio'
- '**/*.dio'
# D2 — remove if not using .d2 files
- '**/*.d2'
# Excalidraw — remove if not using .excalidraw files
- '**/*.excalidraw'
# BPMN — remove if not using .bpmn files
- '**/*.bpmn'
workflow_dispatch:
jobs:
preview:
name: Generate Preview
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v5
with:
# Full history required so git diff can resolve base refs for --files filtering.
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v5
with:
node-version: '20'
# --- PlantUML — remove if not using .puml / .plantuml files ---
- name: Install PlantUML
run: |
sudo apt-get update -q
sudo apt-get install -y --no-install-recommends default-jre-headless plantuml
# --- Mermaid — remove if not using .mmd / .mermaid files ---
- name: Install Mermaid CLI
run: npm install -g @mermaid-js/mermaid-cli
# --- Graphviz — remove if not using .dot / .gv files ---
- name: Install Graphviz
run: |
sudo apt-get update -q
sudo apt-get install -y graphviz
# --- Draw.io — remove if not using .drawio / .dio files ---
- name: Install Draw.io
run: |
DRAWIO_VERSION=$(curl -s https://api.github.com/repos/jgraph/drawio-desktop/releases/latest | jq -r '.tag_name | ltrimstr("v")')
wget -q "https://github.com/jgraph/drawio-desktop/releases/download/v${DRAWIO_VERSION}/drawio-amd64-${DRAWIO_VERSION}.deb" -O drawio.deb
sudo apt-get install -y ./drawio.deb xvfb
# --- D2 — remove if not using .d2 files ---
- name: Install D2
run: curl -fsSL https://d2lang.com/install.sh | sh
# --- Excalidraw — remove if not using .excalidraw files ---
- name: Install Excalidraw CLI
run: npm install -g @swiftlysingh/excalidraw-cli
# --- BPMN — remove if not using .bpmn files ---
- name: Install BPMN CLI
run: |
npm install -g bpmn-to-image
sed -i "s/headless: 'new'/headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox']/" $(npm root -g)/bpmn-to-image/index.js
- name: Install diagram-sync
run: npm install -g diagram-sync
- name: Generate changed diagrams
run: |
CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD | grep -E '\.(puml|plantuml|mmd|mermaid|dot|gv|drawio|dio|d2|excalidraw|bpmn)$' || true)
if [ -n "$CHANGED" ]; then
diagram-sync --files $CHANGED
else
echo "No diagram files changed."
fi
# add --format png or --format pdf after --files $CHANGED to override the default svg output
- name: Upload diagram previews
uses: actions/upload-artifact@v4
with:
name: diagrams-preview
path: diagrams/
commit:
name: Generate and Commit
runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
permissions:
contents: write
steps:
- uses: actions/checkout@v5
with:
# GITHUB_TOKEN with contents: write is sufficient when the main branch is unprotected.
# If your branch is protected and you need to bypass protection rules, replace with a PAT:
# token: ${{ secrets.PAT_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}
# Full history required so git diff can resolve base refs for --files filtering.
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v5
with:
node-version: '20'
# --- PlantUML — remove if not using .puml / .plantuml files ---
- name: Install PlantUML
run: |
sudo apt-get update -q
sudo apt-get install -y --no-install-recommends default-jre-headless plantuml
# --- Mermaid — remove if not using .mmd / .mermaid files ---
- name: Install Mermaid CLI
run: npm install -g @mermaid-js/mermaid-cli
# --- Graphviz — remove if not using .dot / .gv files ---
- name: Install Graphviz
run: |
sudo apt-get update -q
sudo apt-get install -y graphviz
# --- Draw.io — remove if not using .drawio / .dio files ---
- name: Install Draw.io
run: |
DRAWIO_VERSION=$(curl -s https://api.github.com/repos/jgraph/drawio-desktop/releases/latest | jq -r '.tag_name | ltrimstr("v")')
wget -q "https://github.com/jgraph/drawio-desktop/releases/download/v${DRAWIO_VERSION}/drawio-amd64-${DRAWIO_VERSION}.deb" -O drawio.deb
sudo apt-get install -y ./drawio.deb xvfb
# --- D2 — remove if not using .d2 files ---
- name: Install D2
run: curl -fsSL https://d2lang.com/install.sh | sh
# --- Excalidraw — remove if not using .excalidraw files ---
- name: Install Excalidraw CLI
run: npm install -g @swiftlysingh/excalidraw-cli
# --- BPMN — remove if not using .bpmn files ---
- name: Install BPMN CLI
run: |
npm install -g bpmn-to-image
sed -i "s/headless: 'new'/headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox']/" $(npm root -g)/bpmn-to-image/index.js
- name: Install diagram-sync
run: npm install -g diagram-sync
- name: Generate changed diagrams
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
diagram-sync
else
CHANGED=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -E '\.(puml|plantuml|mmd|mermaid|dot|gv|drawio|dio|d2|excalidraw|bpmn)$' || true)
if [ -n "$CHANGED" ]; then
diagram-sync --files $CHANGED
else
echo "No diagram files changed."
fi
fi
# add --format png or --format pdf after --files $CHANGED to override the default svg output
- name: Commit generated diagrams
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add diagrams/
if git diff --staged --quiet; then
echo "No diagram changes to commit."
else
git commit -m "chore: auto-export diagrams [skip ci]"
git push
fiNo secrets setup required — GITHUB_TOKEN with contents: write works out of the box for unprotected branches. If your main branch is protected and you need to push through branch protection rules, replace secrets.GITHUB_TOKEN with a PAT saved as PAT_TOKEN. See the Provider Guides for the ready-to-use workflow file.
- Node.js 20+
- Each provider requires its own CLI tool — install only what you need:
- PlantUML: Java 11+ and PlantUML — see PlantUML guide
- Mermaid: see Mermaid guide
- Graphviz: see Graphviz guide
- Draw.io: requires
draw.ioandxvfb(Linux only) —diagram-syncauto-usesxvfb-runfor headless rendering when no display is available — see Draw.io guide - D2: see D2 guide
- Excalidraw: requires
@swiftlysingh/excalidraw-cli— SVG and PNG, no browser required — see Excalidraw guide - BPMN: requires
bpmn-to-image— SVG, PNG and PDF — on CI, patch with--no-sandboxafter install (see BPMN guide)
Providers are detected at runtime and missing ones are skipped with a warning.
- How to automate PlantUML diagram generation in CI/CD
- How to automate Mermaid diagram generation in CI/CD
- How to automate Graphviz diagram generation in CI/CD
- How to convert Draw.io files to images automatically in CI/CD
- How to automate D2 diagram generation in CI/CD
- How to export Excalidraw files to SVG or PNG automatically in CI/CD
- How to automate BPMN process diagram generation in CI/CD
- How to keep README architecture diagrams up to date automatically
- How to sync architecture diagrams from source files
- How to generate architecture diagrams on GitHub Actions
- How to treat architecture diagrams as code
- Documentation-as-code workflow for PlantUML, Mermaid, D2, and more
- npm: https://www.npmjs.com/package/diagram-sync
- GitHub: https://github.com/Buffden/diagram-sync
- Provider Guides: https://github.com/Buffden/diagram-sync/tree/main/docs/providers
MIT — Buffden




