Installation
Clone the Next.js + Neon starter, start the local Postgres stack with Docker, run the web and admin apps, seed and sign in.
This page gets the kit running on your computer. You need Node.js 24, pnpm 10.33.0 (through Corepack) and Docker. The database is Neon Postgres in production; locally the kit runs Postgres 17 in a container, so you don't need a Neon account yet.
Clone the repository
git clone https://github.com/usenextbase/nextjs-planetscale-starter.git
cd nextjs-planetscale-starterThe repository name still says PlanetScale. The main branch is the Neon Postgres version. If GitHub says the repository doesn't exist, sign in with the account that has access.
Install dependencies
corepack enable
pnpm installThe root package.json pins pnpm@10.33.0, so Corepack installs the right version. pnpm install also runs fumadocs-mdx in apps/web to generate the .source/ folder; don't edit that folder.
On Windows, run git config core.symlinks true before cloning. The .claude/skills entry is a symlink to .agents/skills/.
Create the env files
The web app, the database package and the scripts share one env file at the repository root:
[ -f .env.local ] || cp .env.example .env.localThe admin app has its own template with the origin variables it needs:
[ -f apps/admin/.env.local ] || cp apps/admin/.env.local.example apps/admin/.env.localThe defaults point at the local Docker services and at http://localhost:3000 for the web app and http://localhost:3001 for the admin app, so you don't need to change anything to start. Keep your existing files if you already have them. The full list of variables is on the environment variables page.
Start the stack and the web app
pnpm local:startThis single command runs, in order:
pnpm db:start:docker compose upfor Postgres, MinIO, Mailpit, Valkey and Centrifugo, then a one-offminio-initcontainer that creates thenextbase-publicandnextbase-privatebuckets.pnpm db:migrate: appliesapps/database/drizzle-postgres/to the local database.pnpm db:seed: the minimal database seed, then the full fixture seed fromapps/web/src/cli/seed.ts. Both are idempotent.- The Next.js dev server for the web app (
pnpm web#dev) and the outbox dispatcher (pnpm realtime:dispatch) together throughconcurrently.
Open http://localhost:3000 when the web process prints that it is ready.
Local services and their addresses:
| Service | Address |
|---|---|
| Postgres | postgresql://nextbase:nextbase@127.0.0.1:35577/nextbase |
| Mailpit inbox | http://127.0.0.1:8025 (SMTP on port 1025) |
| MinIO console | http://127.0.0.1:9001 (S3 API on port 9000) |
| Centrifugo | http://127.0.0.1:8000 |
| Valkey | redis://127.0.0.1:6379 |
If an image can't be pulled, see troubleshooting. If you prefer to run the steps separately, use pnpm db:start, pnpm db:migrate, pnpm db:seed and then pnpm web#dev.
Start the admin app
pnpm local:start doesn't start the admin app. In a second terminal, run:
pnpm --filter admin devThis starts apps/admin with next dev --port 3001. Open http://localhost:3001; it redirects to /app-admin, and to /login on the same host when you aren't signed in as an administrator. The web app's sidebar shows an Open admin panel link for administrators once NEXT_PUBLIC_ADMIN_APP_URL is set, which the root .env.example already does.
pnpm dev from the root runs the dev script of every package through Turborepo, which includes both Next.js apps, Drizzle Studio and the email preview server. The email preview also defaults to port 3001, so when you want both the admin app and the email preview, start the admin app on its own as above and give the preview another port.
Sign in
The seed creates four accounts. All of them use the password password123:
| Role | |
|---|---|
admin@example.com | Application administrator |
user@example.com | Workspace owner |
user2@example.com | Second member and invitation fixture |
invited@example.com | Pending invitation fixture |
Sign in at http://localhost:3000/login with user@example.com to see a workspace with a seeded project. Sign in at http://localhost:3001/login with admin@example.com to open the admin panel at /app-admin; the admin sign-in form takes email and password only.
To try the real flow, sign up at /sign-up on the web app with a new address. The verification email lands in Mailpit at http://127.0.0.1:8025. After verifying, complete onboarding and create a workspace, then add a project through the website so you have data for an agent later.
Verify the setup
Unit tests and static checks run without Docker:
pnpm lint
pnpm typecheck
pnpm testIntegration tests need the stack from pnpm db:start:
pnpm test:integrationpnpm verify:local runs everything CI runs: provider purity, migration lint, a clean-database replay of migrations and seeds in a throwaway Postgres container, structural parity, lint, typecheck, unit tests, integration tests and a production build of every package.
pnpm test:e2e builds both apps and starts the web app on port 3100 with apps/web/.env.test and the admin app on port 3101 (override with ADMIN_BASE_URL), then runs the Playwright suites. The admin specs run against the admin origin. Run pnpm --filter web exec playwright install --with-deps chromium once first.
Stop, restart or reset
Stop the containers and keep their data:
pnpm db:stoppnpm db:status shows the Compose services. Starting again with pnpm local:start reapplies migrations and reruns the idempotent seeds, which is safe.
Data lives in the postgres-data and minio-data Docker volumes. The repository has no reset script; to start from an empty database, remove the volumes with Docker Compose from apps/database:
docker compose -f apps/database/docker-compose.yml down -vUseful commands
| Task | Command |
|---|---|
| Web app only | pnpm web#dev |
| Admin app only (port 3001) | pnpm --filter admin dev |
| Build one app | pnpm --filter web build or pnpm --filter admin build |
| Drizzle Studio | pnpm db:studio |
| Generate a migration after editing the schema | pnpm db:generate |
Regenerate database/types | pnpm gen-types |
| Email template preview (also defaults to port 3001) | pnpm --filter email dev |
| Run the outbox dispatcher once | pnpm --filter web realtime:dispatch:once |
| Remove expired unattached uploads | pnpm --filter web storage:cleanup |
pnpm db:push:force pushes the schema directly without a migration file. Use it only for disposable local prototyping.
When you can sign in and see a workspace, continue with authentication or connect an agent through agent connections.