Self-hosted service
ZimengXiong/ExcaliDash avatar
ZimengXiong/ExcaliDash

ExcaliDash: a self-hosted Excalidraw dashboard with OIDC, version history and scoped sharing

A self-hosted dashboard and organizer for Excalidraw with multi-user collaboration and scoped sharing.

1,558 stars152 forksTypeScriptLGPL-3.0

At a glance

What is it?
ExcaliDash wraps the Excalidraw editor in a self-hosted server that adds accounts, collections, snapshots and live collaboration. It is a BETA product with a short install path and a long list of deployment conditions you have to satisfy yourself.
Who is it for?
Adopt ExcaliDash if you already run Docker Compose, want Excalidraw drawings stored on your own host, and can supply TLS, a reverse proxy, fixed secrets and backups yourself. Do not adopt it if you need a supported, stable release or if you cannot commit to upgrading a BETA service.
Can I use it commercially?
Yes, with conditions. LGPL-3.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap ExcaliDash fills between the Excalidraw editor and a shared drive

Excalidraw is an editor. It draws, it exports, and it hands you a file. What it does not give you is a server that remembers who owns which drawing, who else may open it, or what the drawing looked like three edits ago. Teams that want that usually end up with a folder of .excalidraw files on a shared drive and a verbal agreement about naming.

ExcaliDash is aimed at that gap. The README describes it as a self-hosted dashboard and organizer for Excalidraw with live collaboration features, and the feature list is the shape of the missing server: persistent storage for drawings, real time collaboration, version history and restore, optional multi-user authentication with OIDC, scoped internal and external sharing, search, drag-and-drop collections, and export/import for backup.

The audience is whoever already decided the drawings should live on infrastructure they control. That is a narrower group than the one that just wants to sketch something. If you are a single person drawing diagrams occasionally, a local Excalidraw session and a folder is less work than a Compose stack. ExcaliDash starts to pay for itself when several people share a library and someone has to answer the question of who can see what.

What actually runs: a TypeScript frontend, a Prisma-backed backend and a WebSocket channel

The repository is TypeScript on both sides. The development instructions split the project into frontend and backend directories, each with its own package.json and its own .env file. The backend uses Prisma, and the setup sequence in the README is npx prisma generate followed by npx prisma db push, which means the schema is applied directly to the database rather than through a migration history you review before applying.

Persistence is a backend volume. The upgrade notes are explicit that the persistent backend volume holds your SQLite DB and secrets, and warn against adding -v to docker compose down unless you intend to delete that volume. So the default data store is SQLite, with the database file living inside the container volume. The README points to docs/DEPLOYMENT.md for database provider details, which implies other providers are supported, but the Compose path described in the root README is the SQLite one.

Collaboration is the part that needs a live channel. The repository topics include websocket, and the README advertises real time collaboration with a demo recording. The README does not document the protocol, the message shapes, or how conflicts between two simultaneous editors are resolved. That is a real gap if you are evaluating it for a team that edits the same drawing at the same time. The feature exists; the failure semantics are not written down where an operator can read them.

Version history is described as automatic retention of recent drawing snapshots, with preview from the editor and restore of a previous state. The word recent is doing work there. The README does not state how many snapshots are kept, how often they are taken, or whether the retention window is configurable. Treat the feature as present and its bounds as unspecified.

Getting a working instance: the Compose commands and the environment keys that matter

The quickstart assumes Docker and Docker Compose v2. The recommended path pulls the published image rather than building:

curl -OL https://raw.githubusercontent.com/ZimengXiong/ExcaliDash/main/docker-compose.prod.yml docker compose -f docker-compose.prod.yml pull docker compose -f docker-compose.prod.yml up -d

The frontend is then reachable at localhost:6767. If you would rather build from source, the alternative is to clone the repository and run docker compose build followed by docker compose up -d.

Three configuration decisions are called out in the README and each one has an operational consequence.

JWT_SECRET. In a single-container deployment it can be omitted; the backend generates one on first start and persists it in the backend volume. The README recommends setting it explicitly for portability and most production deployments. The reason is straightforward: a secret that lives only in one volume does not survive a rebuild of that volume, and every existing session token becomes invalid the moment it changes.

TRUST_PROXY. The supplied Compose files set TRUST_PROXY=false by default. The README says to set it to a positive hop count, for example 1, only when requests always pass through a trusted reverse proxy that correctly sets forwarded headers. Setting it when that is not true means the application trusts client-supplied forwarding headers, which affects how it identifies request origins.

UPDATE_CHECK_OUTBOUND. ExcaliDash has an in-app update notifier that checks GitHub Releases. If your deployment must not make outbound network calls, set UPDATE_CHECK_OUTBOUND=false on the backend. This is the one setting here that is about egress rather than ingress, and it is easy to miss on an air-gapped host.

For anything beyond these three, the README defers to docs/CONFIGURATION.md for the full environment-variable reference and docs/DEPLOYMENT.md for reverse proxy, auth/OIDC, database provider, offline, backup and password policy details. The root README is deliberately short; the operational surface is in those two documents.

Upgrading, the in-app update notifier and what the BETA label costs you

Upgrading a Docker Hub deployment is a pull and a recreate:

docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d

The README offers a stop/start variant with more downtime and fewer moving parts. Two notes accompany it. Do not add -v to down unless you intend to delete the persistent backend volume, which contains the SQLite database and the secrets. Only add --remove-orphans if you previously ran a different Compose file under the same project name and need to clear out renamed services.

There is no migration tooling described for upgrades. The README says to see the release notes for a specific release, and the release history shows why that matters: v0.6.0 is a normal release, while v0.6.1-dev.e11e820 and v0.6.2-dev.fcc3c39 are development builds published within hours of it. If you pull a floating tag you may land on a dev build. Pin a specific tag if you care which one you are running.

The BETA warnings are unusually direct for a README. One caution states that production-readiness depends on deployment controls: TLS, a trusted reverse proxy, fixed secrets, backups, and endpoint rate limits. Another states plainly that ExcaliDash is in BETA and asks you to back up your data regularly. Read together, these say the project is handing you the controls and not the guarantees. There is no mention of an audit, a security review, or a stability commitment. The upgrade path is a container recreate, and the data path is a single volume you are responsible for copying.

Where the documentation is thin, and what that means before you commit

Three areas are named in the feature list and left largely unexplained in the material available.

Scoped sharing. The README shows a screenshot labelled scoped internal and external sharing but does not define the scope model. Internal versus external suggests a boundary between authenticated users and link-based access, but whether external links expire, whether they are revocable, and whether they can be read-only is not stated. If external sharing is the reason you are considering ExcaliDash, that is the first thing to check in docs/DEPLOYMENT.md.

Snapshot retention. Automatic retention is advertised; the retention policy is not. An operator planning storage capacity has no number to work with.

Conflict behaviour. Real time collaboration is advertised; the merge or locking strategy is not. Two people on the same drawing is the normal case for this product, and the README does not describe what happens when they overlap.

None of these are reasons to reject the project. They are reasons to treat the README as a feature inventory rather than an operations manual, and to read the two linked documents before you put anything real into it.

One design choice deserves credit. The export format is plain .excalidraw files. The README describes it as a non-proprietary archival format. That means your exit path is the same as your entry path, and a backup is something you can open in the upstream editor without ExcaliDash running. For a BETA self-hosted tool, that is the single most reassuring thing in the repository.

ExcaliDash against running Excalidraw with your own sync layer

The obvious alternative is not another dashboard. It is the upstream Excalidraw editor plus whatever storage and sharing you already have: a Git repository of .excalidraw files, a Nextcloud or S3 bucket, or a shared drive with a naming convention.

The difference is where identity lives. With a file-based approach, access control is the filesystem's or the sync provider's. Permissions are per folder, versions are commits or file history, and collaboration is whatever the sync layer does when two people save the same file. Nothing in that stack knows what a drawing is.

ExcaliDash moves that into the application. It has users, an admin dashboard, an admin bootstrap flow, OIDC sign-in, collections, search, and per-drawing sharing scopes. The trade is that you now operate a service with a database, a JWT secret, a session model and a proxy configuration, instead of a folder. You also inherit its BETA status, which a Git repository does not have.

There is a middle path the README itself supports: the export format is plain .excalidraw, so ExcaliDash can be the collaboration and permission layer while your archive stays in a format the upstream editor reads. That is the arrangement worth considering if you want the shared library but are not ready to make ExcaliDash the only place the drawings exist.

Maintenance cost, the LGPL-3.0 licence, and the verdict

The recurring work is small but real. You pull a new image and recreate the container. You keep the backend volume backed up, because it holds the SQLite database and the secrets, and losing it means losing both the drawings and the sessions. You watch which tag you are on, since dev builds are published alongside releases. If you run OIDC, you maintain that integration. If you run behind a proxy, you keep TRUST_PROXY aligned with the actual hop count, because it is a fact about your network, not a setting you pick once and forget.

ExcaliDash is licensed LGPL-3.0. That is a copyleft licence with a linking exception for libraries. The practical point for most readers: if you run it as a self-hosted service, you are using it, not distributing it. If you modify it and distribute the modified work, the licence terms apply to that distribution. This is a summary of the identifier, not legal advice. Check the full licence text and your own counsel if you plan to ship a modified version.

The judgement is narrow. Adopt ExcaliDash if you already run Docker Compose, want Excalidraw drawings on your own host, and accept that you are supplying TLS, a reverse proxy, fixed secrets, backups and rate limits yourself. Do not adopt it if you need a stable, supported release, or if nobody on the team owns the upgrade. Before you put real drawings in, verify three things: that JWT_SECRET is set explicitly rather than auto-generated, that TRUST_PROXY matches your actual proxy topology, and that UPDATE_CHECK_OUTBOUND is false if the host must not call out. Then read docs/DEPLOYMENT.md for the scoped-sharing semantics, because that is the feature the README shows and does not define.

Editorial conclusion

Adopt ExcaliDash if you already run Docker Compose, want Excalidraw drawings stored on your own host, and can supply TLS, a reverse proxy, fixed secrets and backups yourself. Do not adopt it if you need a supported, stable release or if you cannot commit to upgrading a BETA service. Verify first that JWT_SECRET is set explicitly, that TRUST_PROXY matches your actual proxy hop count, and that UPDATE_CHECK_OUTBOUND is set to false if your deployment must not make outbound calls.

Official sources

  1. License: LGPL-3.0
  2. Project website
  3. README
  4. Releases
  5. ZimengXiong/ExcaliDash on GitHub
Community notes

Community notes