WarmblyDocs

Troubleshooting

The errors self-hosters actually hit, what causes each one, and the command that fixes it.

Search this page for the exact string you saw. Every row names the cause and the command, not a category.

I clicked register and got a 403

403 This server is not accepting new accounts. Ask an administrator for an invitation.

Nothing is broken, and you do not need to change any setting to get in.

A self-hosted instance ships with DISABLE_REGISTRATION=invite_only, which closes the sign-up form to the public. There is exactly one exemption: while the users table is empty, any signup is allowed, and that is what makes a fresh instance claimable. Once one account exists, the exemption is spent for good.

The usual sequence behind that 403 is:

  1. you ran make dev at some point, which seeds nine fixture accounts by default
  2. make dev and make up pin the same compose project, the same warmbly_postgres_data volume and the same warmbly_dev database, so those accounts are your instance's accounts
  3. make up therefore found a claimed instance, issued no setup link, and printed none
  4. the sign-up form was the only path left, and invite_only refuses it

Pick the fix that matches what you want:

You wantDo this
An account for yourself, right nowdocker compose -p warmbly exec backend warmblyctl user create --email [email protected] --admin
To recover an account that already existsdocker compose -p warmbly exec backend warmblyctl user reset-password --email [email protected] and open the printed URL
To let teammates inInvite them from Settings > Members and share the link. See inviting teammates
A truly fresh startmake reset && make up. This destroys the volumes, including the per-organization encryption keys
Open public signupDISABLE_REGISTRATION=false and restart. Read the consequence first: opening public signup

Not sure which state you are in?

make doctor

Newer builds return the invite-only refusal with its own machine code, registration_invite_only, and the text "This server is invite only. Ask an administrator to invite you, then open the link in the invitation to create your account." Same cause, same fixes.

First run and sign-in

SymptomCause and fix
403 This server is invite only. when registeringDISABLE_REGISTRATION defaults to invite_only on self-host. Open the invitation link that carries the token, run warmblyctl user create --email [email protected] --admin, or set DISABLE_REGISTRATION=false and restart. See accounts and access
403 This server is not accepting new accounts.DISABLE_REGISTRATION=true. No invitation overrides it. Only warmblyctl user create adds accounts
Signup worked once, then stoppedThe first-launch exemption applies only while the users table is empty. See the first-launch exemption
make up printed no claim linkThe database already has accounts, usually from a previous make dev. Use warmblyctl user create --email [email protected] --admin, or make reset to start over, which destroys the encryption volume
The claim link says invalid or expired24 hour lifetime, single use, and it does not survive a Redis flush. Print a new one with warmblyctl setup-link
/setup says the instance is already set upAn account exists, so no claim link will ever be issued. warmblyctl user reset-password --email ..., or warmblyctl user create --email [email protected] --admin
An invited person gets invitation_invalidThe invitation expired (7 days by default), was cancelled, was already used, or they typed a different address than the one it was issued for. Re-invite them
Single sign-on succeeds at the provider then 404sThe redirect URI must be <API_PUBLIC_URL>/v1/auth/oidc/callback, not /api/v1/.... It has to match on both sides exactly. See single sign-on
Single sign-on stopped provisioning new usersProvisioning now obeys DISABLE_REGISTRATION. Invite the person, or set SSO_AUTO_PROVISION=true and restart
No login code arrivesSelf-host does not send one (AUTH_LOGIN_CODE=off). Registration and reset codes go wherever MAIL_TRANSPORT points; under compose that is the backend log
No invitation email arrivesSame cause. Copy the link from Pending invitations and send it yourself. See without a mail relay
"Too many attempts" on password resetPassword reset is capped at 2 requests per address per 4 hours. Use warmblyctl user reset-password instead of waiting
The passkey button is missingPasskeys need a secure context, so APP_URL must be HTTPS or a localhost address. A LAN IP over plain HTTP cannot be a relying party
Google, Apple or passkey buttons appear but never workThe sign-in screen follows GET /auth/config. If they still appear, that request is failing and the screen says which API URL it tried
Locked out of the only admin accountwarmblyctl user create --email [email protected] --admin. Every recovery command is on recovering access

The build fails

SymptomCause and fix
no space left on device, often from a random service mid-compileDocker is out of disk. Free space with docker builder prune -af and docker image prune -af, check the host has about 10 GB free, then re-run make up
failed to authorize: ... EOF while pulling a base imageA transient registry blip. Re-run make up; completed layers are cached
failed to xattr /path/._something: operation not permitted on macOSThe checkout is on a filesystem without native extended attributes (exFAT, NTFS or a network share), so macOS writes ._* sidecar files that BuildKit cannot read. Run dot_clean -m . then find . -name '._*' -delete and re-run. Cloning to an APFS volume avoids it

The stack is up but something is wrong

SymptomCause and fix
The backend exits at bootRead the first log lines. The usual causes are a published default secret with APP_ENV=prod (the message names ALLOW_INSECURE_DEFAULTS), cloud provider defaults outside compose, a CREDENTIALS_ENCRYPTION_KEY that is not exactly 64 hex characters, or a missing EMAIL_ADDRESS, EMAIL_NAME or GEODB_PATH
The dashboard loads but every request failsCheck http://localhost:5173/config.js returns the right API_URL. It is generated at container start from WARMBLY_API_URL. If the URL is right, the origin is probably missing from CORS_ALLOW_ORIGINS
Nothing updates live and presence is emptyAUTH_SECRET must equal realtime's JWT_SECRET, and PUBSUB_ENABLED must agree across backend, consumer and realtime. Compare the fingerprints under Instance > Configuration
Workers or tracking get 401sINTERNAL_API_TOKEN must match on the backend, the workers (as ENCRYPTED_KEYS_WORKER_TOKEN) and tracking. Unset fails closed
"No mailbox workers are available" when connecting a mailboxNo worker has a heartbeat inside the last 10 minutes. Check make status shows worker running and make logs worker is clean. An empty ENCRYPTED_KEYS_BACKEND_URL or worker token lets a worker start and never register, silently
Connecting a mailbox is rejected on the portSMTP must be 587 or 465. The Mailpit sink used by make sandbox listens on 1025 with no STARTTLS, so it cannot be used as a test mailbox
A mailbox stalls after about an hourThe worker is missing BOX_GOOGLE_* or BOX_OUTLOOK_*. The backend starts the OAuth flow but each worker refreshes the token. Set them and restart the worker
Scheduled sends never fireDelayed sends run through the in-process Postgres task poller (TASKS_PROVIDER=local), so the backend must be running
Every send dead-letters with permission denied on /data/blobsThe blobs volume was created before the images owned that path, so it is still root:root while the services run as uid 1000. Fix it once with docker compose -p warmbly exec -u root backend chown -R warmbly:warmbly /data/blobs. The blob_fs_root health check reports it, and volumes created from current images are already correct
email account <id> not found in workerThe mailbox is assigned to a worker that no longer exists, usually because the worker was recreated without a pinned WORKER_ID and came back with a fresh UUID. The reconciler releases and re-places it on a live worker within its interval; pin WORKER_ID to stop it recurring. See worker identity
Opens and clicks never recordFirst check the container is actually up with docker compose -p warmbly ps -a: a dead tracking breaks nothing else, because sends do not wait on it. Then check TRACKING_DOMAIN resolves and the service answers on /health. If you overrode KAFKA_TRACKING_TOPIC, it has to be overridden for the Rust publisher and the Go subscriber together
tracking exits immediately with Bind for 0.0.0.0:3000 failed: port is already allocatedSomething else on the host owns port 3000, a very common default. Set TRACKING_PORT=3001 in .env, re-run make up, and point your reverse proxy's tracking host at the new port
Seeding fails with no migration found for version NThe seed image is older than your schema. Re-run with --build
Worker install_state: errorTest connection first (is the SSH key in authorized_keys?), then read last_error and Logs on the worker's detail page
Worker heartbeat offlineCan the VPS reach the backend URL, NATS or Kafka, and Redis? Is the container running (Live status)?

Recovery

Every recovery command, in all four runtimes, is on recovering access: instance status, listing admins, resetting a password through a printed one-time link, creating a new owner and admin, clearing a lost authenticator, and reissuing the first-run claim link.

The destructive alternative is make reset, which removes the volumes and starts over.

make reset destroys the encryption volume

The volumes hold the per-organization data keys. Every sealed mailbox credential and every stored message body becomes permanently undecryptable, even if you restore a database dump afterwards.

Do not flush Redis to clear a rate limit

redis-cli FLUSHDB also destroys bootstrap:setup_token, every pending auth session and every login attempt counter. On an unclaimed instance that throws away the only way in. Delete the specific key, or wait out the window.

Still stuck

make logs            # follow everything
make logs backend    # follow one service
make doctor          # the full health check set, exits non-zero on an error

Ask in Discord or open a GitHub issue with the output of make doctor and the first 50 lines of the failing service's log.

See also

On this page