Self-Host openvlt
Get openvlt running on your own hardware in minutes. Your notes stay on your machine as plain markdown files — no cloud, no third parties, no subscriptions.
Quick Install
The fastest way to get started. Works on macOS and Linux. This script installs all dependencies, builds the app, and starts the server.
curl -fsSL https://openvlt.com/install.sh | bash
The install script will:
- Install Node.js 20+ and bun (if not present)
- Clone the repository to
~/.openvlt/app/ - Build the application
- Start the server on port 3456 via pm2
- Set up the
openvltCLI command
Once complete, open http://localhost:3456 to create your account.
Docker
Recommended for VPS and server deployments. The Docker image uses a multi-stage build with node:22-alpine and runs as a non-root user.
services:
openvlt:
build: .
ports:
- "${OPENVLT_PORT:-3456}:3456"
volumes:
- ./data:/app/data
environment:
- NODE_ENV=production
restart: unless-stopped# Clone and start git clone https://github.com/ericvaish/openvlt.git cd openvlt docker compose up -d # Or build and run manually docker build -t openvlt . docker run -d -p 3456:3456 -v openvlt_data:/app/data openvlt
The ./data volume is critical — it contains your vault files and database. Mount it to persist data across container restarts.
Manual Setup
For full control over the setup. Requires Node.js 20+ and bun.
git clone https://github.com/ericvaish/openvlt.git cd openvlt bun install bun run build bun run start
The server starts on port 3456 by default. Set the PORT environment variable to change it. For production, use a process manager like pm2 to handle restarts.
# Install pm2 bun add -g pm2 # Start with pm2 PORT=3456 pm2 start node -- .next/standalone/server.js pm2 save
CLI Commands
If you used the quick install script, the openvlt CLI is available globally.
openvlt start # Start the server (default port 3456) openvlt start 8080 # Start on a custom port openvlt stop # Stop the server openvlt restart # Restart the server openvlt status # Show status and check for updates openvlt update # Pull latest version, rebuild, restart openvlt logs # Show recent logs openvlt logs -f # Follow logs in real-time openvlt uninstall # Remove openvlt (keeps your data)
Configuration
openvlt is configured via environment variables. All settings have sensible defaults — no configuration file is required.
| Variable | Default | Description |
|---|---|---|
| PORT | 3456 | Server listening port |
| HOSTNAME | 0.0.0.0 | Bind address |
| OPENVLT_DB_PATH | data/.openvlt/openvlt.db | SQLite database file path |
| WEBAUTHN_ORIGIN | http://localhost:3456 | WebAuthn origin (must match your domain) |
| WEBAUTHN_RP_ID | localhost | WebAuthn relying party ID (your domain) |
| NODE_ENV | production | Set to production for deployments |
For WebAuthn (biometric login) to work in production, set WEBAUTHN_ORIGIN to your full URL (e.g. https://notes.example.com) and WEBAUTHN_RP_ID to your domain (e.g. notes.example.com).
Directory Structure
All user data lives in the data/ directory. Notes are plain markdown files — you can browse, edit, and back them up with standard tools.
data/
├── vault/
│ └── {userId}/ # Each user gets an isolated directory
│ ├── notes/
│ │ ├── meeting.md # Plain markdown files
│ │ └── ideas.md
│ └── attachments/
│ └── image.png
└── .openvlt/
└── openvlt.db # SQLite metadata & search indexImportant: The markdown files on disk are always the source of truth. SQLite stores metadata, search indexes, and sync state only — never note content.
Database
openvlt uses SQLite in WAL mode with FTS5 for full-text search. The database is created and migrated automatically on first start — no manual setup required.
- Schema is created automatically on first run
- Migrations run automatically on startup
- Default location:
data/.openvlt/openvlt.db - Override with
OPENVLT_DB_PATHenv variable
Security
openvlt is designed for self-hosting with strong security defaults.
User Isolation
Each user's files are scoped to data/vault/{userId}/. The service layer enforces directory boundaries — users cannot access each other's files through the API.
Authentication
Passwords hashed with bcrypt. Optional WebAuthn for biometric login (Touch ID, Face ID, Windows Hello). 24-word recovery key generated at registration. Sessions stored as httpOnly cookies with signed tokens.
End-to-End Encryption
Lock sensitive notes with AES-256-GCM. The encryption key is derived from your lock password via PBKDF2 (100,000 iterations) and never leaves the browser.
Docker
The Docker container runs as a non-root user (UID 1001) with minimal permissions.
Reverse Proxy
For production, put openvlt behind a reverse proxy with HTTPS. Here are example configurations.
notes.example.com {
reverse_proxy localhost:3456
}server {
listen 443 ssl http2;
server_name notes.example.com;
ssl_certificate /etc/letsencrypt/live/notes.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/notes.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3456;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support (for HMR in dev, optional in prod)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}Remember to set WEBAUTHN_ORIGIN and WEBAUTHN_RP_ID to match your domain when using a reverse proxy.
Backups
Since notes are plain files, backing up is straightforward. Back up two things:
# Back up everything rsync -av data/ /path/to/backup/ # Or just the essentials cp data/.openvlt/openvlt.db /path/to/backup/ rsync -av data/vault/ /path/to/backup/vault/
You can also use git, Syncthing, or any file sync tool on the data/vault/ directory since it's just markdown files.
Updating
If you used the quick install script:
openvlt update
For Docker:
git pull docker compose up -d --build
For manual installs:
git pull bun install bun run build bun run start # or: pm2 restart openvlt
Database migrations run automatically on startup — no manual migration step needed.
Need help? hi@ericvaish.com · Open an issue on GitHub