PlantUML Server on Ubuntu 24.04 LTS
Overview
This guide shows you how to deploy and use PlantUML Server 1.2026.9 on Ubuntu 24.04 LTS from SecureAnyCloud on Microsoft Azure.
PlantUML turns short text descriptions into diagrams: sequence, class, activity, component, state, use case, deployment and object diagrams, plus mind maps, Gantt charts, JSON and YAML views, entity relationship diagrams and C4 architecture diagrams from its standard library. PlantUML Server is its web front end. It gives you a browser editor with a live preview, and it renders any diagram from a URL (/png/<encoded>, /svg/, /pdf/, /txt/), so wikis, documentation sites, CI pipelines and chat tools can show diagrams that you keep as text in your repositories.
The SecureAnyCloud image locks PlantUML Server down. What you get:
- HTTPS from the first boot. nginx serves PlantUML on port 443 with a certificate generated on your VM's first boot. Port 80 redirects to HTTPS.
- A password gate in front of everything. PlantUML Server has no login of its own. The whole site sits behind an HTTP Basic Auth prompt whose password is generated on your VM's first boot, so a fresh VM is never an open diagram renderer on the internet.
- No default login. The image contains no password. First boot writes the gate's user name and a random 24 character password to
/var/lib/sac/data/plantuml-server-credentials.txt(root only); nginx stores only a SHA-512 crypt hash. Common guesses are refused, and nginx removes the password from every request before PlantUML sees it. - Diagrams cannot read the server or reach the network. PlantUML runs in its strictest security profile,
SANDBOX: a diagram cannot include a local file or a URL (!include,!includeurl,!theme ... from), load JSON (%load_json), read the service's environment (%getenv) or embed an image from a URL. On top of that the PlantUML service can reach nothing but this VM's loopback address, so even a changed profile cannot fetch from your virtual network or the internet. - The server-side fetch endpoints are off. Upstream PlantUML Server can fetch a diagram from any URL a visitor names (
/proxy,/metadata?src=and ametadata=argument on its editor pages). nginx refuses all of them, so the VM cannot be used to probe your network. - Other web sites cannot act with your login. A browser re-sends the gate password to any site that asks, so nginx refuses every state changing request (POST and the like) and every WebSocket upgrade whose browser
Originis not this VM, and only the editor's own two POST calls are allowed at all. - A strict Content-Security-Policy. The editor may run only scripts served by this VM; no inline script, no
eval, no framing by other sites. The only inline code allowed is the editor's own seven button handlers, by exact hash. - No route to the Azure metadata service. nginx, PlantUML and the first boot service are blocked at the kernel level from the Azure Instance Metadata Service and the WireServer. The Azure VM agent keeps its access.
- PlantUML runs locked in. Jetty listens on
127.0.0.1:8080only, as its own unprivileged user, with a read only file system apart from a private temporary directory, a memory cap, a task cap and 64 worker threads. It never sees the client's forwarding headers: nginx passes the real client address from the socket. - Current, pinned releases. The official PlantUML Server 1.2026.9 release (checksum pinned, its bundled engine checked against Maven Central) runs on Eclipse Jetty 12.1.14, which carries the 2026 Jetty request smuggling and denial of service fixes, and OpenJDK 21 from Ubuntu.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: PlantUML Server 1.2026.9 (/var/lib/plantuml/webapps/ROOT.war), Eclipse Jetty 12.1.14 (/opt/jetty), OpenJDK 21, nginx, Ubuntu 24.04 LTS, cloud-init and the Azure Linux agent. Settings are in /etc/plantuml/.
Default user: the admin user you choose (azureuser in the examples).
Architecture: x64, Gen2.
Recommended size: Standard_B2s.
Prerequisites
- An Azure subscription, an SSH public key, and a virtual network and subnet in your region.
- A network security group that allows TCP 22 (SSH), TCP 443 (PlantUML Server) and TCP 80 (redirects to HTTPS). Open them to your own networks only. PlantUML Server needs no other port.
Deploy from the Azure portal
Search the Azure Marketplace for PlantUML Server, choose the SecureAnyCloud offer and select Create.
Pick your subscription, resource group and region. Choose the size Standard_B2s, an SSH public key for the admin user, and inbound ports 22, 80 and 443. Then select Review + create.
Deploy from the Azure CLI
RG="plantuml-rg"; LOCATION="eastus"; VM_NAME="plantuml1" IMAGE_URN="sac1647283583153:plantuml-server-ubuntu-24-04:default:latest" az group create --name "$RG" --location "$LOCATION" az vm image terms accept --urn "$IMAGE_URN" az vm create --resource-group "$RG" --name "$VM_NAME" --image "$IMAGE_URN" \
--size Standard_B2s --admin-username azureuser --ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 443 --priority 1001 az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 80 --priority 1002az vm create opens port 22 for you. Narrow the rules to your own networks with az network nsg rule update --source-address-prefixes <your-cidr> if you can.
Connect and check the services
ssh azureuser@<public-ip>First boot reads the VM's public IP from the Azure Instance Metadata Service, sets the served name and certificate, and creates the gate password. Then PlantUML and nginx start. Jetty unpacks PlantUML on start, which takes up to a minute on a new VM. Check the units:
systemctl is-active plantuml nginx plantuml-server-firstboot plantuml-server-address
sudo test -f /var/lib/sac/data/plantuml-server-firstboot.done && echo FIRSTBOOT_DONEAll four units are active and the first boot marker is present:
active active active active FIRSTBOOT_DONEOnly SSH and nginx listen on the network. PlantUML (Java, in Jetty) listens on the loopback address only:
sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort0.0.0.0:22 users:(("sshd"),("systemd")) 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx")) [::ffff:127.0.0.1]:8080 users:(("java"))The built in self-test checks the whole path through the gate: HTTPS, the password prompt, the editor, PNG, SVG and text rendering, the sandbox and the request guards:
sudo plantuml-server-selftestSELFTEST-OK https 301 + healthz + gate 401 + 6 wrong gate credentials refused + editor + PNG + SVG (Smetana) + text + SANDBOX (file include refused, %getenv empty) + fetch endpoints off + 403 cross-origin POST + /coder + 415 compressed body + 444 unknown host + preflightGet your URL and password
sudo cat /var/lib/sac/data/plantuml-server-credentials.txtThe file is readable by root only and holds the URL and the gate's user name and password for this VM:
# PlantUML Server 1.2026.9 - generated on first boot by plantuml-server-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Keep this file secret. PLANTUML_URL=https://<public-ip>/ PLANTUML_USERNAME=plantuml PLANTUML_PASSWORD=<PLANTUML_PASSWORD>
#
# Open PLANTUML_URL in a browser and sign in with PLANTUML_USERNAME / PLANTUML_PASSWORD (self-signed certificate minted
# on this VM's first boot; port 80 redirects to https). Render URLs: https://<host>/png/<encoded>, /svg/, /txt/. # Certificate: compare the fingerprint the user guide prints, or replace it with a CA-issued certificate; never add it to a # trust store. # Diagrams run in PlantUML's SANDBOX profile: no local file, URL, environment or JSON access from a diagram.
# Self-test:
sudo plantuml-server-selftest
# Change the served name:
sudo plantuml-server-set-hostname <your-domain>
# User guide: https://secureanycloud.com/stacks/plantuml-server-on-ubuntu-24-04-lts/The URL uses the VM's public IP. The first boot service cannot reach the metadata service itself, so a small service with no access to PlantUML's files, plantuml-server-address, reads the address from the Azure Instance Metadata Service on every boot (including a Standard SKU public IP, which only the metadata service's load balancer view shows) and records it here:
cat /run/sac-plantuml-server/address.envPLANTUML_PUBLIC_IP=<public-ip> PLANTUML_PRIVATE_IPS="<private-ip>"Requests to the VM's private address are redirected to the public one. To serve a DNS name instead, see Step 9.
The certificate is self signed and generated on this VM, so your browser warns about it the first time. Do not add it to a trust store. Instead, compare the fingerprint your browser shows with the one this VM reports, and accept the certificate only if they match (or replace it with a certificate from a certificate authority, Step 9):
sudo openssl x509 -in /etc/ssl/plantuml-server/plantuml-server.crt -noout -fingerprint -sha256sha256 Fingerprint=<64 hexadecimal digits in colon separated pairs, unique to this VM>It is an end entity certificate for this server only (CA:FALSE, server authentication), so it can never vouch for any other site.
Draw in the browser editor
Open the URL from Step 4, accept the certificate once you have checked its fingerprint, and sign in with the user name and password. The editor opens with a small example. Type PlantUML on the left; the preview on the right redraws a moment after you stop typing, and the address bar above the editor always holds the link to the diagram as you see it.
The View as icons open the diagram as PNG, SVG, ASCII art or PDF. The settings icon (the cog) chooses the preview format, a color theme and the editor options.
Component and deployment diagrams work the same way.
Render diagrams from the command line
Every diagram has a short encoded form. Send the source to /coder to get it, then fetch /png/<encoded>, /svg/<encoded>, /pdf/<encoded> or /txt/<encoded>. The examples below run on the VM in a ~/diagrams folder and reach nginx through the loopback address; from your own machine use the URL from Step 4 and -u plantuml (curl then asks for the password). They keep the password out of the command line by passing it to curl through a pipe:
mkdir -p ~/diagrams && cd ~/diagrams
H=$(sudo sed -n 's#^PLANTUML_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/plantuml-server-credentials.txt) gate() {
sudo awk -F= '/^PLANTUML_USERNAME=/{u=$2} /^PLANTUML_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/plantuml-server-credentials.txt; } cat > order.puml <<'PUML' @startuml actor Customer participant "Web shop" as Shop database Orders Customer -> Shop : place order Shop -> Orders : INSERT order Orders --> Shop : order id Shop --> Customer : confirmation @enduml PUML
ENC=$(curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -H "Origin: https://$H" --data-binary @order.puml "https://$H/coder") echo "$ENC" curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -o order.png -w '%{http_code} %{content_type}\n' "https://$H/png/$ENC" curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -o order.svg -w '%{http_code} %{content_type}\n' "https://$H/svg/$ENC" file order.pngHOqn3i8m34NtdEAhUo_GWGKni814aPZT98X8D8viz_w4462ptljF9cyis6zgl4PnXSIIJuMoORZ51VhWCe0KSprS8AE5DE8i8OgwRuTnrmQOK9xa8_ZjNKDLVTPL7a_puNBjjjFntpQE57fOnU_11C_vdcGbIvnV 200 image/png 200 image/svg+xml order.png: PNG image data, 300 x 307, 8-bit colormap, non-interlacedThe same encoded string works in the browser (https://<your-host>/uml/<encoded> opens it in the editor) and in any page that can show an image, as long as the viewer has signed in to the gate. The text renderer is handy in terminals and code reviews:
cd ~/diagrams
H=$(sudo sed -n 's#^PLANTUML_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/plantuml-server-credentials.txt) gate() {
sudo awk -F= '/^PLANTUML_USERNAME=/{u=$2} /^PLANTUML_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/plantuml-server-credentials.txt; } curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -H "Origin: https://$H" --data-binary @order.puml "https://$H/coder" | xargs -I{} curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" "https://$H/txt/{}"┌────────┐ ┌────┐ ┌──────┐ │Customer│ │Shop│ │Orders│ └────┬───┘ └──┬─┘ └───┬──┘ │ place order │ │ │──────────────>│ │ │ │ │ │ │ INSERT order │ │ │──────────────>│ │ │ │ │ │ order id │ │ │< ─ ─ ─ ─ ─ ─ ─│ │ │ │ │ confirmation │ │ │< ─ ─ ─ ─ ─ ─ ─│ │ │ │ │ ┌────┴───┐ ┌──┴─┐ ┌───┴──┐ │Customer│ │Shop│ │Orders│ └────────┘ └────┘ └──────┘Class, component, state and activity diagrams are laid out by PlantUML's built in Smetana engine (see Step 8), with no extra software:
cd ~/diagrams
H=$(sudo sed -n 's#^PLANTUML_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/plantuml-server-credentials.txt) gate() {
sudo awk -F= '/^PLANTUML_USERNAME=/{u=$2} /^PLANTUML_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/plantuml-server-credentials.txt; } cat > classes.puml <<'PUML' @startuml class Customer { +id: int +email: String } class Order { +id: int +total: Decimal } Customer "1" -- "*" Order : places @enduml PUML curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -H "Origin: https://$H" --data-binary @classes.puml "https://$H/coder" | xargs -I{} curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -o classes.svg -w '%{http_code} %{content_type}\n' "https://$H/svg/{}" grep -o '>Customer<\|>Order<\|>places<' classes.svg | sort -u200 image/svg+xml >Customer< >Order< >places<Remove the example folder when you are done:
rm -rf ~/diagramsThe diagram sandbox
PlantUML's settings are in /etc/plantuml/plantuml.env, and every diagram is laid out by Smetana through /etc/plantuml/config.puml:
grep -v '^#' /etc/plantuml/plantuml.env | grep . cat /etc/plantuml/config.pumlPLANTUML_SECURITY_PROFILE=SANDBOX PLANTUML_CONFIG_FILE=/etc/plantuml/config.puml PLANTUML_LIMIT_SIZE=4096 PLANTUML_STATS=off !pragma layout smetanaSANDBOX is PlantUML's strictest profile. A diagram that tries to read a file is refused (PlantUML answers 400 and names the refused include in its X-PlantUML-Diagram-Error header), and %getenv returns nothing:
mkdir -p ~/diagrams && cd ~/diagrams
H=$(sudo sed -n 's#^PLANTUML_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/plantuml-server-credentials.txt) gate() {
sudo awk -F= '/^PLANTUML_USERNAME=/{u=$2} /^PLANTUML_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/plantuml-server-credentials.txt; } printf '@startuml\n!include /etc/passwd\nAlice -> Bob\n@enduml\n' > probe.puml curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -H "Origin: https://$H" --data-binary @probe.puml "https://$H/coder" | xargs -I{} curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -o /dev/null -D - "https://$H/png/{}" | tr -d '\r' | awk -F': ' '/^HTTP/ {print} tolower($1) == "x-plantuml-diagram-error" {print "refused by the sandbox:", $2}' printf '@startuml\nAlice -> Bob : HOME=[%%getenv("HOME")]\n@enduml\n' > env.puml curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" -H "Origin: https://$H" --data-binary @env.puml "https://$H/coder" | xargs -I{} curl -sk -K <(gate) --resolve "$H:443:127.0.0.1" "https://$H/txt/{}" | grep -o 'HOME=\[[^]]*\]' cd ~ && rm -rf ~/diagramsHTTP/2 400 refused by the sandbox: cannot include /etc/passwd HOME=[]The same holds for URLs: !include http://169.254.169.254/... (the Azure metadata service), !includeurl http://168.63.129.16/... (the WireServer) and any address in your virtual network are refused before PlantUML opens a connection, and the image's build checks it with a listener that records every request it receives (none arrive).
The standard library still works, because it is built into PlantUML: !include <C4/C4_Container>, !include <azure/...> and the other <...> libraries need no file or network access. What a diagram cannot do is pull in a file or a URL. Put shared styles and macros into the diagram text itself, or into /etc/plantuml/config.puml, which PlantUML adds to the top of every diagram (restart with sudo systemctl restart plantuml after changing it).
/usr/local/sbin/plantuml-preflight checks the settings each time PlantUML starts and refuses to start it with a weaker profile: INTERNET, LEGACY and UNSECURE would let every person who can open the editor read files from this VM or make it fetch URLs.
Opt-in: allow named URLs (ALLOWLIST). If your diagrams must include files from a fixed set of URLs (a shared style sheet on your Git server, for example), switch to the ALLOWLIST profile, which allows only the URL prefixes you list. This is a deliberate loosening, so weigh the risk first: every person who can sign in to the gate can then make this VM fetch those URLs and see what comes back, so list only addresses whose content is safe to show to all of them, never an internal admin endpoint or anything else in your virtual network, and keep the list short. Three changes, as root:
- Create
/etc/plantuml/allowlist.propertieswith one line, the URL prefixes separated by semicolons, for exampleplantuml.allowlist.url=https://git.example.com/styles/. - In
/etc/plantuml/plantuml.envsetPLANTUML_SECURITY_PROFILE=ALLOWLISTand addPLANTUML_PROPERTY_FILE=/etc/plantuml/allowlist.properties. - Let PlantUML out of the loopback address, keeping the Azure metadata and WireServer block in place: create
/etc/systemd/system/plantuml.service.d/60-allowlist-egress.confcontaining
[Service] IPAddressAllow= IPAddressDeny= IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 ::ffff:169.254.169.254/128 ::ffff:168.63.129.16/128Then run sudo systemctl daemon-reload && sudo systemctl restart plantuml. To go back, delete the drop-in and the property file and set SANDBOX again. The network security group still applies to what PlantUML can reach; consider an outbound rule that allows only the hosts you listed.
Layout engine
Diagrams that need an automatic layout (class, component, use case, state, object and deployment diagrams) are laid out by Smetana, PlantUML's built in Java port of the Graphviz dot layout. No separate layout program runs: everything happens inside the PlantUML service. Graphviz is not installed, because Ubuntu publishes it only in its community maintained universe archive, which does not get standard Ubuntu security updates.
Smetana handles most diagrams the same way Graphviz does. If one of yours relies on a Graphviz only feature and you accept running a package from universe (security fixes for it come with Ubuntu Pro), you can install Graphviz yourself with sudo apt-get install graphviz and remove the !pragma layout smetana line from /etc/plantuml/config.puml; then restart PlantUML. Note that the image's ProtectSystem and memory limits then also apply to the dot process PlantUML starts.
Publish on your own domain
Point a DNS record for your domain at the VM's public IP (a static public IP keeps the address stable), then give PlantUML Server the name. The helper mints a new certificate for it, pins nginx and the cross-site guard to it and redirects the VM's IP addresses there:
sudo plantuml-server-set-hostname <your-domain>For a certificate browsers trust, replace /etc/ssl/plantuml-server/plantuml-server.crt and /etc/ssl/plantuml-server/plantuml-server.key with your own (for example from Let's Encrypt or your certificate authority) and run sudo systemctl reload nginx. Never add the self signed certificate to a trust store on your computers; check its fingerprint as in Step 4 instead.
Opt-in: public rendering for embedding. A wiki or documentation site that shows diagrams to readers who never sign in needs the image endpoints without the gate. That turns the VM into a public renderer: anyone who finds it can make it render diagrams, which costs CPU and memory (the limits in Step 10 still apply, and the sandbox still holds). If you need it, restrict the network security group to the networks your readers come from, and open only the image formats. Add this block to the PlantUML server on port 443 in /etc/nginx/sites-available/sac-plantuml, before location / {:
# sac opt-in: public PNG and SVG rendering (no gate); everything else keeps the password prompt location ~ ^/(png|svg)/[0-9A-Za-z_~-]+$ { include /etc/nginx/plantuml-proxy.conf; }Then run sudo nginx -t && sudo systemctl reload nginx. Remove the block to close it again.
Security notes
nginx, PlantUML and the first boot service are blocked from the Azure Instance Metadata Service and the WireServer, and PlantUML may talk to nothing but the loopback address:
for u in nginx plantuml plantuml-server-firstboot; do printf '%s ' "$u"; systemctl show -p IPAddressDeny --value "$u" | tr ' ' '\n' | LC_ALL=C sort | tr '\n' ' '; echo; done printf 'plantuml allow: '; systemctl show -p IPAddressAllow --value plantuml | tr ' ' '\n' | LC_ALL=C sort | tr '\n' ' '; echonginx 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 plantuml 0.0.0.0/0 ::/0 plantuml-server-firstboot 168.63.129.16/32 169.254.169.254/32 ::ffff:168.63.129.16/128 ::ffff:169.254.169.254/128 fd00:ec2::254/128 plantuml allow: 127.0.0.0/8 ::1/128(PlantUML's own list is any, which covers the metadata addresses too; its metadata drop-in stays in place for when you widen it as in Step 7.)
The gate answers before PlantUML does, the upstream fetch endpoints are refused, a state changing request that another web site's page sends is refused, a request body with a Content-Encoding is refused, and a request for a host name the VM does not serve gets no answer at all:
H=$(sudo sed -n 's#^PLANTUML_URL=https://\([^/]*\)/$#\1#p' /var/lib/sac/data/plantuml-server-credentials.txt) gate() {
sudo awk -F= '/^PLANTUML_USERNAME=/{u=$2} /^PLANTUML_PASSWORD=/{p=$2} END{printf "user = \"%s:%s\"\n", u, p}' /var/lib/sac/data/plantuml-server-credentials.txt; } curl -sk -o /dev/null -w '%{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/uml/" curl -sk -K <(gate) -o /dev/null -w '%{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/proxy?src=https://example.com/diagram.puml" curl -sk -K <(gate) -o /dev/null -w '%{http_code}\n' --resolve "$H:443:127.0.0.1" "https://$H/uml/?metadata=https://example.com/diagram.png" curl -sk -K <(gate) -o /dev/null -w '%{http_code}\n' --resolve "$H:443:127.0.0.1" -X POST -H 'Origin: https://evil.example' --data 'x=1' "https://$H/coder" printf 'x' | curl -sk -K <(gate) -o /dev/null -w '%{http_code}\n' --resolve "$H:443:127.0.0.1" -H 'Content-Encoding: gzip' --data-binary @- "https://$H/coder" curl -sk -o /dev/null -w '%{http_code}\n' --resolve evil.example:443:127.0.0.1 https://evil.example/ || true401 404 400 403 415 000PlantUML runs as its own user with a read only file system:
systemctl show -p User -p ProtectSystem -p PrivateTmp -p TasksMax plantumlTasksMax=256 User=plantuml PrivateTmp=yes ProtectSystem=strictOther notes:
- Resource limits. Diagrams are capped at 4096 pixels per side (
PLANTUML_LIMIT_SIZE), request bodies at 4 MiB, each client at 32 connections and 20 requests a second, and PlantUML at 64 rendering threads, 75% of the VM's memory and 256 tasks. nginx gives up on a render after 60 seconds; a very large diagram is better split into several. Choose a bigger VM size for heavy rendering. - What PlantUML can write. Only its private temporary directory, which systemd empties every time the service stops. The credentials file, the TLS key and the nginx configuration are invisible to it.
- The editor's components. The browser editor bundles the Monaco editor 0.36.1, which contains an old DOMPurify. Monaco uses it only on its own help text, never on your diagrams, and the Content-Security-Policy blocks any script the page did not load itself. SecureAnyCloud re-checks it with every PlantUML Server release.
- The gate password never reaches PlantUML. nginx removes it, and the browser's cookies, from every request.
- SSH. Root login, empty passwords and X11 forwarding are off. Whether password login is allowed is the choice you made when you created the VM; the image does not override it.
- Logs. nginx logs are in
/var/log/nginx, with the real client address; Jetty and PlantUML log to the journal (journalctl -u plantuml).
Keep it up to date
Ubuntu security updates, including OpenJDK, install automatically. To apply all updates now:
sudo apt-get update && sudo apt-get upgrade -y && sudo systemctl restart plantumlNew PlantUML Server and Jetty releases arrive as new image versions of this offer, built and checked the same way. PlantUML Server keeps no data on the VM: your diagrams live wherever you keep their text, so moving to a new version is deploying it and pointing your DNS name at it. Copy /etc/plantuml/ across if you changed it. Read the PlantUML Server releases before you move.
Support and licensing
PlantUML Server and the PlantUML engine are open source under the GNU General Public License, version 3 or later (Arnaud Roques and the PlantUML contributors); the source code is at github.com/plantuml/plantuml-server and github.com/plantuml/plantuml. The image runs the unmodified official release, and /usr/share/doc/sac-plantuml-server/licences/ holds the source offer and the Eclipse Jetty license (EPL 2.0 or Apache 2.0).
Diagrams you generate belong to you and are not covered by the GPL. There is no per-user license fee. The SecureAnyCloud charge covers the image build, hardening, first boot setup, security patching and support.
- Email: sales@secureanycloud.com
- Website: https://secureanycloud.com
- Support: 24/7, with a 24 hour response SLA
Deploy on Azure
Launch PlantUML Server 1.2026.9 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com