- Java 21+
- Clojure CLI 1.12+
- Node.js / npm (for Tailwind CSS)
# Clone with submodules
git clone --recurse-submodules <repo-url>
# Or if already cloned, init submodules
git submodule update --init --recursive
# Install npm dependencies (Tailwind CSS)
npm install
# Create .env from example
cp .env.example .envEdit .env as needed. The defaults work for local development; GitHub OAuth credentials are optional.
Start the nREPL:
clojure -M:devThen in the REPL:
(require 'dev)
(dev/start)This starts:
- Web server on http://localhost:3000
- Tailwind CSS watcher (recompiles on class changes)
- Live reload via file watcher — editing
.clj,.css, or.mdfiles triggers an automatic browser refresh
Other REPL commands:
(dev/stop) ; stop everything
(dev/restart) ; full restart
(dev/reload!) ; manually trigger a browser refreshnpm run css:buildBuild a standalone jar for small VMs:
npm run css:build
clojure -T:build uberThis produces:
target/datalevin-docs-standalone.jarThe uberjar avoids resolving the production classpath on every boot and is the preferred deployment target on a 1 GB VM.
Start the docs site from the built uberjar with explicit JVM heap limits:
clojure -T:build uber
scripts/start-prod.shSet ENV=prod and a real SESSION_SECRET before starting. Startup now fails fast if ENV=prod and SESSION_SECRET is missing.
In production, startup also fails fast unless outbound email is configured with MAIL_FROM and SMTP_HOST.
That wrapper runs the same jar-based startup pattern as the systemd unit:
java -Xms256m -Xmx384m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication -XX:MaxMetaspaceSize=128m -jar target/datalevin-docs-standalone.jarOn small VMs, setting -Xms and a conservative -Xmx avoids the JVM expanding until the host OOM killer intervenes while still leaving more room for Datalevin's mmap usage. Provision at least 1 GB of swap on these hosts so short bursts of heap or page-cache pressure do not immediately turn into an OOM kill. The prod wrapper also defaults to G1 with string deduplication and a 128 MB metaspace cap. You can override the defaults if needed:
JAVA_XMS=256m JAVA_XMX=768m scripts/start-prod.shIf you want a lower-overhead collector on a very small VM, switch the wrapper to Serial GC:
JAVA_GC=serial scripts/start-prod.shOverride APP_JAR if the jar lives outside the repo checkout:
APP_JAR=/opt/datalevin-docs/datalevin-docs-standalone.jar scripts/start-prod.shAn example unit file is included at deploy/systemd/datalevin-docs.service.
It keeps MemoryHigh=800M and OOMPolicy=stop, while relying on the JVM heap limits and Datalevin's own mapsize instead of a hard MemoryMax cap.
Typical install steps:
sudo install -d /opt/datalevin-docs /etc/datalevin-docs
sudo install -m 0644 target/datalevin-docs-standalone.jar /opt/datalevin-docs/datalevin-docs-standalone.jar
sudo install -m 0644 deploy/systemd/datalevin-docs.service /etc/systemd/system/datalevin-docs.serviceCreate /etc/datalevin-docs/datalevin-docs.env with at least:
ENV=prod
PORT=3000
BASE_URL=https://docs.example.com
DB_PATH=/var/lib/datalevin-docs/data
SESSION_SECRET=replace-with-a-real-secret
MAIL_FROM="Datalevin Docs <noreply@example.com>"
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=smtp-user
SMTP_PASS=smtp-password
SMTP_TLS=trueWithout SMTP config, development keeps logging verification and reset links to the console instead of sending email.
Then enable the service:
sudo systemctl daemon-reload
sudo systemctl enable --now datalevin-docs