LocalHake

Tutorial

Excalidraw on Proxmox 9 VE LXC

Excalidraw is a free, open-source virtual whiteboard web application for creating hand-drawn style diagrams and sketches. This guide covers installing it on Proxmox VE 9 in an LXC container with a production build.

Hake HardwarePublished Updated ~9 minintermediate

This post contains paid links (affiliate) — how that works.


▶ Watch this build on YouTube
Contents

Excalidraw is a free, open-source virtual whiteboard web application for creating hand-drawn style diagrams and sketches. It's designed for quick brainstorming, flowcharts, architecture diagrams, wireframes, and collaborative drawing sessions with a simple, intuitive interface that mimics sketching on paper.

Prerequisites

  • Proxmox VE 9.0+ installed and running
  • SSH or console access to Proxmox host
  • Available static IP address on your network (e.g., 192.168.10.105)
  • Your network gateway IP address
  • Minimum 3GB RAM and 10GB disk space available (to be safe, use 4GB here if possible)

Network Configuration Required

Before starting, determine:

  • Desired Excalidraw IP: An unused static IP (e.g., 192.168.10.105)
  • Gateway (typically router) IP: Your router's IP (e.g., 192.168.10.1)
  • Subnet Mask: Usually /24 for home networks

Step 1: Create the LXC

Update templates

bash
pveam update

Identify the correct version of Debian

bash
pveam available | grep debian | grep -v turnkey

This guide will use the latest Debian 13 (update for your case)

bash
pveam download local debian-13-standard_13.1-2_amd64.tar.zst

Create the container

bash
pct create 105 local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst \
  --hostname excalidraw \
  --memory 4096 \
  --cores 2 \
  --rootfs local-lvm:10 \
  --net0 name=eth0,bridge=vmbr0,ip=192.168.10.105/24,gw=192.168.10.1 \
  --unprivileged 1 \
  --onboot 1 \
  --start 1

Parameters explained:

  • 105: Container ID (must be unique; adjust if already in use)
  • --memory 4096: 4GB RAM (required for building Excalidraw)
  • --cores 2: Two CPU cores (recommended for yarn build process)
  • --rootfs local-lvm:10: 10GB disk space
  • --net0: Static IP configuration for network access
  • --unprivileged 1: Runs as unprivileged container for better security
  • --onboot 1: Auto-start container on system boot
  • --start 1: Start container immediately after creation

Important adjustments:

  • Container ID (105): Change if already in use (check with pct list)
  • IP Address: Replace 192.168.10.105/24 with your desired static IP
  • Gateway: Replace 192.168.10.1 with your network's gateway
  • Storage: Replace local-lvm if using different storage backend
  • Bridge: Replace vmbr0 if using different bridge (check with ip a | grep vmbr)

Verify the container is running successfully

bash
pct status 105

Step 2: Container Setup

Enter the container

bash
pct enter 105

Set locale to avoid package installation warnings. Uncomments the en_US.UTF-8 locale in the configuration file.

bash
sed -i 's/^# en_US.UTF-8 UTF-8$/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
locale-gen && \
update-locale LANG=en_US.UTF-8 && \
exec bash

Update LXC

bash
apt update && apt upgrade -y

Install essential development and dependencies

bash
apt install -y git nano build-essential curl

Step 3: Install Node

Download tar and checksum from https://nodejs.org

bash
wget -P /tmp https://nodejs.org/dist/v24.11.0/node-v24.11.0-linux-x64.tar.xz
wget -P /tmp https://nodejs.org/dist/v24.11.0/SHASUMS256.txt

Verify checksum

bash
grep 'node-v24.11.0-linux-x64.tar.xz' /tmp/SHASUMS256.txt | awk '{print $1 "  /tmp/" $2}' | sha256sum --check

Extract

bash
tar -xJf /tmp/node-v24.11.0-linux-x64.tar.xz -C /usr/local --strip-components=1

Update paths

bash
echo 'export PATH="/usr/local/bin:$PATH"' >> /etc/profile && \
source /etc/profile

Verify

bash
node --version
npm --version
which node

Install serve

bash
npm install -g serve
which serve

Enable corepack and install yarn

bash
corepack enable && \
echo 'Y' | yarn --version

Clean up

bash
rm /tmp/node-v24.11.0-linux-x64.tar.xz
rm /tmp/SHASUMS256.txt

Step 4: Download and Build Excalidraw

Create installation directory for Excalidraw

bash
mkdir -p /opt/excalidraw

Clone the repository

bash
git clone https://github.com/excalidraw/excalidraw.git /opt/excalidraw

Change to the Excalidraw directory

bash
cd /opt/excalidraw

Install all project dependencies

bash
yarn install

Set Node.js memory limit for build (if it fails even with this, it likely needs even more memory and you may need to increase the container memory temporarily)

bash
export NODE_OPTIONS="--max-old-space-size=3072"

Build Excalidraw for production. This compiles and optimizes the application. The build process is CPU and memory intensive and may take 5-10 minutes.

bash
yarn build:app

Step 5: Create Systemd Service

Create systemd service file for Excalidraw

bash
nano /etc/systemd/system/excalidraw.service

Paste the following service configuration

text
[Unit]
Description=Excalidraw Service
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/excalidraw/excalidraw-app/build
ExecStart=/usr/local/bin/serve -s /opt/excalidraw/excalidraw-app/build -l 3000
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Save and exit nano (Ctrl+X, Y, Enter)

Step 6: Enable and Start the Service

Reload systemd daemon to recognize new service file

bash
systemctl daemon-reload

Enable Excalidraw to start automatically on boot

bash
systemctl enable excalidraw

Start the Excalidraw service immediately

bash
systemctl start excalidraw

Check service status and verify it's running

bash
systemctl status excalidraw

Step 7: Verify Installation

Test HTTP access from within container (should see "HTTP/1.1 200 OK")

bash
curl -I http://localhost:3000

Test if your Excalidraw instance is now accessible at (update IP to reflect your LXC IP address):

text
192.168.10.105:3000

Updating Excalidraw

If you need to update Excalidraw in the future, stop the running service before updating.

bash
systemctl stop excalidraw

Navigate to Excalidraw installation directory

bash
cd /opt/excalidraw

Fetch latest changes from GitHub repository

bash
git fetch origin

Check what would be updated

bash
git log HEAD..origin/main --oneline

Pull and merge latest changes

bash
git pull origin main

Update project dependencies

bash
yarn install

Rebuild the application with new code

bash
export NODE_OPTIONS="--max-old-space-size=3072"
yarn build:app

Restart the service with updated version

bash
systemctl start excalidraw

Verify service started successfully

bash
systemctl status excalidraw

Reverse Proxy (Optional if Caddy is Installed)

If you are running Caddy, you should add a new entry for Excalidraw.

Open the Caddyfile

bash
nano /etc/caddy/Caddyfile

Add new block

text
draw.hake.rodeo {
    reverse_proxy 192.168.10.105:3000
}

Run the Caddyfile formatter

bash
caddy fmt --overwrite /etc/caddy/Caddyfile

Then restart the Caddy service

bash
systemctl reload caddy

Gear used