Sign inSign up

junkerderprovinz/excalidraw

By junkerderprovinz

•Updated about 22 hours ago

Self-hosted Excalidraw that keeps to itself: links, collaboration and fonts all stay on your server.

Image
0

5.2K

junkerderprovinz/excalidraw repository overview

excalidraw

Build  Lint  Docker Pulls  Image Size  Arch  Go  nginx  Unraid  License: AGPL-3.0


Excalidraw⁠ is a virtual whiteboard for sketches that look hand-drawn. This is a self-hosted build of it that keeps to itself: shared links, live collaboration, the session scene, the fonts and the icons all come from your own server, and the browser never contacts anyone else. One container, one port, nothing to configure.


A one-knight job: I build it, keep it running, work through the issues and add what people ask for, until nothing is missing. It is free, with no accounts, no telemetry, no ads and no paid tier. No asterisk anywhere. Nothing readable ever leaves your own walls. Forged on evenings and weekends, with heart and stubbornness.

If it has earned a place on your server or computer, toss a coin to your knight: it helps cover the costs and keeps the project alive. It also makes this knight's heart beat a little faster. Three ways below, whichever suits you.


Buy me a coffee   PayPal   Donate with crypto


⁠Table of Contents

  1. What is this?⁠
  2. Screenshots⁠
  3. What it keeps off the internet⁠
  4. How it is built⁠
  5. Quick Start on Unraid⁠
  6. Configuration⁠
  7. Reverse Proxy⁠
  8. Building it yourself⁠
  9. Updating Excalidraw⁠
  10. License⁠
  11. How AI is used here⁠
  12. Support this project⁠

⁠1. What is this?

Excalidraw is excellent, and the published image of it is a plain web server with the app inside. What that image does not tell you is how much of the app still talks to Excalidraw's own infrastructure: a shared link is stored on their server, a live session keeps its scene in Google Firestore, the fonts come from a CDN, and an analytics script loads on every visit.

None of that is a criticism of the project. It is how a free hosted service pays for itself, and all of it is configurable at build time, which is precisely why the published image cannot offer it as a setting.

This image is that build, done differently. Everything above points back at the container, and a gate in the build refuses to produce an image where any of it still points outward.


⁠2. Screenshots

A diagram on the canvas
The whiteboard itself, unchanged: this is Excalidraw, drawing the way it always does.

The share dialog
Both of these now run on your server. The session is end-to-end encrypted, and the key never leaves the link.


⁠3. What it keeps off the internet

WhatUpstreamHere
Shared linksjson.excalidraw.comthis container, in SQLite
Live session sceneGoogle Firestorethis container, in SQLite
Pasted imagesFirebase Storagethis container, in SQLite
Collaboration socketoss-collab.excalidraw.comthis container
Fontsa CDNthis container
Analyticssimpleanalyticscdn.comremoved
Shape librarylibraries.excalidraw.comoff by default, switch below
Text to diagramoss-ai.excalidraw.comoff by default, switch below

The last two are real features rather than telemetry, so they are switches instead of a decision made for you. Off means the request never leaves your server; the feature reports an error rather than pretending to work.

Everything a drawing contains is encrypted in your browser before it is stored, with the key in the part of the link after the #, which browsers never send to a server. The container holds bytes it cannot read.


⁠4. How it is built

Three processes behind one nginx:

  • The app, built from a pinned Excalidraw commit with one file replaced, the one that talks to Firestore. Same encryption, same merge logic when two people draw at once, different destination.
  • The store, a small Go binary over SQLite, serving the scene, room and file endpoints the app expects. The usual self-hosted store is a Node service whose published image has not been rebuilt since February 2022; this one is built here and tested here.
  • The room server, Excalidraw's own relay. It forwards messages between browsers and stores nothing, which is why the store exists.

The addresses in the app are relative paths, not an absolute URL built from a variable you have to set. Your browser resolves them against whatever address you opened, so the same image works on a LAN IP, behind a reverse proxy and under a subdomain with nothing to configure.


⁠5. Quick Start on Unraid

Search for excalidraw in Community Applications, or add the container by hand:

docker run -d \
  --name excalidraw \
  -p 8080:80 \
  -p 8443:443 \
  -v /mnt/user/appdata/excalidraw:/config \
  --restart unless-stopped \
  ghcr.io/junkerderprovinz/excalidraw:latest

Then open https://your-server:8443⁠.

Use the HTTPS port. Live collaboration needs crypto.subtle, which browsers only provide in a secure context, so over plain HTTP the session button fails with a cryptography error. The container generates a self-signed certificate on first start and keeps it in /config, so your browser only has to be told once. The HTTP port stays for the single-user case where no session is ever started.


⁠6. Configuration

VariableDefaultWhat it does
ENABLE_LIBRARYfalseSet to true to let the shape library load from libraries.excalidraw.com.
ENABLE_AIfalseSet to true to let the text-to-diagram feature send your text to oss-ai.excalidraw.com.
STORE_DB/config/store.sqliteWhere drawings, rooms and images are kept.
TZEtc/UTCTime zone for the log.

/config holds the database and the certificate. Back it up and you have backed up everything.


⁠7. Reverse Proxy

Point your proxy at port 80 of the container and let it terminate TLS. The app only ever uses relative paths, so nothing needs to know its own address. Two things the proxy has to allow:

  • WebSocket upgrades on /socket.io/, or live collaboration cannot connect.
  • A body size large enough for a drawing with images, 64 MB matches what the container accepts.

⁠8. Building it yourself

git clone https://github.com/junkerderprovinz/excalidraw.git
cd excalidraw
docker build -t excalidraw .

The build takes a while, because it compiles Excalidraw from source. The last step is the gate: it searches the finished app for every address that should be gone and fails the build if it finds one, so an image that calls home cannot be produced by accident.

The Go store has its own tests:

cd backend && go test ./...

⁠9. Updating Excalidraw

The upstream commit is pinned in the Dockerfile as EXCALIDRAW_SHA, so the image does not change under you. To move it forward, set the new commit and rebuild. If the replaced file has changed upstream, the build fails at the TypeScript step rather than silently shipping a broken whiteboard, and the gate independently checks that the two switchable addresses are still where the runtime expects them.


⁠10. License

This repository is licensed under AGPL-3.0 (see LICENSE⁠).

Excalidraw itself is MIT-licensed⁠ and belongs to the Excalidraw team. The two replaced files in frontend/ are derived from theirs and say so in their headers. This project is not affiliated with or endorsed by Excalidraw.


⁠11. How AI is used here

One knight builds this, and AI is one of the tools I work with, the same way I work with an editor or a compiler. It helps me write code and documentation and it checks my work, and that saves me a good many evenings. It does not make the decisions, though. I read and understand everything before it ships, and if something here breaks, that is on me and not on the tool.

You do not have to take my word for it. The code is open and every release note is written by hand. The issue tracker shows how problems actually get handled, including the ones I got wrong the first time. If you find something that is not right, open an issue and I will look at it.


⁠12. Support this project

Questions via the support thread⁠, bugs, ideas and feature requests via GitHub issues⁠.

A one-knight job: I build it, keep it running, work through the issues and add what people ask for, until nothing is missing. It is free, with no accounts, no telemetry, no ads and no paid tier. No asterisk anywhere. Nothing readable ever leaves your own walls. Forged on evenings and weekends, with heart and stubbornness.

If it has earned a place on your server or computer, toss a coin to your knight: it helps cover the costs and keeps the project alive. It also makes this knight's heart beat a little faster. Three ways below, whichever suits you.

Buy me a coffee   PayPal   Donate with crypto

Tag summary

Content type

Image

Digest

sha256:9e3884b16…

Size

105.7 MB

Last updated

about 22 hours ago

docker pull junkerderprovinz/excalidraw