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 an A record.
  • Docker Engine (v20.10+) and Docker Compose (v2.0+) installed.
  • Ports 80 and 443 open 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.

  1. Stop port 80 processes: Ensure no process is using port 80. bash systemctl stop nginx || true
  2. 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
  3. 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.sh to restart the docker containers when certificates renew: bash #!/bin/bash docker exec drawio-nginx nginx -s reload Make 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:

  1. Start the Docker Services: bash cd /opt/drawio docker compose up -d
  2. Verify Services are Running: Check container statuses: bash docker compose ps
  3. Monitor Container Logs: To inspect the application initialization or diagnose issues, run: bash docker compose logs -f drawio The 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:

  1. Pull the latest images: bash cd /opt/drawio docker compose pull
  2. Recreate the containers: bash docker compose up -d --remove-orphans
  3. Prune obsolete images: Free up disk space by deleting older, unused image builds: bash docker image prune -f