Vercel Deployment (madeopen.com marketing site)
This guide documents how the public marketing site at madeopen.com is deployed to Vercel. It is not a user-facing self-hosting guide — Made Open users run their own hub and their own web instance on their own infrastructure (see deployment-docker.md). This document exists so the operator can reproduce the Vercel project setup if it ever needs to be recreated.
Scope: apps/web only. The hub, NATS, Meilisearch, Redis, and the rest of the stack are not deployed to Vercel (Vercel serverless can't host a stateful Fastify backend).
Prerequisites
- A Vercel account with access to deploy the repo.
- DNS control over
madeopen.com. - (Optional) A Supabase project if you want the operator to be able to sign in and preview the full site while
COMING_SOON_MODEis on. The middleware falls through gracefully without Supabase, so you can skip this for the initial coming-soon-only deploy.
What's already in the repo
vercel.jsonat the repo root pins the framework, install command, build command, output directory, and agit diffguard for theignoreCommand.apps/web/src/middleware.tsimplements theCOMING_SOON_MODEgate that rewrites unauthenticated traffic to/coming-soon(landed in commitaec70de).apps/web/src/app/coming-soon/page.tsxis the minimal landing page (outside the(public)layout so it doesn't inherit the marketing navbar).apps/web/.env.exampledocuments the flag.
Phase 1 — Link the project
- Vercel dashboard → Add New → Project → Import Git Repository → pick
drdropout/made-open. - Framework Preset: auto-detected Next.js (confirm).
- Root Directory: leave as
.(repo root).vercel.jsonhandles pathing. - Build & Development Settings: leave untouched — they are driven by
vercel.json. - Node.js Version: 20.x (matches
ci.yml). Set in Project Settings → General → Node.js Version. - Hit Deploy. First build may fail if env vars aren't set (Phase 2). That's expected.
Phase 2 — Environment variables
Set these in Project Settings → Environment Variables. Vercel cannot read vercel.json for secrets; this is manual.
| Variable | Value | Environments | Required? |
|---|---|---|---|
COMING_SOON_MODE | 1 | Production, Preview, Development | Yes |
NEXT_PUBLIC_SUPABASE_URL | https://<ref>.supabase.co | Production, Preview | Optional — needed if the operator wants to sign in and preview the full site |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Matching anon key | Production, Preview | Same |
NEXT_PUBLIC_HUB_URL | Leave unset | — | Not needed until the hub is live |
COMING_SOON_MODE must be set on all three environments. Vercel creates public preview URLs for every PR; without the gate on Preview, a leaked URL would expose the full in-progress site.
After setting variables, redeploy from Deployments → latest → Redeploy.
Phase 3 — Attach madeopen.com
- Project Settings → Domains → add
madeopen.comandwww.madeopen.com. - At the DNS registrar for
madeopen.com:- Apex (
madeopen.com):Arecord →76.76.21.21 www:CNAME→cname.vercel-dns.com
- Apex (
- In Vercel, configure
www.madeopen.com→ redirect tomadeopen.com(one click on the domain detail page). - Wait for DNS propagation (usually minutes). Vercel issues the Let's Encrypt certificate automatically once propagation completes.
Verification
After Phase 3:
# Coming Soon gate
curl -s https://madeopen.com/ | grep -c "Coming soon" # > 0
curl -s https://madeopen.com/features | grep -c "Coming soon" # > 0
curl -s https://madeopen.com/admin | grep -c "Coming soon" # > 0
# Login escape hatch
curl -s https://madeopen.com/auth/login | grep -c "Coming soon" # 0
curl -s https://madeopen.com/auth/login | grep -ci "sign in" # > 0
# TLS + HTTPS redirect
curl -I http://madeopen.com/ # 308 → https
curl -I https://www.madeopen.com/ # 308 → apex
And open a preview URL from a PR branch — confirm it also renders Coming Soon.
Going fully live
No code changes. No redeploy-with-code required.
- Project Settings → Environment Variables → Production → set
COMING_SOON_MODEto0(or delete it). - Deployments → latest → Redeploy.
- The middleware gate falls through, the normal
(public)routes serve their real content, andmadeopen.com/renders the full marketing page.
Keep COMING_SOON_MODE=1 on the Preview environment if you want PR previews to stay gated even after production is live.
Troubleshooting
Build fails with "Cannot find module '@made-open/shared'":
Turbo didn't build the shared package first. Confirm buildCommand in vercel.json is pnpm turbo run build --filter=@made-open/web (with the filter). The turbo.json dependsOn: ["^build"] clause does the ordering.
Build fails on pnpm install --frozen-lockfile:
pnpm-lock.yaml is out of sync with a package.json. Run pnpm install locally, commit the lockfile, push.
Pages render blank / 500 in production:
Usually Supabase env vars mismatch. The middleware falls through silently when both are unset, but if one is set and the other isn't, createServerClient throws. Either set both or unset both.
Middleware rewrites don't fire:
Check COMING_SOON_MODE is exactly "1" (string). Any other value including "true" is treated as off.
/coming-soon itself redirects to Coming Soon in a loop:
Won't happen — the middleware explicitly allowlists /coming-soon and /auth/*. If it does, someone broke the allowlist logic; check isComingSoonAllowed in apps/web/src/middleware.ts.
Related
- Docker deployment guide — for users self-hosting the full stack
- Release signing runbook — for the update-system signing ceremony
apps/web/src/middleware.ts— theCOMING_SOON_MODEgate implementationapps/web/src/app/coming-soon/page.tsx— the landing page