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.

DatabaseD1 over HTTP
FilesLocal disk
Rate limitsIn memory
Code imagesSVG
Code image cleanupIn process
LiveNot 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

  1. On the server, get the code:

    git clone https://github.com/vite-hub/drop /srv/drop
    cd /srv/drop
    pnpm install
  2. Build for Node and create the database:

    DROP_HOST=vps pnpm build
    CLOUDFLARE_D1_DATABASE_NAME=vitehub-drop-vps pnpm db:migrate:d1
  3. Write /srv/drop/.env with the settings below. Keep the server on 127.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=3000

    Try it with:

    node --env-file=.env .output/server/index.mjs
  4. Keep 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.target
    sudo systemctl enable --now drop
  5. Put 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 use https://. With nginx, set proxy_set_header Host $host and proxy_set_header X-Forwarded-Proto $scheme.

  6. Run the smoke test:

    DROP_URL=https://drop.example.com pnpm test:e2e:deployed

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
HOST, PORTWhere Node listens. 127.0.0.1 and 3000 behind Caddy.
CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKENCloudflare account id and an account API token with D1 edit access.
CLOUDFLARE_D1_DATABASE_ID, CLOUDFLARE_D1_DATABASE_NAMEThe 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 drop

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