Skip to main content

Self-hosting

Droppie is fully open source (MIT) and designed to run anywhere. The app is client-side — wallets sign everything, trees are built in the browser — so "self-hosting" is mostly serving static-ish Next.js pages plus one optional pinning endpoint.

Requirements

  • Node.js >= 20 (we build with 22 — see .nvmrc)
  • pnpm 9 (corepack enable gives you the right version)
  • Foundry — only if you're hacking on the contracts themselves

Clone and build

git clone https://github.com/droppie-io/droppie-monorepo.git
cd droppie-monorepo

pnpm install
pnpm build # turbo builds sdk → web + docs

The monorepo layout:

droppie-monorepo/
├── apps/
│ ├── web/ # Next.js app — landing, dashboard, create, claim
│ └── docs/ # this docs site (Docusaurus)
├── packages/
│ ├── contracts/ # Foundry — DroppieDrop, DroppieERC20
│ └── sdk/ # @droppie/sdk

Run things locally:

pnpm --filter web dev # app on http://localhost:3000
pnpm --filter docs start # docs on http://localhost:3001

Environment variables

Copy .env.example to apps/web/.env.local and fill in what you need. Every variable is optional — the app degrades gracefully without them:

VariableRequiredFallback behavior when unset
NEXT_PUBLIC_WALLETCONNECT_PROJECT_IDNoFalls back to a dev placeholder ("droppie-dev"). Injected wallets (MetaMask, Rabby, …) work fine; WalletConnect QR pairing to mobile wallets won't. Get a free ID at dashboard.reown.com.
NEXT_PUBLIC_DOCS_URLNoThe app's navbar/footer "Docs" links fall back to https://docs.droppie.notcool.in. Point it at your own deployment of this docs site if you host it yourself.
PINATA_JWTNoThe /api/pin endpoint returns 501 with { "fallback": "download" }, and the create wizard offers the distribution JSON as a download instead of pinning to IPFS. Host the file on any HTTPS URL and use that as the drop's metadataURI. Get a JWT at pinata.cloud.

NEXT_PUBLIC_ variables are baked in at build time — rebuild after changing them.

Docker

The repo root ships a Dockerfile that builds the web app as a Next.js standalone output — a small, self-contained image.

NEXT_PUBLIC_* variables are inlined into the client bundle at build time, so pass them as --build-args — setting them with docker run -e has no effect on an already-built image. Only server-side variables like PINATA_JWT are read at runtime:

# from the repo root — NEXT_PUBLIC_* vars are baked in at build time
docker build \
--build-arg NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id \
-t droppie-web .

# PINATA_JWT is server-side, so a runtime -e works for it
docker run -p 3000:3000 -e PINATA_JWT=your_pinata_jwt droppie-web

NEXT_PUBLIC_DOCS_URL works the same way (--build-arg NEXT_PUBLIC_DOCS_URL=…) if you host this docs site yourself. Or with compose, using the repo's docker-compose.yml (which passes the build args for you, pointing the app's "Docs" link at the bundled docs container):

docker compose up -d --build

Environment variables come from your shell or a local .env file next to the compose file. The app listens on port 3000; put your reverse proxy of choice (Caddy, nginx, Traefik) in front for TLS.

The compose file also starts a local Anvil chain on :8545 (chain id 31337) — a handy sandbox for a full local demo. Add it to your wallet, fund your address with cast, and create tokens and drops from the app UI exactly like on any other chain.

This docs site builds to plain static files — pnpm --filter docs build outputs apps/docs/build/, which any static host (Netlify, Vercel, GitHub Pages, an S3 bucket) can serve as-is.

Contracts — nothing to pre-deploy

Droppie is factoryless: the app ships the contracts' creation bytecode inside @droppie/sdk, and every user deploys their own DroppieDrop / DroppieERC20 from their wallet. There are no hosted contracts, no addresses to configure, and no per-chain deployment step — a self-hosted Droppie works on every configured chain out of the box.

Two customizations you can make:

  • Add a chain. Add its id and display metadata to SUPPORTED_CHAINS / CHAIN_INFO in packages/sdk/src/addresses.ts, wire it into the wagmi chain config in apps/web, and rebuild. Since users deploy their own contracts, the new chain is fully functional immediately.
  • Modify the contracts. Edit packages/contracts/src, run forge build && forge test, regenerate the packages/contracts/abi/*.json artifacts (the command is documented in the contracts package README), copy the new bytecode fields into packages/sdk/src/bytecode.ts, and rebuild:
pnpm --filter @droppie/sdk build # its tests fail if bytecode drifts from the artifacts
pnpm --filter web build

Production checklist

  • Set NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID (own the WalletConnect pairing experience)
  • Set PINATA_JWT or plan to host distribution JSONs yourself
  • TLS-terminating reverse proxy in front of the container
  • If you modified the contracts: regenerate the artifacts and SDK bytecode together
  • forge test and the CI suite pass on your commit