The official whiteboard app for Nextcloud. Create and share whiteboards with real-time collaboration.
- 🎨 Drawing shapes, writing text, connecting elements
- 📝 Real-time collaboration with semi-offline support
- 💾 Client-first architecture with local storage
- 🔄 Automatic sync between local and server storage
- 🌐 Works semi-offline - changes saved locally and synced when online (websocker server configured successfully)
- 💪 Built on Excalidraw
Nextcloud Whiteboard uses a client-first architecture that prioritizes browser-based functionality:
- Browser-First: All whiteboard functionality works directly in the browser
- Local Storage: Changes are immediately saved to browser storage (IndexedDB)
- Real-time Collaboration: WebSocket server handles live collaboration sessions
- Simplified Connectivity: Only browsers need to connect to the websocket server
- Reduced Dependencies: Websocket server is only needed for real-time collaboration, not basic functionality
The websocket server handles real-time collaboration sessions between users. Important: The websocket server is only needed for live collaboration - basic whiteboard functionality works without it.
Essential (for real-time collaboration):
- User browsers need HTTP(S) access to the websocket server
- Nextcloud and websocket server share a JWT secret for authentication
Configure Nextcloud with the websocket server details: (Can be configured in the Nextcloud admin settings)
occ config:app:set whiteboard collabBackendUrl --value="https://nextcloud.local:3002"
occ config:app:set whiteboard jwt_secret_key --value="some-random-secret"
npm ci
JWT_SECRET_KEY="some-random-secret" NEXTCLOUD_URL=https://nextcloud.local npm run server:start
docker run -e JWT_SECRET_KEY=some-random-secret -e NEXTCLOUD_URL=https://nextcloud.local -p 3002:3002 --rm ghcr.io/nextcloud-releases/whiteboard:release
Or using Docker Compose:
services:
whiteboard-server:
image: ghcr.io/nextcloud-releases/whiteboard:release
ports:
- "3002:3002"
environment:
NEXTCLOUD_URL: https://nextcloud.local
JWT_SECRET_KEY: some-random-secret
Environment Variables:
JWT_SECRET_KEY
: Must match the secret configured in NextcloudNEXTCLOUD_URL
: Used for JWT token validation (not for server-to-server communication)
If running the websocket server manually, configure your reverse proxy to expose it:
Apache Configuration
Apache >= 2.4.47:
ProxyPass /whiteboard/ http://localhost:3002/ upgrade=websocket
Apache < 2.4.47:
ProxyPass /whiteboard/ http://localhost:3002/
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?whiteboard/(.*) "ws://localhost:3002/$1" [P,L]
Nginx Configuration
location /whiteboard/ {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://localhost:3002/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Other Reverse Proxies
Caddy v2:
handle_path /whiteboard/* {
reverse_proxy http://127.0.0.1:3002
}
Traefik v3:
- traefik.http.services.whiteboard.loadbalancer.server.port=3002
- traefik.http.middlewares.strip-whiteboard.stripprefix.prefixes=/whiteboard
- traefik.http.routers.whiteboard.rule=Host(`nextcloud.example.com`) && PathPrefix(`/whiteboard`)
- traefik.http.routers.whiteboard.middlewares=strip-whiteboard
The websocket server handles real-time collaboration sessions (not critical whiteboard data):
LRU Cache (Default)
- In-memory session storage, simple setup
- Suitable for most deployments
- Session data cleared on restart (whiteboard data remains safe in Nextcloud/local storage)
STORAGE_STRATEGY=lru
Redis
- For multi-server setups or session persistence
- Enables horizontal scaling with Redis Streams
STORAGE_STRATEGY=redis
REDIS_URL=redis://[username:password@]host[:port][/database_number]
For high-traffic environments with multiple websocket servers:
- Use Redis for shared session state
- Configure load balancer with session stickiness
- Redis Streams handles WebSocket scaling automatically
Real-time Collaboration Not Working
- Verify JWT secrets match between Nextcloud and websocket server
- Check that user browsers can access the websocket server URL
- Ensure reverse proxy correctly handles WebSocket upgrades
- Check browser console for connection errors
Legacy Integration App Conflict
If you previously had integration_whiteboard
installed, remove any whiteboard entries from config/mimetypealiases.json
and run:
occ maintenance:mimetype:update-db
occ maintenance:mimetype:update-js
Misleading Admin Errors Admin connectivity checks may show false negatives in Docker/proxy environments. These errors don't affect actual functionality since the architecture is client-first. Focus on browser-based connectivity tests instead.
To build the project locally:
npm ci
npm run build
For development with hot reload:
npm run watch
To run the websocket server in development:
npm run server:watch