Open-source file storage and knowledge platform. A self-hostable alternative to Dropbox and Google Drive — with built-in search, document transcription, and an AI-powered knowledge base.
- File Explorer — Upload, organize, rename, move, and delete files and folders with grid and list views
- File Previews — In-app viewers for PDFs, images, Markdown, CSV, audio, video, and plain text
- Tags — Organize files with color-coded, workspace-scoped tags and filter by them
- Share Links — Generate shareable links with optional password protection, expiration, and download limits
- Upload Links — Let others upload files to your storage without an account
- Command Palette — Cmd+K search across file names and content with keyboard navigation
- Knowledge Base — AI-powered wiki that ingests tagged documents, supports chat, and visualizes page relationships in an interactive graph view
- Plugins — Extensible plugin system with built-in plugins for search (QMD, FTS), document transcription, Google Drive sync, and knowledge base
- Document Transcription — AI-powered OCR/transcription for images and PDFs, making non-text files searchable
- Notifications — In-app notifications for workspace invites and announcements
- Workspace Invites — Invite users via email with token-based onboarding flow
- Multi-Store Storage — Attach multiple storage backends (Local, S3, R2, Vercel Blob) per workspace with automatic replication across stores
- Read-Only Ingest — Scan external buckets or directories for new files and import them into Locker without moving originals
- Storage Quotas — Per-user storage limits with usage tracking
- Virtual Bash Filesystem — Traverse workspace files with
ls,cd,find,cat,grep, etc. viajust-bash
- Framework: Next.js 16 (App Router, Turbopack)
- Monorepo: Turborepo + pnpm workspaces
- Database: PostgreSQL 16 + Drizzle ORM
- API: tRPC 11 (end-to-end type safety)
- Auth: BetterAuth (email/password, Google OAuth)
- AI: Vercel AI SDK for transcription and knowledge base chat
- UI: Tailwind CSS 4, Radix UI, Geist fonts, Lucide icons
- Email: Resend
- Testing: Playwright (E2E)
- Language: TypeScript (strict mode)
locker/
├── apps/web/ Next.js web app
│ ├── app/ Pages and API routes
│ ├── components/ Shared UI components
│ ├── features/ Feature modules (files, knowledge-bases)
│ ├── server/ tRPC routers, auth, and plugin system
│ └── lib/ Utilities
├── packages/
│ ├── common/ Shared types, validation, constants
│ ├── database/ Drizzle schema and database client
│ ├── email/ Email templates and sending (Resend)
│ └── storage/ Storage provider adapters
├── services/
│ ├── fts/ Full-text search microservice (SQLite FTS5)
│ └── qmd/ Semantic search microservice
├── e2e/ Playwright end-to-end tests
├── docker-compose.yml PostgreSQL + optional search services
└── turbo.json Build pipeline
pnpm installdocker compose up -dcp .env.example .envThe defaults work out of the box for local development. Edit .env to change the storage provider or add OAuth credentials.
pnpm db:generate
pnpm db:migratepnpm devOpen http://localhost:3000, create an account, and start uploading files.
Locker auto-detects which platform it's running on and adjusts its capabilities accordingly. Persistent runtimes (Docker, Fly.io, Railway) support all features. Serverless runtimes (Vercel) disable long-running operations like store sync and bulk KB ingestion.
Every deployment needs:
- PostgreSQL 16+ — the primary database
BETTER_AUTH_SECRET— generate withopenssl rand -base64 32NEXT_PUBLIC_APP_URL— the public URL of your deployment- A storage provider — local filesystem (persistent runtimes only) or a cloud provider (S3, R2, Vercel Blob)
The included docker-compose.yml bundles PostgreSQL, migrations, and the web app. This is the fastest way to self-host.
# 1. Clone and configure
git clone https://github.com/zmeyer44/Locker.git && cd Locker
cp .env.example .env
# 2. Set the required secret
# Replace the placeholder in .env or export it:
export BETTER_AUTH_SECRET=$(openssl rand -base64 32)
# 3. Start everything
docker compose up -dOpen http://localhost:3000. Files are stored on a Docker volume (blob_data) by default.
To use S3 or R2 instead of local storage, set BLOB_STORAGE_PROVIDER and the matching credentials in your .env before starting.
To enable optional search services:
docker compose --profile search up -dRailway provides managed PostgreSQL and persistent disk, so all features work out of the box.
-
Create a new project and add a PostgreSQL service. Copy the
DATABASE_URL. -
Add a new service from the Locker repo. Railway will detect the
Dockerfileautomatically. -
Set these environment variables on the web service:
Variable Value DATABASE_URLThe PostgreSQL connection string from step 1 BETTER_AUTH_SECRETopenssl rand -base64 32BETTER_AUTH_URLYour Railway public URL (e.g. https://locker-production.up.railway.app)NEXT_PUBLIC_APP_URLSame as BETTER_AUTH_URLBLOB_STORAGE_PROVIDERlocal(default) ors3/r2with matching credentials -
If using local storage, attach a volume mounted at
/app/local-blobs. -
Deploy. Railway runs migrations automatically via the
migrateservice in Docker Compose, or you can add a deploy command:pnpm db:migrate && node apps/web/server.js.
Fly provides persistent VMs with attached volumes.
-
Create a PostgreSQL cluster:
fly postgres create --name locker-db
-
Launch the app from the repo root:
fly launch --dockerfile Dockerfile
-
Attach the database:
fly postgres attach locker-db
-
Create a volume for local storage:
fly volumes create blob_data --size 10 --region your-region
Add to
fly.toml:[mounts] source = "blob_data" destination = "/app/local-blobs"
-
Set secrets:
fly secrets set \ BETTER_AUTH_SECRET=$(openssl rand -base64 32) \ BETTER_AUTH_URL=https://your-app.fly.dev \ NEXT_PUBLIC_APP_URL=https://your-app.fly.dev
-
Run migrations and deploy:
fly deploy
- Create a PostgreSQL database in Render. Copy the internal connection string.
- Create a new Web Service from the Locker repo. Set the Dockerfile path to
./Dockerfile. - Add a Disk mounted at
/app/local-blobsfor local file storage. - Set environment variables:
DATABASE_URL,BETTER_AUTH_SECRET,BETTER_AUTH_URL,NEXT_PUBLIC_APP_URL. - Deploy.
Vercel runs Next.js on serverless functions. Locker auto-detects this and disables features that require a persistent runtime:
- Store sync and ingest are disabled
- Bulk KB ingestion is disabled
- Local filesystem storage is unavailable
You must use a cloud storage provider (Vercel Blob, S3, or R2).
-
Import the repo into Vercel.
-
Set the framework preset to Next.js and the root directory to
apps/web. -
Add a PostgreSQL database (Vercel Postgres, Neon, Supabase, etc.) and set
DATABASE_URL. -
Set environment variables:
Variable Value BETTER_AUTH_SECRETopenssl rand -base64 32BETTER_AUTH_URLYour Vercel deployment URL NEXT_PUBLIC_APP_URLSame as BETTER_AUTH_URLBLOB_STORAGE_PROVIDERvercel(recommended) ors3/r2BLOB_READ_WRITE_TOKENFrom Vercel Blob (if using vercelprovider) -
Deploy. Run migrations manually or via a build command:
pnpm db:migrate && pnpm build.
Locker detects the hosting platform automatically from environment variables (VERCEL, FLY_REGION, RAILWAY_ENVIRONMENT, etc.). If detection is wrong or you want to test serverless behavior locally, set:
LOCKER_RUNTIME_ENV=vercel # or: docker, fly, railway, render, developmentSet BLOB_STORAGE_PROVIDER in .env to configure the default storage backend for new workspaces:
| Provider | Value | Required Env Vars |
|---|---|---|
| Local filesystem | local |
LOCAL_BLOB_DIR |
| AWS S3 | s3 |
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, S3_BUCKET |
| Cloudflare R2 | r2 |
R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET |
| Vercel Blob | vercel |
BLOB_READ_WRITE_TOKEN |
Workspace admins can attach additional stores from Settings > Stores. Each store can be configured as:
- Writable replica — files are automatically synced to this store for redundancy
- Read-only ingest — Locker scans the store for new files and imports them without moving the originals
Stores use their own credentials (BYOB), so different teams can bring different backends. A primary store is designated per workspace for new uploads; replicas receive copies automatically.
Locker ships with a built-in plugin system. Plugins are installed per-workspace and can add search, transcription, file actions, and more.
| Plugin | Description |
|---|---|
| QMD Search | Semantic document search powered by QMD embeddings |
| Full-Text Search | Lightweight SQLite FTS5 search — no external service required |
| Document Transcription | AI-powered OCR for images and PDFs, making non-text files searchable |
| Google Drive Sync | Import files from and export files to Google Drive |
| Knowledge Base | Build an AI-powered wiki from tagged documents with chat and graph view |
QMD and FTS run as standalone microservices. Enable them with the search Docker Compose profile:
docker compose --profile search up -d| Command | Description |
|---|---|
pnpm dev |
Start all packages in dev mode |
pnpm build |
Production build |
pnpm typecheck |
Type-check all packages |
pnpm lint |
Lint all packages |
pnpm db:generate |
Generate a new Drizzle migration |
pnpm db:migrate |
Apply pending migrations |
pnpm db:seed |
Seed the database |
pnpm format |
Format code with Prettier |
Locker includes a read-only virtual filesystem over workspace files/folders, powered by just-bash.
The shell API is available on tRPC router vfsShell:
vfsShell.createSession({ cwd? })→ create a workspace-scoped shell sessionvfsShell.exec({ sessionId, command, timeoutMs? })→ run a bash commandvfsShell.session({ sessionId })→ get sessioncwd+ expiryvfsShell.closeSession({ sessionId })→ close a session
Implementation details:
- Directory tree is bootstrapped from
folders+filesand cached in memory. - File contents are fetched lazily from the configured storage provider and cached.
- All write operations (
rm,mv, redirections, etc.) fail withEROFS(read-only filesystem). - Access is workspace-scoped and enforced by existing workspace membership checks.
MIT