Local Dev Setup
Get your development environment running from scratch.
This guide walks you through setting up a local Toast development environment. For the short version, see the CONTRIBUTING.md checklist.
Prerequisites
- Node.js 24+ — check with
node --version - pnpm — installed via corepack (
corepack enable) - Docker — required for PostgreSQL and MinIO (object storage)
- Git — for cloning and hooks
Quick Start
git clone https://github.com/TryGhost/Toast.git
cd Toast
pnpm install
pnpm dxpnpm dx is the single command that handles everything: starts Docker containers, runs database migrations, seeds data, and launches all dev servers.
What pnpm dx Does
On first run, pnpm dx detects it's a fresh checkout and runs the bootstrap sequence:
- Port detection — checks if default ports (5432, 9000, 3000, 5173) are available
- Environment setup — creates
.envfrom.env.example, prompting for custom ports if needed - Docker Compose — starts PostgreSQL 16 and MinIO (S3-compatible object storage)
- Database migrations — applies all Drizzle ORM migrations
- Database seeding — creates a default site and admin user
- Dev servers — starts the API server (port 3000) and admin panel (port 5173) via Turborepo
On subsequent runs, it skips the bootstrap and goes straight to starting Docker and dev servers.
Non-Interactive Mode
For CI or scripting, skip all prompts:
DX_AUTO=1 pnpm dxOr manually create .env before running:
cp .env.example .env
# Edit ports if needed
pnpm dxTipTap Pro Token
The editor uses TipTap Pro extensions. If pnpm install fails with ERR_PNPM_FETCH_404 for @tiptap-pro/* packages, set the token first:
export TIPTAP_PRO_TOKEN=<token-from-tiptap-cloud>
pnpm installGet the token from TipTap Cloud → Pro Extensions.
TipTap Runtime Features
TipTap has a two-layer model:
TIPTAP_PRO_TOKEN— install/build auth only (package registry access)- Runtime capabilities — controlled by env pairs:
- Collaboration:
VITE_TIPTAP_COLLAB_APP_ID+TIPTAP_COLLAB_SECRET - AI:
VITE_TIPTAP_AI_APP_ID+TIPTAP_AI_SECRET
- Collaboration:
The admin panel checks /api/capabilities at runtime to gate features — it never reads env vars directly.
Verify Setup
After pnpm dx completes, verify everything is running:
# API health check
curl http://localhost:3000/healthz # {"status":"ok"}
curl http://localhost:3000/healthz/ready # {"status":"ok","database":"connected"}The admin panel runs at http://localhost:5173. Default login:
- Email:
admin@ghost.org - Password:
admin
Docker Services
docker-compose.dev.yml manages two services:
| Service | Image | Default Port | Purpose |
|---|---|---|---|
| db | postgres:16-alpine | 5432 | Primary database |
| minio | minio/minio | 9000 / 9001 | S3-compatible file storage |
A minio-setup init container creates the default toast-uploads bucket on first start.
Port Conflicts
If port 5432 is already in use (e.g., a system PostgreSQL), you have two options:
- Interactive — run
pnpm dxand choose custom ports when prompted - Manual — edit
.envto setDB_PORT=5433and updateDATABASE_URLaccordingly
Git Worktrees
When working on multiple branches simultaneously, use worktrees instead of stashing:
# Create a worktree with auto-allocated ports
pnpm worktree:create my-feature-branch
# Check which ports your worktree uses
pnpm worktree:context --json
# List all worktrees and their ports
pnpm worktree:list --json
# Detect and fix port collisions
pnpm worktree:ports:check
pnpm worktree:ports:fixEach worktree gets its own .env with unique ports so multiple branches can run dev servers simultaneously without conflicts.
Important: In a worktree, never assume default ports from docs. Always run pnpm worktree:context --json first.
Common Commands
| Task | Command |
|---|---|
| Start everything | pnpm dx |
| Run tests | pnpm test |
| Run a single test | pnpm test path/to/file.test.ts |
| Integration tests | pnpm test:integration |
| Type check | pnpm typecheck |
| Lint + fix | pnpm lint:fix |
| Format + fix | pnpm format:fix |
| Full quality check | pnpm check |
| Strict check (no fix) | pnpm check:strict |
| Generate DB migration | pnpm db:generate |
| Apply migrations | pnpm db:migrate |
| Reset database | pnpm db:reset |
| Browse DB (Drizzle Kit) | pnpm db:studio |
Editor Setup
Install the Biome VS Code extension for real-time lint feedback. The ESLint extension is also recommended for the custom guard rules.
See Linting & Formatting Strategy for the full rule set.
Troubleshooting
pnpm install fails with 404 errors
You need a TipTap Pro token. See TipTap Pro Token above.
Docker containers won't start
Check that Docker Desktop is running and no other containers are using the same ports:
docker ps # List running containers
docker compose -f docker-compose.dev.yml down # Stop Toast containers
docker compose -f docker-compose.dev.yml up -d # RestartDatabase connection errors
Verify the database is healthy:
docker compose -f docker-compose.dev.yml ps # Check container status
pnpm db:migrate # Re-run migrations
pnpm db:reset # Nuclear option: reset and re-seedPort already in use
If a dev server fails to start because a port is taken:
lsof -i :3000 # Find what's using the port
pnpm worktree:ports:check # Check for worktree port collisions