Self-host it in about fifteen minutes.
Docker Compose brings up FrankenPHP and PostgreSQL together. No local PHP needed — the backend, the tests and the CLI all run inside the container.
What you need.
| Docker + Compose | Starts PostgreSQL and the FrankenPHP app together. The recommended local setup. |
| Composer | Run on the host, before the stack starts. See step 2 — this is the one that bites. |
| Node.js | For the Next.js admin client only. The backend does not need it. |
| PHP 8.4 | Inside the container (dunglas/frankenphp:1-php8.4). You do not need it locally. |
| PostgreSQL 15 | The only supported database. Compose starts it for you. |
Five steps.
Copy each block in order. The whole thing takes about fifteen minutes on a warm Docker cache.
Clone it
1 · clonebashgit clone https://github.com/AmroKSaleh/whity-core.git cd whity-coreInstall PHP dependencies — on the host, first
The development image deliberately ships no
vendor/, and Compose bind-mounts your checkout over/app— so the container runs against whatevervendor/your working copy has. If that is nothing,db-initexits255and the stack never comes up.2 · dependenciesbashcomposer installOn Windows, mirror the SDK instead of symlinking it. The plugin SDK is a Composer path repository, which Composer satisfies with a symlink — and a symlink made on a Windows host does not survive the bind mount into a Linux container, so everyWhity\Sdk\*class fails to autoload. Copy it instead:Re-run it after changing anything underwindowsbashCOMPOSER_MIRROR_PATH_REPOS=1 composer installsdk/.Start the stack
Set
JWT_SECRETandENCRYPTION_KEYin.envfirst if you are running anything other thanAPP_ENV=development— production fails fast on a missing or weak value rather than booting insecurely.3 · startbashdocker compose up -dCreate the schema
migrate runalready creates the bootstrap administrator, soseedis optional. Add it for the default tenant, roles and notification-template baseline.4 · migratebashdocker exec whity_frankenphp php public/index.php migrate run # optional: default tenant, roles, notification templates docker exec whity_frankenphp php public/index.php seedStart the web client
5 · webbashcd web npm install npm run devAPI on
localhost:8000, admin UI onlocalhost:3000, health atlocalhost:8000/api/health.
There is a shortcut for steps 3 and 4 once dependencies are installed:make setup runs docker compose up -d followed by the database initialisation script.
One line. Then it knows the rules.
Paste it into Claude Code, Cursor, or whatever you build with. Your agent fetches the setup instructions, works out whether it is looking at the core platform, a plugin project or an empty directory, and writes the project's conventions into your agent instructions file so they survive the session.
You can read exactly what it will execute first — the instructions are a plain markdown file, not a script.
Copies one line to your clipboard
Fetch and execute the appropriate instructions to set me up for Whity from https://whity.dev/agent-setup/prompt.mdLet it drive a real instance.
The line above is enough for an agent writing code. To let one operate a running Whity — create a tenant, assign a role, file a form — connect it to that instance's MCP endpoint. It gets exactly the permissions its token carries, enforced by the same component that guards the HTTP API.
Whity is self-hosted, so the endpoint lives on your own instance; there is no shared server to point at. Issue a token with a human access token, then configure your client.
curl -s -X POST https://your-whity-host/api/mcp/tokens \
-H "Authorization: Bearer <your-access-token>" \
-H "Content-Type: application/json" \
-d '{"name": "my-agent", "scope": ["tools:call"]}'{
"mcpServers": {
"whity": {
"url": "https://your-whity-host/mcp",
"headers": {
"Authorization": "Bearer <mcp-token>"
}
}
}
}Who you log in as.
Every environment gets a bootstrap administrator — system@whity.local by default, or whatever INITIAL_SYSTEM_ADMIN_EMAIL names. Set that to a mailbox you actually control: the default is unroutable, so it can never receive a password reset.
The *@example.com demo logins exist only underAPP_ENV=development, and only when you pass --with-fixtures toseed. Seed passwords are env-configurable; absent an env value a random one is generated and printed once.
Install FAQ.
What do I need to run Whity?
Docker and Docker Compose, Composer on the host, and Node.js for the web client. No local PHP is required — the backend, tests and CLI all run inside the php:8.4 / FrankenPHP container. PostgreSQL 15 is the only supported database and Compose starts it for you.
Why does the stack fail with 'vendor/autoload.php not found'?
Because composer install has to run on the host before docker compose up. The development image deliberately ships no vendor/ directory, and Compose bind-mounts your checkout over /app, so the container runs against whatever vendor/ your working copy has. If that is nothing, db-init exits 255. Only the release image target runs composer install for you.
Why does Whity fail to autoload Whity\Sdk classes on Windows?
The plugin SDK is consumed through a Composer path repository, which Composer satisfies by symlinking vendor/whity/plugin-sdk to the sdk directory. A symlink created on a Windows host does not survive the bind mount into a Linux container. Run COMPOSER_MIRROR_PATH_REPOS=1 composer install so Composer copies the SDK into vendor/ instead, and re-run it after changing anything under sdk/.
Do I need to run seed after migrating?
No. migrate run already creates the bootstrap administrator, so seed is not required for a working install. Run seed to add the default tenant, roles and notification-template baseline, and seed --with-fixtures for the demo logins, which exist only under APP_ENV=development.
Now run something.
The CLI handles migrations, tenants, plugins, translations and the background workers.