ExcaliDash
A self-hosted dashboard and organizer for Excalidraw with multi-user collaboration and scoped sharing.
Install / Use
/learn @ZimengXiong/ExcaliDashREADME
ExcaliDash
A self-hosted dashboard and organizer for Excalidraw with live collaboration features.

Table of Contents
Features
<details> <summary>Persistent storage for all your drawings</summary>

Sign in with OIDC

Migration from v0.3

Admin Bootstrap

Admin Dashboard




Excalidash uses a non-proprietary archival format that stores your drawings in plain .excalidraw format

Upgrading
See release notes for a specific release.
ExcaliDash includes an in-app update notifier that checks GitHub Releases. If your deployment must not make outbound network calls, disable it on the backend:
UPDATE_CHECK_OUTBOUND=false
Docker Hub Upgrades
If you deployed using docker-compose.prod.yml (Docker Hub images), upgrade by pulling the latest images and recreating containers:
docker compose -f docker-compose.prod.yml pull && \
docker compose -f docker-compose.prod.yml up -d
If you prefer a clean stop/start (more downtime, but simpler), you can do:
docker compose -f docker-compose.prod.yml down && \
docker compose -f docker-compose.prod.yml pull && \
docker compose -f docker-compose.prod.yml up -d
Notes:
- Don’t add
-vtodownunless you intend to delete the persistent backend volume (your SQLite DB + secrets). - Only add
--remove-orphansif you previously ran a different Compose file for the same project name and need to remove old/renamed services.
Installation
[!CAUTION] This is a BETA deployment and production-readiness depends on deployment controls: use TLS, trusted reverse proxy, fixed secrets, backups, and endpoint rate limits.
[!CAUTION] ExcaliDash is in BETA. Please backup your data regularly.
Quickstart
Prereqs: Docker + Docker Compose v2.
<details> <summary>Docker Hub (Recommended)</summary>Docker Hub (Recommended)
# Download docker-compose.prod.yml
curl -OL https://raw.githubusercontent.com/ZimengXiong/ExcaliDash/main/docker-compose.prod.yml
# Pull images
docker compose -f docker-compose.prod.yml pull
# Run container
docker compose -f docker-compose.prod.yml up -d
# Access the frontend at localhost:6767
For single-container deployments, JWT_SECRET can be omitted and will be auto-generated and persisted in the backend volume on first start. For portability and most production deployments, set a fixed JWT_SECRET explicitly.
By default, the provided Compose files set TRUST_PROXY=false for safer setup. Only set TRUST_PROXY to a positive hop count (for example, 1) when requests always pass through a trusted reverse proxy that correctly sets forwarded headers.
Docker Build
# Clone the repository (recommended)
git clone git@github.com:ZimengXiong/ExcaliDash.git
# or, clone with HTTPS
# git clone https://github.com/ZimengXiong/ExcaliDash.git
docker compose build
docker compose up -d
# Access the frontend at localhost:6767
</details>
Advanced
<details> <summary>Reverse Proxy / Traefik</summary>When running ExcaliDash behind Traefik, Nginx, or another reverse proxy, configure both containers so that API + WebSocket calls resolve correctly:
| Variable | Purpose |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| FRONTEND_URL | Backend allowed origin(s). Must match the public URL users access (for example https://excalidash.example.com). Supports comma-separated values for multiple addresses. |
| TRUST_PROXY | Set to 1 when traffic passes through one trusted reverse-proxy hop (for example frontend nginx -> backend) and headers are sanitized. |
| BACKEND_URL | Frontend container-to-backend target used by Nginx. Override when backend host differs from default service DNS/host. |
# docker-compose.yml example
backend:
environment:
# Single URL
- FRONTEND_URL=https://excalidash.example.com
# Trust exactly one reverse-proxy hop
- TRUST_PROXY=1
# Or multiple URLs (comma-separated) for local + network access
# - FRONTEND_URL=http://localhost:6767,http://192.168.1.100:6767,http://nas.local:6767
frontend:
environment:
# For standard Docker Compose (default)
# - BACKEND_URL=backend:8000
# For Kubernetes, use the service DNS name:
- BACKEND_URL=excalidash-backend.default.svc.cluster.local:8000
</details>
<details>
<summary>Scaling / HA (Current Limitations)</summary>
ExcaliDash currently supports running one backend instance.
Why:
| Area | Limitation |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Database | The backend uses a local SQLite file database by default (DATABASE_URL=file:/.../dev.db). Running multiple backend replicas either creates split-brain state (separate DB files/volumes) or requires sharing a single SQLite file across hosts, which is not a reliable deployment pattern. |
| Collaboration | Real-time presence state is tracked in-memory in the backend process, so multiple replicas will fragment presence/collaboration unless a shared Socket.IO adapter is added. |
Recommended deployment pattern:
| Component | Guidance | | --------- | ----------------------------------------------------------------------- | | Backend | 1 replica, persistent volume, regular backups. | | Frontend | 1 replica is simplest; scaling is generally fine since it is stateless. |
</details> <details> <summary>Auth, Onboarding, and First Admin Setup</summary>ExcaliDash supports local login and OIDC, and includes a one-time first-admin bootstrap key to protect initial setup/migration flows.
Auth modes:
| AUTH_MODE | Behavior |
| ----------------- | -------------------------------------------------------------- |
| local (default) | Native email/password login only. |
| hybrid | Native login plus OIDC login. |
| oidc_enforced | OIDC-only login (/auth/register and /auth/login disabled). |
If you upgrade and see an onboarding/setup flow, follow the UI. For emergency-only operator access, you can temporarily bypass the onboarding gate:
DISABLE_ONBOARDING_GATE=true docker compose -f docker-compose.prod.yml up -d
One-time first-admin bootstrap setup code (local auth only):
| What | Notes |
| ---------------- | ------------------------------------------------------------------------------------ |
| When required | Auth enabled and no active users (fresh install or certain migrations). |
| Where to find it | Backend logs: [BOOTSTRAP SETUP] One-time admin setup code .... |
| Behavior | Single-use; if you enter an invalid/expired code, check logs for the refreshed code. |
Find the current code in logs:
docker compose -f docker-compose.prod.yml logs backend --tail=200 | grep "BOOTSTRAP SETUP"
OIDC configuration (for hybrid / oidc_enforced) requires these backend env vars:
backend:
environment:
- AUTH_MODE=oidc_enforced
- OIDC_PROVIDER_NAME=Authentik
- OIDC_ISSUER_URL=https://auth.example.com/application/o/excalidash/
- OIDC_CLIENT_ID=your-client-id
# Optional for public clients; required for confidential clients
# -
