BYOS · Self-hosted rendering
Self-hosted PlantUML rendering for Confluence Cloud
If you write PlantUML in Confluence, your diagram source travels to whichever server renders it. That single fact is the whole reason self-hosted rendering exists — and this article is the setup, end to end: why you might want your own server, how to deploy one, and the configuration mistake that breaks most first attempts.
Where your diagram source actually goes
PlantUML rendering works by encoding the diagram source into a URL. The macro takes your text, compresses and encodes it, and requests the image from a PlantUML server:
@startuml
actor Engineer
participant "Confluence page" as CC
participant "PlantUML server" as PS
Engineer -> CC : publish page
CC -> PS : GET /svg/<encoded diagram source>
PS --> CC : rendered SVG
@enduml
By default that server is the public one at plantuml.com. For a conference-talk sequence diagram, that's fine. For the architecture of your payment system, the question "who operates the server that receives my diagram source?" may have a required answer: we do. That's what bring-your-own-server (BYOS) means here — the app keeps working exactly the same way, but the render requests go to a PlantUML server inside your network.
The open-source part is what makes this possible
Self-hosting only works because the pieces are open. The PlantUML server is an open-source project with an official Docker image — you can run the same renderer the public site runs. And PlantUML for Confluence, the app this site is about, is source-available too: the code that composes the render URL and displays the result is public at github.com/paloriapps/plantuml-confluence. So the entire path your diagram takes — from macro body to rendered SVG — is code you can read and infrastructure you can own. You are not trusting a vendor's description of the data path; you can check it.
The setup
1. Deploy the PlantUML server
The official Docker image is the simplest path — it ships with Tomcat included, so one container gives you a working render server. Run it wherever your internal services live.
2. Put it behind HTTPS
Confluence Cloud requires encrypted connections, so the render server must be reachable over HTTPS. In practice that means a reverse proxy — nginx, Caddy, or Traefik — in front of the container, terminating TLS.
3. Fix the CORS gotcha before it finds you
This is the step that catches most first-time deployments. The browser calls your render server directly from the Confluence page, so the response must carry exactly one Access-Control-Allow-Origin header. The Tomcat bundled in the Docker image already emits one. If your reverse proxy adds another, browsers reject the response as invalid — and the failure looks like the app being broken, not like a proxy misconfiguration. Pick one owner for the header:
- Let Tomcat own it: configure the proxy to not add CORS headers (
proxy_hide_headerin nginx), or - Let the proxy own it: disable Tomcat's CORS emission via the container's environment variables and manage headers at the proxy exclusively.
Then verify there is exactly one header:
curl -sI https://your-render-server/plantuml/ | grep -i access-control-allow-origin
One line of output, not two.
4. Point the app at your server
In Confluence: Settings → Apps → PlantUML, enter the HTTPS URL of your server. The setting is per-site and applies to every PlantUML macro on it.
What gets stored, and where
Worth stating precisely, because it's the substance behind the privacy claim:
- Your diagram source is stored in the macro body, inside the Confluence page itself. It lives where your page lives.
- Your render-server URL is stored per site in Forge's key-value store.
- Nothing else. The app has no backend functions and no telemetry — rendering happens client-side by composing a URL to your chosen server. There is no Palori server in the path at all.
The honest caveat
A self-hosted render server is an ops commitment. It's small — one stateless container — but it's yours: you patch it, you keep it reachable, and if it goes down, diagrams stop rendering until it's back. If your diagrams aren't sensitive, the public server is the right amount of infrastructure, which is to say none. Self-host when the data path requires it, not for its own sake.
If you're arriving here after moving from Data Center and deciding where rendering should happen afterwards, the migration walkthrough covers that decision in context.