Start here
This guide takes a downloaded template to one complete Stripe test payment.
There are two paths, and they end in the same place.
| Path | Use it when |
|---|---|
| One command | You are happy on Vercel and Neon. About fifteen minutes, seven answers. |
| The manual path | Any other host: DigitalOcean, Hostinger, a Docker host, your own server. Eight steps. |
Read this in a browser instead
npm run dev, then open <http://localhost:3000/docs>. This guide and five other documents render on your own board.
What the template already includes
- The public board and document pages.
- The operator console at
/operator. - Postgres migrations.
- Stripe Checkout and signed webhooks.
- A scheduled maintenance route.
- Release checks and a production build.
You do not create a Stripe Product or Price. Each checkout carries its bid amount.
What you must supply
- A Postgres database.
- A Stripe account.
- A deployment host and domain.
- One operator email and password.
Bidder email is off by default. You do not need Resend for the first deployment.
One command
npm ci
npm run quickstart
It asks for your board name, domain, currency, minimum first bid, operator email, operator password, and one Stripe secret key. It generates every other secret, creates your database and a separate test branch, migrates both, deploys, and registers the Stripe webhook and stores its signing secret.
Stop it at any point with Ctrl+C. Run it again and it resumes; finished steps are skipped.
It creates four things inside accounts that belong to you — a Neon project, a Neon branch, a Vercel project, and a Stripe webhook endpoint. Read Quickstart first. It lists all four, how to undo each one, and the two plan limits worth knowing before you start.
When it finishes, go to Complete one test payment.
The manual path
Use these eight steps on DigitalOcean, Hostinger, Cloudflare in front of a server, a Docker host, or your own machine — anywhere the one command does not reach. Pick your host in Deploying this board (docs/deploy/README.md) first; each guide covers the parts specific to it.
1. Install the project
Node 22 or newer, and a Postgres database.
npm ci
cp .env.example .env.local
Do not commit .env.local.
2. Add the required values
Open .env.local. Replace these values:
| Value | Where it comes from |
|---|---|
DATABASE_URL | The pooled Postgres connection string |
NEXT_PUBLIC_SITE_URL | The full site URL, with https:// |
STRIPE_SECRET_KEY | Stripe Developers → API keys |
OPERATOR_EMAIL | The email that can open /operator |
OPERATOR_PASSWORD_HASH | The output from npm run operator:hash |
OPERATOR_SESSION_SECRET | The output from openssl rand -base64 48 |
CRON_SECRET | The output from openssl rand -hex 32 |
Keep STRIPE_SECRET_KEY in test mode for the first deployment.
No value may contain a $. A $ in a .env value is expanded before the application reads it, so what loads is a fragment of what you set — the board then fails on a value that looks perfectly correct in the file. If a generated secret or a database password has one, generate it again.
3. Name the board
Open outbid.config.ts. Change these values first:
board.nameboard.taglineboard.headlineboard.domainbidding.currencybidding.minimumFirstBidCents
Leave payments.provider as stripe. It is the only value this template ships, and the only rail proven against real money. docs/payments.md explains how to add your own.
4. Create the database and check the build
npm run db:migrate
npm run doctor
npm run gate
The doctor names every missing value. Do not deploy while it reports a finding.
5. Deploy the site
Import the repository into your deployment host. Copy the production environment values into the host. Run the database migration before the new build starts.
The template includes vercel.json for the scheduled route. Another host must call /api/cron with the CRON_SECRET bearer token.
6. Register the Stripe webhook
Create this Stripe endpoint:
https://your-domain.example/api/webhooks/stripe
Subscribe to these events:
checkout.session.completedcheckout.session.async_payment_succeededcheckout.session.expiredcharge.refundedcharge.dispute.created
Copy its signing secret to STRIPE_WEBHOOK_SECRET. Deploy the site again.
7. Run the tests against a SEPARATE database
The money-path tests TRUNCATE every table before each case. They read TEST_DATABASE_URL and never DATABASE_URL, and the database name must contain test. Point them at a second database, never at your board.
8. Check the source maps are refused at your web server
The build already refuses to finish if a source map carrying real source would ship. Your web server is the layer that covers a future build nobody checked: deploy/source-maps.md (docs/deploy/source-maps.md) is one block.
Complete one test payment
Both paths end here. These seven checks are how you know the board works.
- Open the public board.
- Enter a product URL.
- Complete a Stripe test payment.
- Confirm that the rank changes.
- Open
/operator/payments. - Confirm that the internal drift value is zero.
- Run
npm run reconcile.
The template is ready for real setup when all seven checks pass.
Before real money
- Replace the Stripe test key with a live key.
- Create a live webhook and replace its signing secret.
- Run
npm run doctoragainst the production environment. - Read Cold start.
- Add real listings before you announce the board.
To collect bidder email, add the Resend values first. Then enable Bidder email at /operator/settings.