Skip to content

Chuck Local Development

Chuck Local Development

How to run Chuck (Strapi 5 in apps/chuck/meat/) locally. The package manager for this app is pnpm (apps/chuck/meat/package.json"packageManager": "pnpm@10.30.2"), not yarn or npm.

Prerequisites

ToolRequiredWhy
Node.js>= 18.0.0 <= 22.x.xStrict engine in package.json. Node 24 is not supported — boot fails. Use nvm use 22.
pnpm10.30.2 (per packageManager)Lockfile is pnpm-lock.yaml.
DockeroptionalFor local Postgres.
Postgres 14+recommendedProduction driver. SQLite works but is a fallback.

First-Run Bootstrap

Terminal window
cd apps/chuck/meat
pnpm install
cp .env.example .env

Generate fresh secrets

The .env.example ships with committed dummy secrets (APP_KEYS, API_TOKEN_SALT, ADMIN_JWT_SECRET, TRANSFER_TOKEN_SALT, JWT_SECRET). They are visible in git history and must not be reused outside throwaway local dev.

Terminal window
# Generate four 16-byte base64 keys for APP_KEYS
for i in 1 2 3 4; do node -e "console.log(require('crypto').randomBytes(16).toString('base64'))"; done
# And the salts / secrets
node -e "console.log(require('crypto').randomBytes(16).toString('base64'))" # API_TOKEN_SALT
node -e "console.log(require('crypto').randomBytes(16).toString('base64'))" # ADMIN_JWT_SECRET
node -e "console.log(require('crypto').randomBytes(16).toString('base64'))" # TRANSFER_TOKEN_SALT
node -e "console.log(require('crypto').randomBytes(16).toString('base64'))" # JWT_SECRET

Paste them into .env. See chuck-env for the full table of vars.

Postgres

Strapi defaults to Postgres in apps/chuck/meat/config/database.ts. The .env.example includes a one-liner for a local Postgres container:

Terminal window
docker run --name foxy-postgres \
-e POSTGRES_USER=root -e POSTGRES_PASSWORD=root \
-e POSTGRES_DB=strapi \
-p 5432:5432 -d postgres

Then in .env:

DATABASE_CLIENT=postgres
DATABASE_URL=postgres://root:root@localhost:5432/strapi
DATABASE_SSL=false

SQLite fallback

If you want zero-deps:

DATABASE_CLIENT=sqlite
DATABASE_FILENAME=.tmp/data.db

The DB file lives at apps/chuck/meat/.tmp/data.db. Schema migrations run on every boot, so deleting the file is a clean reset.

R2 (Uploads)

For local dev you generally don’t need real R2 credentials — uploads will fail but everything else works. If you do need media working locally:

  1. Get an R2 token scoped to a dev bucket (Cloudflare dashboard).
  2. Fill CF_ACCESS_KEY_ID, CF_ACCESS_SECRET, CF_ENDPOINT, CF_BUCKET, CF_PUBLIC_ACCESS_URL in .env.

CF_PUBLIC_ACCESS_URL is also used by CSP middleware (apps/chuck/meat/config/middlewares.ts), so a wrong value will block images in the admin panel even if the upload itself succeeds.

Email (SMTP)

Optional locally. Leave SMTP_* empty and the email plugin will fail on send (acceptable for content work). If you need it, point at a local SMTP catcher (e.g. Mailpit or mailhog/mailhog Docker image).

Run

Terminal window
cd apps/chuck/meat
pnpm develop # dev mode with auto-reload + admin panel rebuild

This starts Strapi on HOST:PORT (.env defaults map to http://localhost:1333). The admin panel is at /admin.

Other useful commands:

Terminal window
pnpm build # build admin panel for production
pnpm start # run already-built process (no auto-reload)
pnpm strapi --help

Admin User Bootstrap

On first boot (with an empty DB), open http://localhost:1333/admin. Strapi prompts you to create the first admin in the browser. There is no CLI for this in Strapi 5 — the form is the only path.

For an empty Postgres DB you only need to do this once. Subsequent admin users are created from /admin → Settings → Users.

If you ever need to re-bootstrap with a wiped DB:

Terminal window
# Destructive — wipes everything
docker rm -f foxy-postgres # if using docker
docker run --name foxy-postgres ... postgres # recreate
# Or for SQLite:
rm -rf apps/chuck/meat/.tmp/data.db

API Token for Brisket Locally

To exercise the brisket → chuck path locally:

  1. pnpm develop chuck.
  2. Create the first admin via the browser.
  3. /admin → Settings → API Tokens → “Create new API Token”.
  4. Choose Read-only (or Full access for testing). Copy the token.
  5. In brisket’s .env:
    STRAPI_URL=http://localhost:1333
    STRAPI_TOKEN=<paste>

Content Seeds

There is no checked-in content seed for chuck — content is created in the admin panel and persisted in the DB. To bring local up to a production-like state, restore a pg_dump from staging or use pnpm strapi transfer:

Terminal window
# Pull from a deployed instance (interactive)
cd apps/chuck/meat
pnpm strapi transfer --from https://<src-host> --from-token <transfer-token>

The transfer token is salted with TRANSFER_TOKEN_SALT (chuck-env). Treat snapshots like prod data — do not commit them.

TODO(@marty): document whether a sanitized seed dump exists in shared storage, and where — no seed file or fixture is checked into apps/chuck/ (find apps/chuck -iname 'seed*' -o -iname 'fixture*' returns no matches).

Verifying the Install

Terminal window
# 1. Health
curl -sf http://localhost:1333/_health -o /dev/null && echo health-ok
# 2. Public-or-tokened content
curl -s http://localhost:1333/api/checkout-faq-section?populate=* \
-H "Authorization: Bearer $STRAPI_TOKEN" | jq '.data.id // .error'
# 3. Admin reachable
curl -sI http://localhost:1333/admin | head -1

If all three return success, brisket can be pointed at this instance.

Troubleshooting

  • “engine ‘node’ is incompatible”nvm use 22.
  • pnpm: command not foundcorepack enable && corepack prepare pnpm@10.30.2 --activate.
  • Boot exits with Missing API_TOKEN_SALT — copy .env.example and fill it; pnpm does not load env from anywhere else.
  • Admin login loopAPP_KEYS changed since last login; clear cookies for localhost:1333.
  • relation "..." does not exist — DB out of sync. With SQLite, delete .tmp/data.db. With Postgres, drop and recreate the strapi database.
  • More in chuck-errors.

Anchor Docs