Troubleshooting
Container does not start
The entrypoint validates every setting before nginx starts. On a problem it prints one line on stderr, starting with sidedoc-entrypoint: ERROR:, and exits with code 64. Read it with docker logs <container> or kubectl logs <pod>. The messages below are listed with an example; the cause and fix apply to every setting that can produce them.
To test settings without starting the server, run only the entrypoint:
docker run --rm --read-only --tmpfs /tmp --env-file sidedoc.env sidedoc:0.6.1 trueSIDEDOC_SENTRY_DSN and SIDEDOC_SENTRY_DSN_FILE are both set
Cause: A setting is given both directly and as a file.
Fix: Remove one of the two.
SIDEDOC_SENTRY_DSN_FILE: cannot read /run/secrets/sentry_dsn
Cause: The file does not exist in the container, or uid 101 cannot read it.
Fix: Check the secret mount and its mode. Docker secrets and Kubernetes secret volumes are readable by default; host bind mounts may need chmod 0444 or a matching group.
SIDEDOC_REAL_IP_FROM must be a single line
Cause: The value, or the file contents without trailing newlines, contains a newline.
Fix: Put lists on one line, separated by spaces.
SIDEDOC_LOG_LEVEL: invalid value 'warning'
Cause: The value does not match the allowed pattern of that setting. Common mistakes: double quotes or a ; in a CSP source list, a Sentry DSN with a non-hex key or a non-numeric project id, a log level spelled warning.
Fix: Compare the value with the Allowed column on the Configuration page.
SIDEDOC_SENTRY_ENABLED: expected true/false, got 'enabled'
Cause: A boolean setting has another value.
Fix: Use true/false (or 1/0, yes/no, on/off).
SIDEDOC_PORT must be 1024-65535 (unprivileged)
Cause: The container runs as a non-root user and cannot bind ports below 1024.
Fix: Keep the container port at 8080 (or another high port) and publish it on 80/443 through the host or the proxy: -p 80:8080.
SIDEDOC_SCHEMA_VERSION=0.3.0: no bundled schemas/0.3.0 (have: 0.1.1 0.1.2 0.2.1 0.2.2 )
Cause: The image does not contain that schema version.
Fix: Pick one of the listed versions, or use an image that bundles the version (see Schemas).
SIDEDOC_SENTRY_ENABLED=true requires SIDEDOC_SENTRY_DSN
Cause: Sentry is enabled without a DSN.
Fix: Set SIDEDOC_SENTRY_DSN or SIDEDOC_SENTRY_DSN_FILE, or disable Sentry.
/tmp must be writable: mount a tmpfs (docker run --read-only --tmpfs /tmp, or an emptyDir)
Cause: The root filesystem is read-only and nothing writable is mounted at /tmp, or the mount is not writable for uid 101.
Fix: Add --tmpfs /tmp (Docker), tmpfs: [/tmp] (compose) or an emptyDir volume at /tmp (Kubernetes). With a tmpfs that sets uid=/mode=, make sure uid 101 can write.
generated nginx configuration is invalid
Cause: nginx -t rejected the rendered configuration. The lines before this message show nginx's own error. With validated settings this should not happen; a modified nginx.conf or an unusual CSP value are the likely causes.
Fix: Read the preceding nginx message. Report it as a bug if you run an unmodified image.
Container starts, but
The health check fails
/healthz is served on SIDEDOC_PORT (default 8080). The Docker HEALTHCHECK follows SIDEDOC_PORT; Kubernetes probes and load balancers must use the same port. A probe against the proxy in front, instead of the container, tests the proxy too.
The page is blank
Open the browser console.
Refused to load ... because it violates the following Content Security Policy directive: something in front of the container adds its ownContent-Security-Policyheader, or an extension injects scripts. Browsers enforce every policy they receive. Remove the extra header at the proxy.404forconfig.jsorassets/...when SideDoc runs under a sub-path: the proxy does not strip the prefix. See Serving under a sub-path.405responses: onlyGETandHEADare allowed. Some health checkers or proxies sendOPTIONSorPOST.
The access log shows the proxy's address, not the client's
Set SIDEDOC_REAL_IP_FROM to the address the container sees for the proxy, and make sure the proxy sends X-Forwarded-For. With Docker port publishing the address is often the bridge gateway (172.17.0.1), not 127.0.0.1.
Sentry receives nothing
- Check that
/config.jscontainsVITE_SENTRY_ENABLED: "true"and your DSN. - In the browser's network tab, look at
POST monitoring/envelope. A502or504means the container cannot reach the Sentry host: it needs outbound HTTPS to the host in the DSN. - Ad blockers rarely block the same-origin tunnel, but privacy extensions can.
A frame or embed is refused
SIDEDOC_CSP_FRAME_ANCESTORS defaults to 'self'. Add the embedding site, with the single quotes on keywords: SIDEDOC_CSP_FRAME_ANCESTORS="'self' https://portal.example.org". An unquoted self is accepted but means a host called self.
SIDEDOC_CSP_FRAME_ANCESTORS from a Compose env file loses its quotes
Compose env_file and .env files strip one level of quotes. Write "'self'" there, or put the value under environment: in compose.yaml. docker run --env-file does not strip quotes.
Documents disappeared after moving the site
The workspace lives in the browser and belongs to the origin. A new domain, port or scheme starts with an empty workspace. Users can import their exported documents; see Persistence.
Old documents open read-only
Their datamodel version is below SIDEDOC_MIN_SUPPORTED_SCHEMA_VERSION. Users can convert them. See Schemas.
Static hosting
Headers are missing on /schemas/ or /vocab/
nginx drops server-level add_header directives in any location that sets its own header, such as Access-Control-Allow-Origin. Include the headers again inside those locations, as in the nginx example.
A CSP added on the server has no effect
The default build also embeds a <meta> CSP, and the browser enforces both. Build with SIDEDOC_CSP_META=false when the server sends the policy.
npm run build fails about schemas
The backend prebuild step needs a schemas checkout in SIDEDOC_SCHEMAS_DIR. Use scripts/build-static.sh, which does not, or see Build with a schemas checkout.