PlantUML self-hosting: from plantuml.jar to Docker to enterprise K8s

puml.online

When plantuml.com’s online service isn’t reliable — flaky network, security policies — self-host the renderer. Three patterns: local jar, Docker, full microservice.

Three deployment patterns

Pattern Use Startup
plantuml.jar local Ad-hoc, CI scratch <2s
Docker image Shared, long-running CI <5s
plantuml-server microservice Editor integration / API <10s

Option 1: plantuml.jar local

Simplest; one jar file, one Java command.

1
2
3
4
5
6
7
8
9
10
11
# Download (~19 MB)
curl -L -o plantuml.jar https://github.com/plantuml/plantuml/releases/download/v1.2026.7/plantuml-1.2026.7.jar

# Single file
java -jar plantuml.jar diagram.puml

# SVG output
java -jar plantuml.jar -tsvg diagram.puml

# Batch
java -jar plantuml.jar -tsvg -failfast2 -nometadata docs/**/*.puml

Pros

  • Zero config, zero deps (besides Java)
  • Drop into Docker image
  • CI invokes directly

Cons

  • Slow start (Java cold ~10s)
  • No HTTP API, CLI only
  • One process per render

CI integration

1
2
3
4
- name: Render PlantUML
run: |
curl -L -o plantuml.jar https://github.com/plantuml/plantuml/releases/latest/download/plantuml.jar
java -jar plantuml.jar -tsvg -nometadata -o rendered/ source/**/*.puml

Option 2: Docker

Most common; team-shared, stable.

1
2
3
4
docker run -d \
--name plantuml-server \
-p 8080:8080 \
plantuml/plantuml-server:latest

http://localhost:8080/ shows a built-in UI.

API endpoints

1
2
3
GET  http://localhost:8080/svg/<encoded>      # sync SVG
POST http://localhost:8080/form # form upload
GET http://localhost:8080/png/<encoded> # sync PNG

Example:

1
curl http://localhost:8080/svg/SoWkIImgAStDuKh9IArEB-tWmyoStDpKiCLCmKeoZDGm7mwU5XH4e1NLWfG6S3U3AYXvDQG7WQpyLOqpCgN94fXAvuYAcd4oC8j4aIWfWGfGQUa_AvBlLf-4Ld4QbvWAm0

Image tags

1
2
3
4
5
6
7
8
# Latest
docker run plantuml/plantuml-server:latest

# Specific
docker run plantuml/plantuml-server:v1.2026.7

# jdk17 (smaller)
docker run plantuml/plantuml-server:jdk17

Tuning

1
2
3
4
5
6
7
docker run -d \
--name plantuml \
-p 8080:8080 \
-e PLANTUML_LIMIT_SIZE=4096 \
-e MAX_URL_LENGTH=4096 \
-e PLANTUML_SECURITY_PROFILE=INTERNET \
plantuml/plantuml-server:latest
Env var Purpose
PLANTUML_LIMIT_SIZE URL length cap
MAX_URL_LENGTH URL byte length
PLANTUML_SECURITY_PROFILE Security policy preset
JAVA_OPTS JVM tuning

Pros

  • Shared, version-pinned
  • HTTP API
  • Image small (~100MB)

Cons

  • Default CORS is same-origin only
  • K8s probes required
  • Restart on image update

Option 3: Kubernetes

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
35
36
37
apiVersion: apps/v1
kind: Deployment
metadata:
name: plantuml
spec:
replicas: 2
selector:
matchLabels:
app: plantuml
template:
metadata:
labels:
app: plantuml
spec:
containers:
- name: plantuml
image: plantuml/plantuml-server:latest
ports:
- containerPort: 8080
livenessProbe:
httpGet: { path: /, port: 8080 }
initialDelaySeconds: 30
readinessProbe:
httpGet: { path: /, port: 8080 }
initialDelaySeconds: 10
resources:
requests: { memory: "256Mi", cpu: "100m" }
limits: { memory: "512Mi", cpu: "500m" }
---
apiVersion: v1
kind: Service
metadata:
name: plantuml
spec:
selector: { app: plantuml }
ports:
- { port: 80, targetPort: 8080 }

Ingress + CORS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: plantuml
annotations:
nginx.ingress.kubernetes.io/cors-allow-origin: "https://puml.online"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST"
spec:
rules:
- host: plantuml.puml.online
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: plantuml
port: { number: 80 }

Editor integration

VS Code

1
2
3
4
5
// .vscode/settings.json
{
"plantuml.server": "https://plantuml.puml.online",
"plantuml.render": "PlantUMLServer"
}

Install PlantUML extension; point server URL at self-hosted.

Vim / Neovim

1
2
let g:plantuml_server = 'https://plantuml.puml.online'
let g:plantuml_render = 'plantuml-server'

Install vim-plantuml.

IntelliJ

Install “PlantUML integration” plugin; set server URL under Settings → Other Settings → PlantUML.

CI / editors share one server

1
2
3
Local editor ──→ https://plantuml.puml.online ──→ K8s Deployment

└─→ CI (GitHub Actions)

One URL serves all clients:

  1. Editor preview
  2. CI docs render
  3. Team wiki static embed

Monitoring / logs

1
2
- name: PLANTUML_METRICS
value: "true"

Exposes Prometheus metrics:

1
2
3
plantuml_render_total_diagrams
plantuml_render_time_seconds
plantuml_render_errors_total

Logs:

1
docker logs -f plantuml-server

Request URL, status, render time per request.

Upgrade

1
2
3
docker pull plantuml/plantuml-server:latest
kubectl rollout restart deployment/plantuml
curl http://plantuml.puml.online/svg/SoWkIImgAStDuNc9LMqDBiLImiBeTWbG

K8s rolls out without downtime.

Offline mode

PlantUML server is stateless; pod reschedules don’t drop data. For fully-offline images, vendor fonts:

1
2
FROM plantuml/plantuml-server:latest
RUN apt-get install -y fonts-noto-cjk

Common gotchas

1. CORS rejects browser fetches

1
Access to fetch at 'https://plantuml...' from origin 'https://editor...' has been blocked by CORS

Fix:

  • Set PLANTUML_SECURITY_PROFILE=INTERNET
  • Or add CORS headers at the Ingress / reverse proxy layer

2. URL length cap

Default MAX_URL_LENGTH is 4096 bytes; complex diagrams exceed it. Bump:

1
-e MAX_URL_LENGTH=8192

Or switch to POST form.

3. JVM OOM on large diagrams

1
2
3
resources:
limits:
memory: "1Gi"
1
-e JAVA_OPTS="-Xmx1g"

4. CJK tofu (square boxes) on default OpenJDK

1
RUN apt-get update && apt-get install -y fonts-noto-cjk

Render with bundled fonts after that.

Self-hosted vs plantuml.com

Dimension Self-hosted plantuml.com
Network Internal Public
Latency Sub-ms in cluster Internet-dependent
Privacy Fully private Submits to third party
Reliability You control SLA Their outage
Version lock You decide Always latest
Maintenance One K8s Service Zero

TL;DR

Self-hosted PlantUML server is the shared rendering backbone for CI, editors, and team wiki. Standard image + K8s autoscaling + CORS + CJK fonts is the four-piece recipe to ship into production.

  • Title: PlantUML self-hosting: from plantuml.jar to Docker to enterprise K8s
  • Author: puml.online
  • Created at : 2026-07-29 16:15:00
  • Updated at : 2026-08-14 21:34:29
  • Link: https://puml.online/blog/plantuml-self-host-en/
  • License: This work is licensed under CC BY-NC-SA 4.0.