Troubleshooting

Begin with the narrowest check that distinguishes host ingress from container runtime. Keep .env unread and never use the rendering form of Compose configuration.

Compose validation fails

cd ~/dev/lobe
sudo -n docker compose config --quiet
  • if this fails:
    • check names and placeholders against .env.example
    • confirm .env exists and remains mode 0600
    • do not run the non-quiet rendering command to inspect interpolation
  • if validation passes but startup fails:
    • run sudo -n docker compose ps -a
    • inspect only the affected service with sudo -n docker compose logs --tail=100 <service>

Lobe remains pending or unhealthy

Lobe is health-ordered behind PostgreSQL, Redis, RustFS, and successful rustfs-init completion.

sudo -n docker compose ps -a
sudo -n docker compose logs --tail=100 lobe
sudo -n docker compose logs --tail=100 postgresql redis rustfs rustfs-init
  • PostgreSQL must pass pg_isready -U postgres.
  • Redis must answer redis-cli ping.
  • RustFS must answer its internal /health check.
  • rustfs-init must exit 0; a nonzero result blocks Lobe startup.
  • Lobe’s own healthcheck fetches http://127.0.0.1:3210/ from inside the container.

Do not delete an authoritative volume to clear a health failure.

LiteLLM is unreachable

The valid route is container DNS over external network lllm_default, not a host gateway.

  • expected proxy: http://lllm-proxy:4000/v1
  • expected key: the scoped virtual key stored as OPENAI_API_KEY
  • expected aliases: fast, oss-120b, free
sudo -n docker network inspect lllm_default >/dev/null
sudo -n docker exec lobe node -e "fetch('http://lllm-proxy:4000/health/readiness').then(r=>{console.log(r.status);process.exit(r.ok?0:1)}).catch(e=>{console.error(e.message);process.exit(1)})"
  • name-resolution or connection failure:
    • verify lllm-proxy exists on lllm_default
    • verify only the Lobe service needs the external network
  • 403 for one of the three approved aliases:
    • verify the lobe virtual key scope and OPENAI_MODEL_LIST=-all,+fast,+oss-120b,+free
  • success for an unlisted model:
    • treat as a scope regression; the verified contract is rejection with 403

Never substitute the LiteLLM master key. Use the redaction-safe probes in verification.

OIDC discovery or callback fails

sudo -n docker exec lobe node -e "fetch('https://auth.loca.zone/.well-known/openid-configuration').then(r=>{console.log(r.status);process.exit(r.ok?0:1)}).catch(e=>{console.error(e.message);process.exit(1)})"

Check the exact client contract:

  • client ID lobe
  • issuer https://auth.loca.zone
  • redirect URI https://lobe.loca.zone/api/auth/callback/authelia
  • scopes openid, profile, email
  • authorization code flow
  • client_secret_basic
  • policy one_factor
  • implicit consent

Interpret authorization markers directly:

  • invalid_client: client ID, secret pair, or token authentication mismatch
  • invalid_redirect_uri: callback does not exactly match the registered URI
  • invalid_scope: requested scopes drifted from the registered set
  • account lacks usable identity after callback: verify the Authelia identity still emits a non-empty email claim

Do not enable email and password login as a workaround. Do not expose either plaintext client secret or its digest.

Desktop or API receives an Authelia HTML redirect

This is an edge-gate regression, not the native OIDC flow.

  • deploy/lobe.nginx.conf must contain no auth_request on lobe.loca.zone.
  • Authelia edge access-control rules must not name lobe.loca.zone.
  • Browser sign-in should reach Authelia only after LobeHub initiates OIDC.

Restore the application-owned identity boundary described in auth-model.

Upload or attachment fails

curl -fsS http://127.0.0.1:52101/health >/dev/null
sudo -n docker compose ps -a rustfs rustfs-init
sudo -n docker compose logs --tail=100 rustfs rustfs-init

Check:

  • S3_ENDPOINT is exactly https://s3.lobe.loca.zone with no path suffix
  • path style is enabled
  • bucket is lobe
  • S3 ACL mutation is disabled
  • RustFS console remains disabled
  • nginx preserves Host and Origin, leaves the object path unchanged, disables request buffering, and allows up to 128m
  • browser requests use HTTPS and show no CORS, mixed-content, or 413 error

The bucket policy intentionally makes uploaded object URLs anonymously readable. A successful anonymous read is not evidence of a write-permission leak; writes still require a valid signature.

Streamed replies stall

  • application loopback healthy but public stream stalls:
    • inspect the lobe.loca.zone nginx vhost
    • require proxy_buffering off and proxy_cache off
    • require 86400-second proxy read and send timeouts
    • preserve HTTP/1.1 upgrade and forwarding headers
  • both loopback and public requests fail:
    • inspect Lobe and dependency status before nginx

Search fails

SearXNG is private and resolves as http://searxng:8080 on lobe-network.

sudo -n docker compose ps searxng
sudo -n docker compose logs --tail=100 searxng

The tracked settings file remains read-only. Its public placeholder secret_key is overridden by SEARXNG_SECRET from .env; do not reveal the override value while diagnosing.

Wiki fails while the app is healthy

The wiki is static and does not depend on Compose.

  • confirm the wiki current symlink resolves to a completed dist-* directory
  • confirm that release contains index.html
  • rerun the wiki lint and atomic build procedure
  • a missing route must return 404, not a fallback document

Certificate mismatch

One certificate lineage named lobe.loca.zone serves all three TLS vhosts. Its SAN set must include:

  • lobe.loca.zone
  • wiki.lobe.loca.zone
  • s3.lobe.loca.zone

If one host presents a different set, inspect the installed nginx vhost and the lineage paths before reload. The verified lineage expires 2026-11-27.