Run it on a VPS
One Node.js process in Docker, with local SQLite and a Docker volume for all persistent data. Put a reverse proxy in front for HTTPS.
| Database | Local SQLite or D1 |
| Files | Local disk |
| Rate limits | In memory |
| Code images | SVG |
| Code image cleanup | In process |
Before you start
You need a server with Git, Docker Compose, and a domain pointing at it. SQLite, files, and KV stay on your server.
Create a GitHub OAuth app with callback https://<your-domain>/api/auth/callback/github. Find your admin user id with gh api users/<login> --jq .id and generate BETTER_AUTH_SECRET with openssl rand -base64 32.
Run Drop
Paste this on the server. Fill the sign-in settings when the editor opens:
git clone https://github.com/vite-hub/drop my-drop
cd my-drop
cp .env.example .env
${EDITOR:-nano} .env
docker compose up -d --buildCompose builds for DROP_HOST=vps, applies the existing Drizzle migrations, and starts Drop. It binds port 3000 to localhost and keeps the SQLite database, files, and cleanup history in the drop-data volume. If migrations fail, the server does not start.
Serve HTTPS
With Caddy, add this to your Caddyfile and reload it:
drop.example.com {
header /f/* Cache-Control "private, no-store"
reverse_proxy 127.0.0.1:3000
}Caddy forwards the host and HTTPS scheme. With nginx, set proxy_set_header Host $host and proxy_set_header X-Forwarded-Proto $scheme. Keep the OAuth app's homepage and callback on that HTTPS domain.
Settings
| Variable | What it is |
|---|---|
| GITHUB_CLIENT_ID | Your GitHub OAuth app's client ID. |
| GITHUB_CLIENT_SECRET | A client secret from the same app. |
| BETTER_AUTH_SECRET | Signs sessions and agent tokens. One per deployment: openssl rand -base64 32 |
| DROP_ADMINS | GitHub user ids that join as Admin, comma-separated: gh api users/<login> --jq .id |
| DROP_DATABASE | Optional build override: sqlite or d1. Existing D1 database ids keep the D1 driver. |
| DROP_DATABASE_URL | Runtime SQLite file: URL. Defaults to file:.data/database/drop.db. Keep it on the persistent volume. |
| DROP_GITHUB_ORG | Optional active organization membership requirement for publishing, viewing, and MCP. |
| DROP_AUTH_PROXY | Set 1 to reuse GitHub oauth2-proxy; requires DROP_GITHUB_ORG. |
| DROP_AUTH_PROXY_COOKIE | Optional proxy cookie name to clear on logout. Secure, host-only, Path=/. |
Reuse GitHub oauth2-proxy
Set DROP_AUTH_PROXY=1 and DROP_GITHUB_ORG=your-org. Native GitHub client credentials are unnecessary in this mode. Configure the existing OAuth app to accept https://<your-domain>/oauth2/callback.
Configure oauth2-proxy with --provider=github, --scope=user:email read:org, --github-org=your-org, --set-xauthrequest=true, and --pass-access-token=true. Forward X-Auth-Request-Access-Token from its auth response to Drop. Drop checks that token against GitHub, maps its numeric user id, and creates its normal session. Do not forward client-supplied identity headers.
Use a Secure, host-only proxy cookie with Path=/, such as __Host-drop-auth, and set DROP_AUTH_PROXY_COOKIE to the same name for logout. Keep the Drop backend accessible only through your proxy.
Browser pages and shared links can require proxy authentication. Exempt /oauth2, /.well-known, /mcp, /api/mcp, and Drop's /api/auth/oauth2 and /api/auth/jwks routes from the proxy's browser redirect. MCP clients authenticate with Drop's own OAuth tokens; Drop checks organization membership for their content access too.
Successful membership checks expire after five minutes. Failed checks deny access. A token without read:org or organization access may require reauthorization.
Keep an existing D1 database
Keep the four Cloudflare settings from your existing deployment and optionally set DROP_DATABASE=d1. Compose passes the database selection to the build and runs D1 migrations at startup. Changing to SQLite needs a rebuild and a separate data migration.
Updates and backups
git pull
docker compose up -d --buildMigrations run on every container start and skip those already applied. Stop Drop before backing up the Docker volume, or use SQLite's backup API and also back up the blobs. Rebuilding preserves the volume; docker compose down -v deletes it.
Run one instance. Rate limits count in its memory. Code images are SVG, and an hourly timer deletes expired images.