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.

DatabaseLocal SQLite or D1
FilesLocal disk
Rate limitsIn memory
Code imagesSVG
Code image cleanupIn 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 --build

Compose 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

VariableWhat it is
GITHUB_CLIENT_IDYour GitHub OAuth app's client ID.
GITHUB_CLIENT_SECRETA client secret from the same app.
BETTER_AUTH_SECRETSigns sessions and agent tokens. One per deployment: openssl rand -base64 32
DROP_ADMINSGitHub user ids that join as Admin, comma-separated: gh api users/<login> --jq .id
DROP_DATABASEOptional build override: sqlite or d1. Existing D1 database ids keep the D1 driver.
DROP_DATABASE_URLRuntime SQLite file: URL. Defaults to file:.data/database/drop.db. Keep it on the persistent volume.
DROP_GITHUB_ORGOptional active organization membership requirement for publishing, viewing, and MCP.
DROP_AUTH_PROXYSet 1 to reuse GitHub oauth2-proxy; requires DROP_GITHUB_ORG.
DROP_AUTH_PROXY_COOKIEOptional 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 --build

Migrations 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.