Nexo is a self-hosted alternative to Notion. Organize documents, build flexible databases with multiple views (table, board, calendar, gallery), and automate workflows — all under your control.
- Go 1.22+
- SQLite (default) or PostgreSQL
go build -o nexo ./cmd# Minimal — SQLite, required vars only
SESSION_SECRET_KEY="your-secret-at-least-32-chars-long" ./nexo server
# With config file
./nexo server --config configAll options can be set via environment variable, CLI flag, or config.yaml.
Priority: env var > CLI flag > config file > default.
| Env var | Description |
|---|---|
SESSION_SECRET_KEY | JWT signing key. Minimum 32 characters. The server refuses to start if this is shorter or unset. Generate with: openssl rand -base64 48 |
| Env var | CLI flag | Default | Description |
|---|---|---|---|
HTTP_PORT | --http.port | 8080 | Listening port |
HTTP_LOGS | --http.logs | false | Enable HTTP access logs |
HTTP_CORS_ALLOW_ORIGINS | --http.cors_allow_origins | * | Comma-separated list of allowed CORS origins. Set this in production (e.g. https://app.example.com). Use * only for local dev. |
| Env var | CLI flag | Default | Description |
|---|---|---|---|
DATABASE_DIALECT | --database.dialect | sqlite | sqlite or postgres |
DATABASE_DSN | --database.dsn | ./database.sqlite | SQLite file path or PostgreSQL DSN |
| Env var | CLI flag | Default | Description |
|---|---|---|---|
SESSION_SECRET_KEY | --session.secret_key | (none) | Required. ≥ 32 chars |
SESSION_EXPIRATION_MINUTES | --session.expiration_minutes | 43200 (30 days) | Token lifetime in minutes |
SESSION_ISSUER | --session.issuer | nexo | JWT iss claim |
| Env var | CLI flag | Default | Description |
|---|---|---|---|
LOGGER_LEVEL | --logger.level | info | debug, info, warn, error |
LOGGER_PRETTY | --logger.pretty | false | Human-readable logs (dev only) |
http:
port: 8080logs: truecors_allow_origins: "https://app.example.com"logger:
level: infopretty: falsedatabase:
dialect: sqlitedsn: ./database.sqlitesession:
# Required — do not commit real values to source control# Generate: openssl rand -base64 48secret_key: "CHANGE_ME_AT_LEAST_32_CHARACTERS_LONG"expiration_minutes: 43200issuer: nexoSee config-example.yaml for a minimal working example.
docker build -t nexo .
docker run -p 8080:8080 \
-e SESSION_SECRET_KEY="$(openssl rand -base64 48)" \
-e HTTP_CORS_ALLOW_ORIGINS="https://app.example.com" \
-e DATABASE_DSN="/data/nexo.sqlite" \
-v nexo_data:/data \
nexoOr with docker-compose.yaml:
# Copy and fill in secrets
cp .env.example .env
docker compose up -dThe collaboration endpoint at /ws/collab/<roomId> requires a valid JWT passed as the token query parameter. Every connection is authorized against the resource identified by the room ID:
| Room prefix | Resource checked |
|---|---|
document:{id} | Document permissions |
drawing:{id} | Drawing → space permissions |
row:{dbId}:{rowId} | Database → space permissions |
Connections with an invalid token, unknown room format, or insufficient permissions are rejected.
MIT