PlantUML + CI: 4 ways to keep diagrams from going stale

puml.online

“Our architecture diagram is out of date” is a universal team pain. Here’s how to use CI to bind diagrams to the codebase — auto-check, auto-rerender, staleness alerts, PR thumbnails.

The anti-pattern: draw a diagram and forget

1
2
3
docs/architecture/
flow.png # drawn in 2023, no one maintains it
README.md

Six months later, services have been deprecated and new ones added. flow.png still shows 2023. Architecture diagrams live in a vacuum.

The fix: CI ties the diagram to the repo.

Trick 1: CI auto-render

.github/workflows/uml.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
name: Render PlantUML
on:
push:
paths:
- 'docs/**/*.puml'
- 'docs/**/*.uml'
pull_request:
paths:
- 'docs/**/*.puml'
- 'docs/**/*.uml'

jobs:
render:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: temurin
- name: Download PlantUML
run: curl -L -o plantuml.jar https://github.com/plantuml/plantuml/releases/download/v1.2024.7/plantuml-1.2024.7.jar
- name: Render .puml to .svg
run: |
find docs -name '*.puml' -o -name '*.uml' | while read f; do
java -jar plantuml.jar -tsvg -failfast2 -nometadata "$f"
done
- name: Check for changes
run: |
git diff --quiet docs/ || (
echo "::warning::SVG files differ. Re-render locally and commit."
git diff --stat docs/
exit 1
)

Whenever .puml changes, CI re-renders SVG. If SVG differs (meaning the dev forgot to render locally), CI raises a warning.

More friendly variant: auto-commit back:

1
2
3
4
5
6
7
8
9
- name: Auto-commit regenerated SVG
run: |
if [[ -n "$(git status --porcelain docs/)" ]]; then
git config user.name github-actions
git config user.email github-actions@github.com
git add docs/**/*.svg
git commit -m "render: regenerate SVG [skip ci]"
git push
fi

Trick 2: PR thumbnails

.github/workflows/uml-pr-comment.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
name: PR PlantUML comment
on:
pull_request:
paths:
- 'docs/**/*.puml'

jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: temurin
- run: |
curl -L -o plantuml.jar https://github.com/plantuml/plantuml/releases/latest/download/plantuml.jar
mkdir -p rendered
find docs -name '*.puml' | while read f; do
base=$(basename "$f" .puml)
java -jar plantuml.jar -tsvg -pipe < "$f" > "rendered/${base}.svg"
done
- name: Comment on PR
uses: marocchino/sticky-pull-request-comment@v2
with:
header: plantuml-preview
message: |
📐 PlantUML diagrams in this PR:

PRs show SVG thumbnails of every .puml touched. Reviewers compare instantly.

Trick 3: Staleness check (kill the rot)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
name: Check diagram staleness
on:
schedule:
- cron: '0 9 * * 1' # every Monday 9am

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Find diagrams older than 6 months
run: |
stale=$(find docs -name '*.puml' -mtime +180)
if [[ -n "$stale" ]]; then
echo "::warning::Stale diagrams (>180 days):"
echo "$stale"
exit 1
fi
- name: Open issue if stale
if: failure()
uses: actions/github-script@v7
with:
script: |
const files = `$(find docs -name '*.puml' -mtime +180)`.trim();
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '[stale] architecture diagrams older than 180 days',
body: `Diagrams that need a review:\n\n\`\`\`\n${files}\n\`\`\``
});

Every Monday morning, any .puml untouched for 180 days auto-opens a stale review issue.

Trick 4: Force README to reference the diagrams

Unwanted drift:

1
2
3
# Project

See our architecture diagrams in Confluence.

CI lint:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
name: Lint README
on:
pull_request:
paths:
- 'README.md'

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check README references repo diagrams
run: |
if ! grep -q 'docs/architecture' README.md; then
echo "::error::README.md must link to docs/architecture/"
exit 1
fi

Force README to link to in-repo diagrams; ban the “diagrams live on external wiki” pattern.

Full workflow

Combine the four:

  1. On push to main: CI auto-rerenders all SVG (stale = failed PR).
  2. On PR: auto-comment with thumbnail SVGs (reviewers see the diff).
  3. Weekly cron: scan untouched diagrams, auto-open issues.
  4. On README change: lint that the doc references in-repo diagrams.
Trigger CI job Effect
push to main render + auto-commit SVG stays current
pull_request render + PR comment Reviewers see diagrams
Weekly Monday 9am staleness check Maintenance nudge
README.md change lint Prevents external drift

Real-world pitfalls

  • CI gets stuck downloading plantuml.jar: vendor a copy in the repo, or fall back to a cached GitHub Actions cache.
  • Java cold start is slow: prefer temurin over default-jdk; install is ~30s.
  • -failfast2 halts on first error: a single broken file can block the whole build. Use || true to keep going, but emit a :warning: so it’s visible.
  • CJK fonts are tofu on default Ubuntu: install fonts-noto-cjk:
    1
    2
    - name: Install CJK fonts
    run: sudo apt-get update && sudo apt-get install -y fonts-noto-cjk
  • PR auto-committing SVG retriggers CI: append [skip ci] to the auto-commit message, or scope the workflow’s paths: filter to .puml only:
    1
    2
    3
    4
    on:
    push:
    paths:
    - 'docs/**/*.puml'

Drop-in template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# .github/workflows/uml-ci.yml
name: PlantUML CI
on:
push:
paths: ['docs/**/*.puml', 'docs/**/*.uml']
branches: [main]
pull_request:
paths: ['docs/**/*.puml', 'docs/**/*.uml']

jobs:
render:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { java-version: '21', distribution: temurin }
- run: sudo apt-get install -y fonts-noto-cjk
- run: curl -L -o plantuml.jar https://github.com/plantuml/plantuml/releases/latest/download/plantuml.jar
- run: |
find docs -name '*.puml' -o -name '*.uml' | while read f; do
java -jar plantuml.jar -tsvg -failfast2 -nometadata "$f"
done
- name: Check or auto-commit
run: |
if [[ -n "$(git status --porcelain docs/)" ]]; then
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
git add docs/
git commit -m "render: regenerate SVG [skip ci]" || echo "no changes"
git push
fi

Paste, push, done.

  • Title: PlantUML + CI: 4 ways to keep diagrams from going stale
  • Author: puml.online
  • Created at : 2026-07-29 14:45:00
  • Updated at : 2026-08-14 21:34:29
  • Link: https://puml.online/blog/plantuml-ci-stale-diagrams-en/
  • License: This work is licensed under CC BY-NC-SA 4.0.