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.
Stack
Next.js 16
Database
SQLite
Port
3456
Storage
.md files
Quick Install
The fastest way to get started. Works on macOS and Linux. One command installs everything and starts the server.
curl -fsSL https://openvlt.com/install.sh | bash
What the script does:
Installs Node.js 22+ and bun if not already present
Clones the repo to ~/.openvlt/app/ and builds the application
Starts the server on port 3456 via pm2 and sets up the openvlt CLI command
http://localhost:3456 to create your account.Docker
recommended for serversBest for VPS and server deployments. The image uses a multi-stage build with node:22-alpine and runs as a non-root user.
services:
openvlt:
build: .
container_name: openvlt
restart: unless-stopped
ports:
- "${OPENVLT_PORT:-3456}:3456"
volumes:
- ./data:/app/data
environment:
- NODE_ENV=production
- PORT=3456
- OPENVLT_DB_PATH=/app/data/.openvlt/openvlt.db# 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
./data volume is critical. It contains your vault files and database. Always mount it to persist data across container restarts.Manual Setup
For full control over the setup. Requires Node.js 22+ 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 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
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 |
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 any 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 indexDatabase
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.
Auto-created
Schema is created on first run
Auto-migrated
Migrations run on every startup
Default path
data/.openvlt/openvlt.db
Override
OPENVLT_DB_PATH env variable
Security
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 (12 rounds). 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 container runs as a non-root user (UID 1001) with minimal permissions.
Reverse Proxy
For production, put openvlt behind a reverse proxy with HTTPS.
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";
}
}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 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/
data/vault/ directory since it's just markdown files.Updating
Quick Install
openvlt updateDocker
git pull && docker compose up -d --buildManual
git pull && bun install && bun run buildNeed help? [email protected] · Open an issue on GitHub