Draw.io Diagram Editor: Deploy Your Own Vector Diagram Tool in Docker
Deploy Draw.io (diagrams.net) using Docker Compose. Create flowcharts, network diagrams, and mockups locally on a secure, self-hosted server.
System Architecture and Pre-requisites
Self-hosting Draw.io (historically diagrams.net) on a VPS provides complete control over your drawing data, ensuring that sensitive infrastructure diagrams, flowcharts, and mockups remain private. The official jgraph/drawio container is Java-based, running Tomcat under the hood.
This guide implements a production-ready setup utilizing Docker Compose behind an Nginx reverse proxy with automatic Let's Encrypt SSL certificates.
Prerequisites
- A virtual private server (VPS) running Ubuntu 22.04 LTS or newer.
- A registered domain or subdomain (e.g.,
drawio.example.com) pointed to your VPS IP via anArecord. - Docker Engine (v20.10+) and Docker Compose (v2.0+) installed.
- Ports
80and443open on the host firewall (e.g.,ufw).
Directory Layout
Create the following folder structure in /opt/drawio to manage configurations and persistent data:
mkdir -p /opt/drawio/nginx
Your final structure will look like this:
/opt/drawio/
├── docker-compose.yml
└── nginx/
└── default.conf
Docker Compose Configuration
The following docker-compose.yml configures the Draw.io application container and an Nginx container. We configure Draw.io to run in a self-contained, high-security mode, stripping external cloud integration dependencies to ensure strict data privacy.
Save the following content to /opt/drawio/docker-compose.yml:
version: '3.8'
services:
drawio:
image: jgraph/drawio:24.4.8 # Pin to a stable release or use latest
container_name: drawio
restart: unless-stopped
expose:
- "8080"
environment:
# Public URL of the service (must include trailing slash)
- DRAWIO_SERVER_URL=https://drawio.example.com/
# Base URL (without trailing slash) used for embeds
- DRAWIO_BASE_URL=https://drawio.example.com
# Force export calls (PDF/Image export) to route internally
- DRAWIO_SELF_CONTAINED=1
# Disable external proxy endpoint
- ENABLE_DRAWIO_PROXY=0
# Enforce secure environment settings using JSON overrides
- DRAWIO_CONFIG={"gapi":0,"od":0,"db":0,"tr":0,"gh":0,"gl":0,"lockdown":true}
networks:
- drawio-net
nginx:
image: nginx:alpine
container_name: drawio-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
depends_on:
- drawio
networks:
- drawio-net
networks:
drawio-net:
driver: bridge
Reverse Proxy Configuration (Nginx)
The Nginx container handles SSL termination, serves ACME challenges for Let's Encrypt, and proxies HTTP traffic to Tomcat on port 8080.
Save the following configuration as /opt/drawio/nginx/default.conf. Remember to replace drawio.example.com with your actual domain:
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name drawio.example.com;
# Certbot / Webroot ACME challenge path
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS Configuration
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name drawio.example.com;
# SSL Certificates (Managed via Let's Encrypt Certbot)
ssl_certificate /etc/letsencrypt/live/drawio.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/drawio.example.com/privkey.pem;
# Modern SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
# Session caching
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# Custom Content Security Policy optimized for Draw.io self-contained
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data:; img-src 'self' data: blob: https:; frame-ancestors 'self';" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Proxy settings
location / {
proxy_pass http://drawio:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Disable buffering to prevent delay in interactive drawing components
proxy_buffering off;
# Increase proxy timeouts to accommodate large XML diagram saves
proxy_connect_timeout 90s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
# Max request size limit for large drawing imports
client_max_body_size 20M;
}
}
Alternative Setup: Caddy Server (Automated SSL)
If you prefer to avoid manual Nginx and Certbot configuration, Caddy handles SSL certificates automatically. Below is the simplified docker-compose.yml for Caddy:
version: '3.8'
services:
drawio:
image: jgraph/drawio:latest
container_name: drawio
restart: unless-stopped
expose:
- "8080"
environment:
- DRAWIO_SERVER_URL=https://drawio.example.com/
- DRAWIO_SELF_CONTAINED=1
- ENABLE_DRAWIO_PROXY=0
- DRAWIO_CONFIG={"gapi":0,"od":0,"db":0,"tr":0,"gh":0,"gl":0,"lockdown":true}
networks:
- drawio-net
caddy:
image: caddy:alpine
container_name: drawio-caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- caddy_data:/data
- caddy_config:/config
environment:
- DRAWIO_DOMAIN=drawio.example.com
command: caddy reverse-proxy --from https://$$DRAWIO_DOMAIN --to http://drawio:8080
depends_on:
- drawio
networks:
- drawio-net
volumes:
caddy_data:
caddy_config:
networks:
drawio-net:
driver: bridge
SSL Provisioning via Let's Encrypt (Certbot)
If using Nginx, generate SSL certificates using Certbot on the host machine before launching Nginx.
- Stop port 80 processes: Ensure no process is using port 80.
bash systemctl stop nginx || true - Run Certbot in Standalone Mode:
bash apt install certbot -y certbot certonly --standalone -d drawio.example.com --non-interactive --agree-tos -m admin@example.com - Establish Automatic Renewal: Let's Encrypt certificates expire every 90 days. Certbot installs a cron job automatically on most distributions. Add a post-hook script in
/etc/letsencrypt/renewal-hooks/post/reload-nginx.shto restart the docker containers when certificates renew:bash #!/bin/bash docker exec drawio-nginx nginx -s reloadMake the script executable:bash chmod +x /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
Detailed Environment Variables Explained
Configuring Draw.io using DRAWIO_CONFIG restricts data leakage to third parties. Below is the breakdown of the parameters parsed in our configuration:
gapi: 0: Disables the Google Drive API. Removes the Google Drive option from the initial file storage screen.od: 0: Disables Microsoft OneDrive integration.db: 0/tr: 0: Disables Dropbox and Trello integrations.gh: 0/gl: 0: Hides GitHub and GitLab integration tabs.lockdown: true: Restricts the UI. The browser client is locked down to local-device storage and offline capabilities, and it enforces that drawing binaries do not fetch remote assets (e.g. templates or external clip art libraries) unless they reside on your self-hosted domain.
Launching the Deployment
Once files are placed and certificates are obtained:
- Start the Docker Services:
bash cd /opt/drawio docker compose up -d - Verify Services are Running: Check container statuses:
bash docker compose ps - Monitor Container Logs: To inspect the application initialization or diagnose issues, run:
bash docker compose logs -f drawioThe application is fully running when the logs output Tomcat initialization patterns:INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [xxxx] milliseconds
Upgrade and Maintenance Procedures
To update the self-hosted Draw.io deployment to the latest version, execute the following commands in sequence:
- Pull the latest images:
bash cd /opt/drawio docker compose pull - Recreate the containers:
bash docker compose up -d --remove-orphans - Prune obsolete images: Free up disk space by deleting older, unused image builds:
bash docker image prune -f