Composure is a self-hostable, collaborative editor for LaTeX, Typst, Markdown, and more.
Full self-hosting docs are coming soon. In the meantime:
git clone https://github.com/withcomposure/composure
cd composure
docker compose -f docker-compose.yml -f docker-compose.db.yml up --buildComposure supports three deployment styles:
- Hetzner + external PostgreSQL (Neon, RDS, etc.)
- Base compose file only.
docker compose up --build- Hetzner + external PostgreSQL + Cloudflare Pages frontend
- Use the split override to build the API-only backend image target.
docker compose --env-file .env.split -f docker-compose.yml -f docker-compose.split.yml up --build- Self-host everything (local PostgreSQL + backend + frontend)
- Add the DB overlay.
docker compose -f docker-compose.yml -f docker-compose.db.yml up --buildFor split mode, copy .env.split.example to .env.split and set at least:
CORS_ORIGIN=https://your-app.pages.devBACKEND_URL=https://api.yourdomain.comFRONTEND_URL=https://your-app.pages.devAPI_BASE_PATH=/SERVE_FRONTEND=false
Cookie behavior in split mode:
- If
FRONTEND_URLandBACKEND_URLare same-site (for exampleapp.example.com+api.example.com), defaults areSameSite=Laxfor sessions andSameSite=Strictfor guest cookies. - If they are truly cross-site (for example
app.pages.dev+api.yourdomain.com), cookies automatically switch toSameSite=None. - You can override explicitly with
SESSION_COOKIE_SAME_SITEandGUEST_COOKIE_SAME_SITE(strict,lax, ornone). SameSite=Nonerequires HTTPS in production.
Requires Node 24.
The examples below use Caddy environment placeholders (for example {$COMPOSURE_DOMAIN}) so you can avoid hard-coded domains, ports, and upstream URLs.
- Caddy auto-handles websocket upgrades when using
reverse_proxy, so no extra websocket directives are needed for collaboration. - PostgreSQL is not HTTP and should not be proxied through Caddy. Keep it on a private network and expose only backend/frontend HTTP services.
- Keep
API_BASE_PATH(backend) andVITE_API_URL(frontend) aligned.
Use this with:
docker compose -f docker-compose.yml -f docker-compose.db.yml up --buildSERVE_FRONTEND=true
{
email {$ACME_EMAIL}
}
{$COMPOSURE_DOMAIN} {
encode zstd gzip
reverse_proxy {$COMPOSURE_BACKEND_UPSTREAM}
}Typical env values:
COMPOSURE_BACKEND_UPSTREAM=127.0.0.1:8080(orcomposure:8080if Caddy is in the same Docker network)API_BASE_PATH=/api(default) or/
Use this when frontend and backend are separate services:
- app on
app.example.com - API on
api.example.com
{
email {$ACME_EMAIL}
}
{$COMPOSURE_APP_DOMAIN} {
encode zstd gzip
reverse_proxy {$COMPOSURE_FRONTEND_UPSTREAM}
}
{$COMPOSURE_API_DOMAIN} {
encode zstd gzip
reverse_proxy {$COMPOSURE_BACKEND_UPSTREAM}
}Recommended app env alignment:
- Backend:
SERVE_FRONTEND=false - Backend:
CORS_ORIGIN=https://${COMPOSURE_APP_DOMAIN} - Backend:
BACKEND_URL=https://${COMPOSURE_API_DOMAIN} - Backend:
FRONTEND_URL=https://${COMPOSURE_APP_DOMAIN} - Backend:
API_BASE_PATH=/(or/api, but then include that inVITE_API_URL) - Frontend:
VITE_API_URL=https://${COMPOSURE_API_DOMAIN}whenAPI_BASE_PATH=/ - Frontend:
VITE_API_URL=https://${COMPOSURE_API_DOMAIN}/apiwhenAPI_BASE_PATH=/api - Frontend: when
VITE_API_URLis absolute, API requests are sent with credentials so session cookies persist between app/api subdomains. - Cookie defaults are already correct for same-site subdomains. For custom behavior, set
SESSION_COOKIE_SAME_SITE/GUEST_COOKIE_SAME_SITE.
Use this when frontend and backend are separate upstreams but share one public domain.
{
email {$ACME_EMAIL}
}
{$COMPOSURE_DOMAIN} {
encode zstd gzip
@backend path /health /assets/* /api/* /v1/*
handle @backend {
reverse_proxy {$COMPOSURE_BACKEND_UPSTREAM}
}
handle {
reverse_proxy {$COMPOSURE_FRONTEND_UPSTREAM}
}
}Notes:
/api/*covers defaultAPI_BASE_PATH=/api./v1/*coversAPI_BASE_PATH=/.- If you use a custom API prefix like
/my/api, add/my/api/*to@backendand setVITE_API_URL=/my/api.
Useful for split deployments where frontend is on Cloudflare Pages, Vercel, Netlify, etc.
{
email {$ACME_EMAIL}
}
{$COMPOSURE_API_DOMAIN} {
encode zstd gzip
@allowed path /health /assets/* /api/* /v1/*
handle @allowed {
reverse_proxy {$COMPOSURE_BACKEND_UPSTREAM}
}
respond 404
}Recommended backend env alignment:
SERVE_FRONTEND=falseCORS_ORIGIN=https://${COMPOSURE_FRONTEND_ORIGIN}BACKEND_URL=https://${COMPOSURE_API_DOMAIN}FRONTEND_URL=https://${COMPOSURE_FRONTEND_ORIGIN}API_BASE_PATH=/(common for split mode) or/api- Ensure frontend
VITE_API_URLpoints to this API origin (including any API base path). - For cross-site frontend/API pairs, run over HTTPS so
SameSite=Nonecookies are accepted by browsers.
For non-public DNS labs where you still want HTTPS, Caddy can issue internal certs.
{$COMPOSURE_LAN_DOMAIN} {
tls internal
reverse_proxy {$COMPOSURE_BACKEND_UPSTREAM}
}Example:
COMPOSURE_LAN_DOMAIN=composure.home.arpaCOMPOSURE_BACKEND_UPSTREAM=127.0.0.1:8080
If you later move to public DNS, remove tls internal and set ACME_EMAIL to use public ACME certificates.
- User account system
- Profile photos
- Password authentication
- User session management
- Account self-deletion with confirmation
- Second factor authentication (TOTP, WebAuthn)
- Force users to change a temporary password on their next login
- Admin dashboard
- User management (view, search, add, delete, suspend)
- Admins can generate password reset links for users
- Account deletion
- Global or per-user project limits
- Invite link management
- Open and invite-only signup modes
- Compiler concurrency
- Recent jobs monitoring
- Upload size and rate limits
- Server monitoring statistics (beyond recent jobs)
- SMTP configuration for email notifications (emails work, not wired up yet)
- Project creation and deletion
- Search and sorting
- Grid and list views
- New project templates
- Recent activity feed
- Shared projects listing
- Pinned projects listing
- Recently deleted (restore, permanent delete, auto-purge)
- File upload
- Folders
- File move, rename, and delete
- Image/PDF preview
- Multi-line comments
- Comment threads (one level, for now)
- Comment editing and deletion
- Real-time collaboration with multiple users and labeled cursors
- View, comment, and edit collaboration modes
- Granular access control per project, with shareable links
- Presence indicators on the dashboard
- Brace auto-closing, with some LaTeX-aware smarts
- Highlighting text also highlights other occurrences of the same text
- Find and replace
- Suggestion for LaTeX and Typst commands and environments
- File path and bibliography auto-complete
- Multiple cursors (
alt/optto add, andshiftfor rectangular selection) - User snippets library
- Editor tabs
- Split view
- Searchable visual math symbol palette (sensitive to file format)
- Pluggable renderer architecture
- Support for multiple rendering workers to balance load
- Compile LaTeX to PDF preview (
tectonic) - Compile Typst to PDF preview (
typst) - Export to PDF, HTML, Microsoft Word (
pandoc) - Export to ArXiv submission format
- Git integration (persisted bare repo)
- User-generated snapshots with commit messages
- Auto-commit on time interval
- Auto-commit on compile
- Auto-commit on export
- Diff viewer for text files
- Per-file restore
- Point-in-time (full commit) restore
- Remote Git repository sync support (e.g. GitHub, GitLab)
- Light and dark themes
- Full mobile support
- Context-sensitive right-click support (in most places)
- Global toast notification system
- One-click deploy docs
- Quick start / self-hosting guide
- Tech stack blurb and architecture overview
- Code contribution guidelines
- Visual demo or walkthrough
- Configurable CORS origins
Contributions are welcome! Whether it's a bug report, feature suggestion, or pull request, please check out the Contributing Guide to get started.
Composure is available under the MIT License.