Run it on a VPS
Drop as one Node.js process on a server of yours: D1 over HTTP for the database, the disk for files, and a reverse proxy in front for HTTPS.
| Database | D1 over HTTP |
| Files | Local disk |
| Rate limits | In memory |
| Code images | SVG |
| Code image cleanup | In process |
| Live | Not deployed yet |
What you need
- A Linux server with Node.js 24 and pnpm.
- A domain pointing at the server, and a reverse proxy that serves HTTPS. These steps use Caddy, which gets the certificate for you.
- A GitHub OAuth app with the callback
https://<your-domain>/api/auth/callback/github.
Deploy
On the server, get the code:
git clone https://github.com/vite-hub/drop /srv/drop cd /srv/drop pnpm installBuild for Node and create the database:
DROP_HOST=vps pnpm build CLOUDFLARE_D1_DATABASE_NAME=vitehub-drop-vps pnpm db:migrate:d1Write
/srv/drop/.envwith the settings below. Keep the server on127.0.0.1; Caddy is the only thing that talks to it.GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= BETTER_AUTH_SECRET= DROP_ADMINS= CLOUDFLARE_ACCOUNT_ID= CLOUDFLARE_API_TOKEN= CLOUDFLARE_D1_DATABASE_ID= CLOUDFLARE_D1_DATABASE_NAME=vitehub-drop-vps HOST=127.0.0.1 PORT=3000Try it with:
node --env-file=.env .output/server/index.mjsKeep it running with systemd, in
/etc/systemd/system/drop.service:[Unit] Description=Drop After=network.target [Service] User=drop WorkingDirectory=/srv/drop EnvironmentFile=/srv/drop/.env ExecStart=/usr/bin/node .output/server/index.mjs Restart=always [Install] WantedBy=multi-user.targetsudo systemctl enable --now dropPut Caddy in front, in
/etc/caddy/Caddyfile, and reload it:drop.example.com { reverse_proxy 127.0.0.1:3000 }Caddy sends
X-Forwarded-Proto: https, so the links Drop builds, the GitHub callback, and the OAuth issuer all usehttps://. With nginx, setproxy_set_header Host $hostandproxy_set_header X-Forwarded-Proto $scheme.Run the smoke test:
DROP_URL=https://drop.example.com pnpm test:e2e:deployed
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 |
| HOST, PORT | Where Node listens. 127.0.0.1 and 3000 behind Caddy. |
| CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN | Cloudflare account id and an account API token with D1 edit access. |
| CLOUDFLARE_D1_DATABASE_ID, CLOUDFLARE_D1_DATABASE_NAME | The id and name of vitehub-drop-vps. |
Database and files
Files and the cleanup job's run history live in .data/, next to the code. The database is D1 over HTTPS. Paths are relative to the working directory, so start Drop from /srv/drop as the unit does. Back up the database and the files:
rsync -a .data/blob/ /backups/blob/To update, pull, rebuild, migrate, and restart. pnpm db:migrate:d1 skips the migrations already applied.
git pull
pnpm install
DROP_HOST=vps pnpm build
pnpm db:migrate:d1
sudo systemctl restart dropOn a VPS
- One process holds everything, so run one instance. Rate limits are counted in its memory and reset when it restarts.
- Code images are SVG only: PNG needs Cloudflare Browser Run.
- The hourly cleanup of expired code images runs on a timer inside the process.
- The Node process calls D1 over HTTPS, so keep the account id, API token, database id, and database name in its environment.