Linkwarden 1.21.1: full-text search on archived content is silently unavailable
-
Hey, another thing that I stumbled upon: the full-text search is not working on Cloudron. The search engine/db Meilisearch is missing. Here is a write up about it done together with AI (Claude Opus4.7):
Symptom
Linkwarden's main selling point — beyond bookmarking — is that it preserves the full text of every saved page (
textContent, extracted via Mozilla Readability and stored in PostgreSQL). On Cloudron'sapp.linkwarden.cloudronapp@1.21.1this content is correctly archived and shows up on link records. But the search box does not actually search it.Easy way to reproduce on any 1.21.1 instance:
- Save a link whose article body contains a distinctive word that does not appear in the title, URL, description or any tag.
- Wait for preservation to finish; confirm via
GET /api/v1/links/<id>thattextContentis populated and contains the word. - Search for that word in the UI (or
GET /api/v1/links?searchQueryString=<word>). - Result: zero hits.
So the bookmarks are preserved, but the preserved content is functionally invisible to the search UI. That's surprising — and (as far as I can tell) not mentioned anywhere in the Cloudron app docs or in the forum.
Root cause
apps/web/lib/api/controllers/search/searchLinks.tshas two code paths:if (meiliClient && query.searchQueryString) { // → Meilisearch path: indexed search across all fields incl. textContent } else { // Fallback: No Meilisearch searchConditions.push({ name: { contains: q } }); searchConditions.push({ url: { contains: q } }); searchConditions.push({ description: { contains: q } }); searchConditions.push({ tags: { some: { name: { contains: q } } } }); // ← textContent is intentionally not in the WHERE clause }The switch is
MEILI_MASTER_KEY. Linkwarden's own env-vars docs state it plainly: "Linkwarden only initializes the MeiliSearch client when this value is set." The Cloudron package ships with noMEILI_*env vars, so the client isnull→ fallback path → archived body text is never queried.The indexing worker (
apps/worker/workers/linkIndexing.ts) is present in the image and ready to push records to a Meilisearch instance — it just has no instance to talk to.Upstream's position
Linkwarden's reference
docker-compose.ymltreats Meilisearch as a first-class sidecar, not an optional add-on:linkwarden: depends_on: - postgres - meilisearch meilisearch: image: getmeili/meilisearch:v1.12.8The docs describe content-level search ("Advanced Search") as the default expectation for self-hosters running ≥ 2.10.
Cloudron's single-container packaging model legitimately makes that hard, but the resulting feature gap isn't currently visible to users.
Suggestion
Three options, in increasing order of effort:
-
Doc-only fix. Add a "Limitations" / "Optional: full-text search" section to the Cloudron Linkwarden app docs explaining that the bundled image runs in the no-Meilisearch fallback mode, what that costs (no body-text search, no advanced operators), and pointing users at
MEILI_HOST/MEILI_MASTER_KEYif they want to bring their own instance. This already works today — the Linkwarden code reads those env vars unchanged, so a self-hoster can spin upgetmeili/meilisearchnext to Cloudron and point Linkwarden at it viacloudron env set. -
Make the integration discoverable. Document
MEILI_HOST/MEILI_MASTER_KEYas recognised env vars on the app page (they already work viacloudron env set— they're just not advertised). A startup log line on the worker — "Meilisearch disabled — full-text search on archived content will not be available" — would also turn the silent degradation into a visible one. -
Bundled sidecar. Ship Meilisearch alongside the Linkwarden container in the package. I understand from topic #2518 that a standalone Meilisearch app was deemed out of scope ("like a database … not really like a web app"), so I'm not asking for that — but bundling it as an internal dependency of the Linkwarden package would side-step that objection while bringing the package in line with upstream's expected architecture.
(1) seems strictly better than the current state regardless of whether (2) or (3) ever happen — right now users only learn the limitation by reading the source.
Verification offer
Happy to test any of the above on the 1.21.1 instance and report back. From reading the code I'd expect that pointing Linkwarden at an external Meilisearch via
cloudron env set MEILI_HOST=… MEILI_MASTER_KEY=…works without any package changes — the fallback should flip to the Meilisearch path on next restart and the indexing worker should backfill existing links — but I haven't tried it yet, and confirming this is exactly the kind of thing I'm offering to do.Possibly related
- docs.linkwarden.app/self-hosting/installation — upstream install guide listing Meilisearch as part of the standard setup.
- The Cloudron app currently runs
apps/worker/workers/linkIndexing.tsas a no-op every cycle; might be worth gating it (or at least the log lines) onMEILI_MASTER_KEYbeing set, to avoid future user confusion.
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login