> ## Documentation Index
> Fetch the complete documentation index at: https://help.plannotator.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshoot Self-Hosted Workspaces

> Fix container boot, OIDC, Cloudflare Access, storage, raw-link, update, and restore problems.

Start with the process logs and `/healthz`, then test a real sign-in and one
write. Health proves that Workspaces started. It does not prove OIDC discovery,
Cloudflare Access, free disk space, or every storage write.

## Container boot and health

| Result                                             | Cause and recovery                                                                                                                                                                      |
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The container exits before port `8790` opens       | Read `docker logs rooms`. The log lists missing settings, secrets that are too short, an unwritable data directory, an unsupported auth mode, or another process holding the data lock. |
| A second container fails on the same volume        | Stop the first process. Workspaces supports one process per data directory. Do not overlap old and new containers during an update.                                                     |
| The data directory is not writable                 | Use a named Docker volume or make the bind mount writable by user ID `1000`. Do not use NFS, SMB, EFS, or another shared network filesystem.                                            |
| `/healthz` returns `200` but writes fail           | Check free disk space and the container logs. Health does not write to SQLite or a live-edit log.                                                                                       |
| One document stops live editing after a disk error | Free disk space. Workspaces closes that document's live session instead of sending an edit it could not save. Reopen the document after storage writes recover.                         |

## OIDC sign-in

| Result                                                   | Cause and recovery                                                                                                                                                                                                                                                   |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Log in** fails before the provider opens               | Check that the HTTPS issuer is reachable and returns valid OpenID Connect discovery data. For a private CA, mount its certificate and set `NODE_EXTRA_CA_CERTS`.                                                                                                     |
| The provider rejects the callback                        | Register the exact public app origin followed by `/auth/callback`. Confirm the client ID and optional client secret match that registration.                                                                                                                         |
| The browser returns to the signed-out page               | Serve the app over HTTPS. Workspaces marks its sign-in cookies `Secure`. Keep `OIDC_COOKIE_SECRET` stable across restarts.                                                                                                                                           |
| Workspaces returns `401 unauthorized` after the callback | Start a fresh login. Verify the registered callback, client ID, client secret, and that the ID token supplies `sub` and `email`. Workspaces returns a generic `401`; use the identity provider's logs for token-exchange details. Do not reload an old callback URL. |
| The provider accepts only a public client                | Remove `OIDC_CLIENT_SECRET` only after registering Workspaces as a public client. PKCE remains enabled.                                                                                                                                                              |

Workspaces can start even if OIDC discovery later fails because discovery begins
at the first login.

## API keys

| Result                                            | Cause and recovery                                                                                                                          |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| You cannot create the first key                   | Sign in through the browser first. Key creation and management require a human session.                                                     |
| A key is missing from **Settings** > **API keys** | Confirm you signed in as the key owner. Do not insert keys into SQLite. A database-created key may have no owner who can list or revoke it. |
| A revoked key still appears to work               | Stop the client and send a new request. A request already in progress may finish; new requests should return `401 unauthorized`.            |
| Removing sign-in access leaves automation working | Removing a person's sign-in access does not revoke their API keys. Restore sign-in so the key owner can revoke each key.                    |

## Backup, restore, and update

| Result                                                       | Cause and recovery                                                                                                                                                        |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A cold restore will not boot                                 | Restore into a fresh volume and confirm the archive includes `.plannotator-ready`. Use the image version that created the backup or a newer one.                          |
| A hot restore fails on the lock or SQLite                    | Set user and group ownership of all restored files to ID `1000`. Write `.plannotator-ready` only after you copy every data directory.                                     |
| Assets are missing after a hot restore                       | Repeat the backup and include `/data/assets` after metadata, live logs, and Git repositories.                                                                             |
| An old container reports unknown migrations                  | Do not start it on the migrated volume. Restore the pre-update backup into a fresh volume and use the old image there.                                                    |
| A Cloudflare D1 restore fails                                | Apply the source migrations to a fresh D1 database, then import data-only `INSERT` statements in parent-first order. Do not replay a raw `wrangler d1 export` file as-is. |
| `wrangler d1 execute --file` exits `0`, but rows are missing | The command may have canceled at a confirmation prompt. Compare row counts with the backup before trusting the restore.                                                   |
| An older Cloudflare deployment needs a newer schema          | Do not deploy old source against changed data. Restore the pre-update D1 backup and use the matching source version.                                                      |

## Cloudflare deployment

| Result                                                    | Cause and recovery                                                                                                                                              |
| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The browser app returns 404 or an API-only response       | Run `pnpm build:web:rooms` before deploying the app Worker.                                                                                                     |
| The first request fails after you deploy                  | Retry for up to one minute. A new Worker origin can take a short time to become ready.                                                                          |
| Workspaces fails because the Artifacts binding is missing | Enable Artifacts, create the namespace through a bootstrap repository, and use the same namespace in both Worker configs. There is no supported fallback store. |
| Access never shows a login screen                         | Use a custom domain and confirm the Access application covers the app origin. The default `workers.dev` address is only for pre-Access checks.                  |
| Workspaces returns `401` after Access                     | Confirm the Access team domain and application audience in the app Worker config.                                                                               |
| An API-key or Git request opens an Access screen          | Use the public API origin. Remove that origin from the Access application. Only the browser app host belongs behind Access.                                     |
| A raw link returns `200 text/html`                        | The request reached the app Worker. Deploy the required usercontent Worker, use a separate origin, and set the app's `USERCONTENT_ORIGIN` to it.                |
| A raw link asks for Cloudflare Access                     | Remove Access from the usercontent origin. Protect only the browser app host. Leave the public API and usercontent hosts outside Access.                        |
| A manual WebSocket check fails over HTTP/2                | Repeat the `curl` check with `--http1.1`. Browser WebSockets do not need this option.                                                                           |

## Collect safe support details

Include these facts in a support request:

* container image version and source commit from `/healthz`, or both Worker
  source commits;
* the failing route without its query string or share credential;
* the HTTP status and content type;
* the deployment type and auth mode; and
* the relevant error line with private values removed.

Do not send environment files, secret values, API keys, cookies, Access
assertions, callback codes, account IDs, private hostnames, or raw share URLs.

<CardGroup cols={2}>
  <Card title="Container backup and restore" icon="database" href="/workspaces/self-hosting/container/backup-and-restore" arrow="true">
    Restore a cold or hot backup into a fresh volume.
  </Card>

  <Card title="API, MCP, and Git errors" icon="terminal" href="/workspaces/troubleshooting/api-mcp-and-git" arrow="true">
    Fix authentication and agent connection errors.
  </Card>
</CardGroup>

*Reviewed July 22, 2026. Maintained by the Plannotator documentation team.*
