Installation
This template ships as source code. Extract the ZIP, set env vars, install deps, migrate the database, seed demo data, and run the dev server. The whole flow takes under 10 minutes on a laptop with Docker.
Prerequisites
- Node.js 22.13+ (matches CI and the pinned pnpm runtime)
- pnpm 10+ —
npm install -g pnpm@10or use corepack - Docker Desktop — used to start Postgres in the demo flow (optional if you have Postgres installed natively)
- Git — for cloning and version control
Quick Start (Docker Compose, recommended)
The demo compose runs Postgres only (exposed on host port 54321). The Next.js
app runs on the host via pnpm dev so hot-reload and source edits work
normally.
# 1. Copy environment
cp .env.example .env
# 2. Start Postgres in the background (pulls image on first run)
docker compose -f docker-compose.demo.yml up -d
# 3. Install JS deps
pnpm install
# 4. Apply migrations (the script waits for Postgres to become reachable)
pnpm db:migrate
# 5. Seed demo users and CMS content
pnpm db:seed
# 6. Run the dev server
pnpm dev
# 7. Open http://localhost:3000 in your browser
Demo login
- Regular user:
demo@example.com/Password123!! - Admin:
admin@example.com/Password123!!
If
DEMO_MODE=trueand you deploy publicly, setSEED_PASSWORDin.envto override the default password — otherwise your admin account is trivially guessable.
Quick Start (native Postgres)
If you already have Postgres running locally:
# 1. Create the database
createdb inkwell_cms
# 2. Copy env and point DATABASE_URL at your local instance
cp .env.example .env
# Edit .env: DATABASE_URL="postgresql://<user>:<pass>@127.0.0.1:5432/inkwell_cms"
# 3. Install + migrate + seed + dev
pnpm install
pnpm db:migrate
pnpm db:seed
pnpm dev
Environment Variables
The .env.example file documents every variable the app reads. For local development the defaults work — for production, see DEPLOYMENT.md for required variables and secret-generation commands.
Must set in production:
DATABASE_URL— Postgres connection stringJWT_SECRET— min 32 chars,openssl rand -base64 48OAUTH_STATE_SECRET— min 32 chars,openssl rand -hex 32(required if OAuth enabled)NEXT_PUBLIC_APP_URL— your canonical public URL
Verifying the install
After seeding, the following should all work:
http://localhost:3000renders the landing pagehttp://localhost:3000/blogrenders seeded published postshttp://localhost:3000/aboutandhttp://localhost:3000/contactrender CMS-managed static pageshttp://localhost:3000/rss.xmlrenders the RSS feedhttp://localhost:3000/sitemap.xmlrenders the XML sitemaphttp://localhost:3000/loginacceptsdemo@example.com/Password123!!http://localhost:3000/dashboardloads after loginhttp://localhost:3000/contentloads the CMS admin studio asadmin@example.comhttp://localhost:3000/content/commentsshows the seeded moderation queue asadmin@example.comhttp://localhost:3000/content/analyticsshows seeded content analytics asadmin@example.comhttp://localhost:3000/healthreturns{ status: "healthy" }(200)http://localhost:3000/readyreturns{ status: "ready", database: "connected" }(200)http://localhost:3000/api/docsrenders the API reference
Pre-flight checks
The buyer ZIP ships as a clean runnable starter. Template Empire runs the regression suite before packaging; buyers should run the shipped app-level checks before customising:
pnpm typecheck # TypeScript strict mode
pnpm lint # ESLint (zero warnings)
pnpm build # Production build
pnpm audit:prod # Dependency vulnerability scan
Common issues
pnpm install fails on better-sqlite3 or native bindings
Some deps compile native bindings. Install Python 3 and a C++ toolchain:
- macOS:
xcode-select --install - Ubuntu/Debian:
sudo apt install build-essential python3 - Windows: install Visual Studio Build Tools with "Desktop development with C++"
Migrations fail with PostgreSQL is not reachable
docker compose up -d starts the container, but Postgres may still be warming
up or the host port may not be reachable yet. The pnpm db:migrate and
pnpm db:seed scripts wait up to 60 seconds before failing. If they still fail,
inspect the container and port mapping:
docker compose -f docker-compose.demo.yml ps
docker compose -f docker-compose.demo.yml logs postgres
Confirm .env matches the exposed compose port (POSTGRES_PORT) and that no
other local Postgres/container is already bound to that port.
Migrations fail with relation does not exist
pnpm db:migrate is the correct first-run command. The template ships
with versioned migrations in src/lib/db/migrations/. If migrations fail,
the database connection is probably wrong (check DATABASE_URL or the
POSTGRES_* values in .env match the Postgres the compose/host is actually
running). The demo compose intentionally uses host port 54321 to avoid
colliding with a local Postgres service on 5432. pnpm db:push
is an escape hatch for schema experimentation only and skips version tracking.
OAuth callback fails with OAUTH_STATE_SECRET is required
Uncomment the line in .env.example (it's uncommented in .env.example itself; if you still see this error, ensure you have it in your .env and restart the dev server).
Port 3000 already in use
Set PORT=3001 (or any free port) in .env, or stop the conflicting process: lsof -ti:3000 | xargs kill.
Next steps
- CUSTOMIZATION.md — theme, brand name, colours, fonts, demo content, legal pages
- DEPLOYMENT.md — production hosting, Docker, health checks, cron, OAuth setup
- LICENCE.md — commercial licence terms