Upgrades and Troubleshooting
Keep your self-hosted Glassy updated with docker compose pull or auto-update with Watchtower, check container health, and diagnose common issues.
Before you start
- self-hosting/01-deploying-with-docker
What you'll learn
- Upgrade Glassy to the latest version
- Set up automatic updates with Watchtower
- Check container health and readiness
- Debug from inside the container
- Diagnose common startup and runtime issues
Reference documentation
Read the technical docs for this feature →Keeping Glassy updated is simple — a two-command pull and recreate. This lesson covers upgrades, auto-updates, health checks, and diagnosing common problems.
Upgrading
Manual upgrade
Point GLASSY_TAG at the new release and pull
docker compose pullFirst set GLASSY_TAG in .env to the released version you want (see the
releases page) — never latest,
which is the hosted build and omits self-host features. docker compose pull
then fetches that version from GHCR.
Recreate the container
docker compose up -dThe new image starts. Database migrations apply automatically on startup — no manual intervention needed.
The entire process takes under a minute. Your data is safe — it lives in the glassy-data volume, which persists across container recreations.
Pinning a version before upgrading
If you want to stay on a specific released version instead of following every release:
GLASSY_TAG=v2.58.0
Then docker compose pull && docker compose up -d. To move forward later, change GLASSY_TAG to the newest released version (see the releases page) and re-run. Never set GLASSY_TAG=latest — the floating :latest tag is the hosted build and omits self-host features.
Automatic updates with Watchtower
The bundled Watchtower overlay exists for hands-off updates, but it only recreates containers whose image tag actually moves — and a pinned released version (the recommended setup) never moves, so in practice upgrading stays a manual re-pin:
docker compose -f docker-compose.yml -f docker-compose.watchtower.yml up -d
Watchtower polls daily and recreates the container when its image tag moves; with a pinned GLASSY_TAG it stays quiet. Migrations run automatically whenever a new container starts.
Health checks
Readiness endpoint
curl http://localhost:3000/ready
Returns JSON when healthy:
{"status":"ready", ...}
If you get HTML instead of JSON, the container is still starting up — wait a few seconds and retry.
Container logs
docker compose logs glassy
Look for Server listening on port 8080 (the internal port) to confirm startup. Error lines are prefixed with ❌.
In-app health
Open the Account HUD → Health card. It shows real-time server, database, and API status.
Debugging from inside the container
The runtime image includes curl for network diagnostics:
# Check if Ollama is reachable from the container
docker compose exec glassy curl -s http://host.docker.internal:11434/api/tags | head -c 200
# Check if Obsidian's Local REST API is reachable
docker compose exec glassy curl -s http://host.docker.internal:27124/ -H "Authorization: Bearer YOUR_KEY"
Common issues
Container won’t start
docker compose logs glassy
Common causes:
- Missing
JWT_SECRETorAPI_KEY_ENCRYPTION_KEY— both are required. Generate withopenssl rand -hex 32. - Membership verification failed — look for
❌lines. EnsureGLASSY_MEMBER_EMAILmatches an active membership and the cloud was reachable on first boot. Also ensureGLASSY_SELFHOST_TOKENmatches the token generated in Settings → Self-hosting on app.glassy.fyi. - Port conflict — if port 3000 is in use, set
APP_PORT=3001.
Membership verification failed
The appliance must reach app.glassy.fyi on first boot to verify your membership. If your server has no internet on first boot:
- Connect to the internet temporarily
- Start the appliance and complete first-boot verification
- After that, you can go offline for up to 30 days
If your membership expired, renew it at clear.glassy.fyi and restart the container.
Obsidian not connecting
Browser extension bridge setup (Windows/WSL2 — the only working path):
- Install the Obsidian Local REST API plugin (v4.0+) in Obsidian. Copy the API key.
- Install the Glassy Capture extension in Chrome or Firefox.
- Sign in to the extension with your Glassy account.
- Extension popup → Settings → Obsidian Bridge → enter URL (
http://127.0.0.1:27123) and API key → toggle on → Test Connection (tests the full bridge loop) → Save. - Verify:
curl -H "Authorization: Bearer <jwt>" http://localhost:3000/api/ext/obsidian-bridge/status→{"connected":true}.
If the extension says “Bridge connected” but the server says not connected:
- Update to extension v2.20.1 — older versions held the SSE in the MV3 service worker (Chrome evicts after ~30s), and v2.12.0–v2.13.0 lacked localhost host permissions for the self-host Glassy server URL. v2.14.0+ uses the offscreen document (persistent, never evicted) and broadens
optional_host_permissionsto cover any localhost port. v2.15.0 adds achrome.alarmsheartbeat (survives SW eviction) that recreates the offscreen doc after 2+ missed heartbeats. v2.16.0 adds the MCP Settings UI for fetching your MCP key and generating client config snippets. v2.17.1 is the save & sync reliability release (capture-rule pre-population, 5xx-resilient sessions, offline-queue pause semantics, popup offline queueing, wildcard glassy.fyi host permissions). v2.18.0 adds bridge transport v2 — raw markdown bodies + request headers (If-Match, Content-Type, Accept) traverse the bridge and upstream ETag is relayed back, so vault writes (tap-to-toggle checkboxes, add-under-heading, daily-note append, push-to-vault) succeed over the bridge on containerized self-host where only the extension can reach Obsidian; older companions saw reads work but writes 502. v2.18.1 adds bridge reconnect hardening — the offscreen SSE reconnect delay drops 5000ms → 1000ms, so a dropped bridge recovers within a second (pairs with the server heartbeat fix in dash beta.28). - Update the server image to v2.35.0-beta.11+ if the bridge cycles every ~60 seconds. This is a server-side race condition (a stale SSE close handler deleting a newer connection in the registry), not a client issue. The fix tags each connection with a unique
connectionIdand only removes the connection if the IDs match. - Verify
CLUSTER_WORKERS=1in the container:docker exec glassy env | grep CLUSTER. - Toggle the bridge off and on in the extension popup to force a fresh SSE connection.
If server logs show 401 Invalid or expired SSE ticket:
- Update the server image to v2.35.0-beta.9+. This is a server-side bug fixed in beta.9: two overlapping routers ran
authtwice on each bridge request, consuming the one-time SSE ticket on the first run. The extension silently fell back to the less-secure?token=<JWT>URL form (so the bridge still worked), but the JWT was leaking into server/proxy logs. beta.9 reorders the router mounts and adds a regression test. No extension or Obsidian configuration change is required.
For native Linux/macOS (direct path):
- Verify the Local REST API plugin is enabled in Obsidian (v4.0+)
- Check the plugin is bound to
127.0.0.1(default) or0.0.0.0for split-machine setups - Check
OBSIDIAN_NETWORK_ALLOWLISTif Obsidian is on a different machine
AI not working (NO_AI_PROVIDER)
This means no cloud AI provider is configured. Either:
- Install and run Ollama (see Ollama: Local Models)
- Add your own API key via BYOK (see BYOK)
Browser WebGPU AI still works for embeddings and small tasks regardless.
Slow performance
- Check
CLUSTER_WORKERS— set to 1 on a small box, raise on multi-core - If running Ollama, ensure you have enough RAM (4 GB+ recommended)
- Check disk space — a full disk causes SQLite errors
Getting help
For issues not covered here:
- Check the full troubleshooting guide
- Review the Glassy Docs
- File an issue on GitHub
You’ve completed the Self-Hosting track
Your self-hosted Glassy appliance is deployed, understood, backed up, and maintained. Explore the other tracks:
- Getting Started — the complete orientation
- Glassy Capture — master the browser extension
- Local AI — on-device AI, Ollama, and BYOK