Skip to content
Deployments

Deploy Excalidraw with the excalidraw-whiteboard template

Deployment

Deploy Excalidraw with the excalidraw-whiteboard template

Stand up Excalidraw, an open-source collaborative whiteboard, on a single Quake AI instance using the validated OpenTofu template excalidraw-whiteboard. You apply the template, point two subdomains at the host and serve both over HTTPS through Caddy, set the collaboration server's domain and start the containers, open the app, start a live collaboration session, and confirm real-time sync from a second browser session.

Excalidraw keeps your team's diagrams on infrastructure you own. You run it yourself; this is a self-hosted tool you operate, not a managed multi-tenant service.

Team memberTeammateFloating IPUbuntu instanceCaddyreverse proxyExcalidrawfrontendexcalidraw-roomcollaboration server draw.example.com -> 8080collab.example.com -> 8081 (WebSocket)encrypted drawing syncHTTPSHTTPS
Click to zoom
What you'll build: an Excalidraw host on a single instance with two hostnames, each served over HTTPS through a Caddy reverse proxy

Monthly cost estimate

Pricing calculator ↗

Sized as a custom package on shared vCPU.

Starting template$13.10/mo

Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.

What each resource is for

Excalidraw whiteboard host

s1a.small · 2 shared vCPU, 2 GiB RAM, 0.5 Gbps

Runs the Excalidraw frontend and the excalidraw-room collaboration server as two stateless containers on the same instance, with no bundled datastore.

Both containers are lightweight and stateless, so 2 vCPU and 2 GiB RAM covers a small team; the lightest ops-tools footprint in this library alongside Uptime Kuma.

$16.50/mo

Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.

Included in baseline

s1a.small

2 shared vCPU, 2 GiB RAM, 0.5 Gbps

$16.50

Compute + RAM rate basis

2 vCPU + 2 GiB RAM at $29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM (regular). Totals apply the flat −$5/mo package promotion.

—

Block storage (20 GiB)

20 GiB at $0.08/GiB/mo

$1.60

Public IP (included)

1 included with the custom package

$0.00

Package promotional discount

Flat −$5.00/mo on the custom package (same promotion as named plans).

$-5.00

Included at no charge

These line items are zero on Quake AI. Many other providers meter them separately.

Data transfer (inbound and outbound)

Unlimited data transfer on every plan; Quake AI does not meter per-GB egress.

AWS, GCP, and Azure meter outbound transfer per GB. DigitalOcean and Hetzner include an allowance on compute plans, then charge overage.

Learn more
$0.00

Private networking

Private networks, subnets, Neutron routers, and security groups are included with the plan.

VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.

$0.00

Control-plane API requests

OpenStack API calls for provisioning and management are included.

Some managed services on other clouds meter API calls or charge for premium control-plane features.

$0.00

Pricing data last validated: . For current rates, check quake.ai/pricing.

Prerequisites#

You need:

  • OpenTofu 1.6.0 or later (or Terraform 1.6.0 or later) installed locally.
  • Your OpenStack credentials sourced into the shell (source openrc.sh). See the OpenStack CLI guide.
  • An SSH keypair that already exists in your project. Record its name for the key_name variable.
  • A copy of the excalidraw-whiteboard template directory from the template reference page.
  • Two subdomains you can point at the instance.

Step 1: Apply the template#

Copy the template's example variables file and set key_name:

bash
cp terraform.tfvars.example terraform.tfvars
HCL
key_name = "YOUR_KEY_NAME"

Initialize, preview, and apply:

bash
tofu init
tofu plan
tofu apply

OpenTofu provisions a private network, a router, a security group, an instance, and a floating IP. On first boot, cloud-init installs Docker Engine and writes /opt/excalidraw/docker-compose.yml and /opt/excalidraw/.env, but does not start either container: the frontend's collaboration-URL patch depends on knowing the real public domain first.

Read the outputs and record floating_ip:

bash
tofu output

Step 2: Point two domains at the host and serve HTTPS with Caddy#

  1. Create two DNS A records, for example draw.example.com and collab.example.com, both pointing at YOUR_FLOATING_IP. Follow How to point a domain at a Quake AI resource. Wait until both resolve:
bash
dig +short draw.example.com
dig +short collab.example.com
  1. SSH to the instance and create /opt/excalidraw/Caddyfile:
draw.example.com {
  reverse_proxy 127.0.0.1:8080
}

collab.example.com {
  reverse_proxy 127.0.0.1:8081
}

Caddy upgrades WebSocket connections automatically inside reverse_proxy, so the collaboration block needs no extra configuration.

  1. Add Caddy to /opt/excalidraw/docker-compose.yml:
YAML
services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    network_mode: host
    volumes:
      - /opt/excalidraw/Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
volumes:
  caddy_data:

For background on certificates, see How to issue and auto-renew a TLS certificate with Let's Encrypt.

Step 3: Set the collaboration domain and start Excalidraw#

Edit /opt/excalidraw/.env and set:

VITE_APP_WS_SERVER_URL=https://collab.example.com

Start the full stack:

bash
cd /opt/excalidraw
sudo docker compose up -d

Check the frontend container's logs for the patch confirmation:

bash
docker compose logs excalidraw | grep "Patching hardcoded collab URL"

Step 4: Start a live collaboration session#

  1. Open https://draw.example.com.
  2. Open the hamburger menu in the top left and select Live collaboration.
  3. Select Start session and copy the generated share link.

Step 5: Confirm real-time sync from a second browser session#

  1. Open the share link in a second browser or an incognito window.
  2. Draw a shape in one window and confirm it appears in the other within a second or two.
  3. Open the browser's developer tools, filter the network tab to WS, and confirm the open WebSocket connects to collab.example.com, not oss-collab.excalidraw.com. That confirms the collaboration-URL patch applied correctly.

What you built#

  • Applied the excalidraw-whiteboard template to provision a network, security group, instance, and floating IP, with both containers held until configuration finished
  • Served two hostnames over HTTPS by pointing both domains at the floating IP and routing them through a Caddy reverse proxy, with WebSocket upgrade on the collaboration hostname
  • Set the collaboration domain and started both containers
  • Started a live collaboration session and confirmed real-time sync between two browser sessions

Scope of this deployment#

This template runs a single-VM Excalidraw host, not a managed multi-tenant whiteboard service. The instance is CPU-only and runs in one region, and it bundles no database because there is nothing to persist server-side: drawing content lives in each browser's local storage, and the collaboration server relays end-to-end-encrypted data without persisting it. You operate the instance, Docker, and both containers yourself. The collaboration server has no built-in authentication, so anyone with a live share link can join a room; this suits a small trusted team rather than a public-facing deployment.

Next steps#

Clean up#

When you no longer need the deployment, destroy everything the template created:

bash
tofu destroy

Then remove the two DNS A records you created in step 2.

Before this
Was this page helpful?