Optional apps / Optional apps

Optional apps

The core web-app suite — notes, tasks, status, bookmarks, feeds, search, dev tools, and file sharing — each gated by an ENABLE_ flag.

Beyond the Matrix homeserver and Element, pocket-homeserver can install a suite of self-hosted web apps. They are all optional and off by default. You turn each one on with its ENABLE_<APP> flag in .env (the setup.sh wizard asks about each), then run the installer — every enabled app installs its backend and drops a Caddy vhost, and the edge comes up already aware of it. Each app’s installer lives in scripts/apps/ (e.g. linkding.sh) and is run by scripts/install.sh when its flag is on.

# in .env (or via ./setup.sh)
ENABLE_LINKDING=true
# then:
./scripts/install.sh
AppHostnameWhat it isServed asPersistent data
Linkdinglinks.${DOMAIN}bookmarksDjango + gunicorn${DATA_DIR}/linkding
Pingvin Shareshare.${DOMAIN}file sharingNestJS API + Next.js UI${DATA_DIR}/pingvin
FreshRSSrss.${DOMAIN}RSS / Atom readerPHP + php-fpm${DATA_DIR}/freshrss
Memosnotes.${DOMAIN}notes / quick capturesingle Go binary${DATA_DIR}/memos
Vikunjatasks.${DOMAIN}tasks / kanban / GTDsingle Go binary (API+UI)${DATA_DIR}/vikunja
SearXNGsearch.${DOMAIN}private metasearchPython (Flask) + uWSGInone (stateless)
IT-Toolstools.${DOMAIN}client-side dev toolboxstatic sitenone (client-side)
Gatusstatus.${DOMAIN}uptime / health dashboardsingle Go binaryconfig only
Wallabagread.${DOMAIN}read-later / article saverPHP + php-fpm$HOME/.pocket/wallabag (ext4)
Radicaledav.${DOMAIN}calendar + contacts (CalDAV/CardDAV)Python venv$HOME/.pocket/radicale (ext4)
Triliumwiki.${DOMAIN}notes / wikibundled Node + better-sqlite3$HOME/.pocket/trilium (ext4)
Vaultwardenvault.${DOMAIN}password manager (Bitwarden-compat)Rust binary (image-extracted)$HOME/.pocket/vaultwarden (ext4)
Navidromemusic.${DOMAIN}music streaming (Subsonic)single Go binary$HOME/.pocket/navidrome (ext4) + SD library
Kavitabooks.${DOMAIN}manga / comics / ebooks.NET binary (+libicu)$HOME/.pocket/kavita (ext4) + SD library
Audiobookshelfaudiobooks.${DOMAIN}audiobooks / podcastsNode (built from source)$HOME/.pocket/audiobookshelf (ext4) + SD library
Forgejogit.${DOMAIN}git forge (repos / issues / PRs)single Go binary$HOME/.pocket/forgejo (ext4)
AdGuard Homedns.${DOMAIN}filtering DNS-over-HTTPS resolversingle Go binary$HOME/.pocket/adguard (ext4)

The v0.9 “platform & networking” modules are documented separately because they don’t all fit the one-app-one-hostname shape: Forgejo (git forge — FORGEJO.md) and AdGuard Home (DoH-over-tunnel resolver — ADGUARD.md) are normal hostname’d apps and appear in the table above; the BYO reverse-proxy (PROXY_ROUTES.md) generates a vhost for any loopback service you already run (dynamic hostnames, no binary); and Tailscale (TAILSCALE.md) is a userspace mesh VPN with no public hostname at all (it is its own transport and bypasses the Cloudflare edge — read its trust-boundary note). All are opt-in and off by default; each has a full Resource & Risk section in its dedicated doc.

The v0.7 “productivity & security” set (Wallabag/Radicale/Trilium/Vaultwarden) and the v0.8 “media” set (Navidrome/Kavita/Audiobookshelf) are heavier and each has a dedicated page with a full Resource & Risk section and the auth details: READLATER.md, DAV.md, NOTES.md, VAULT.md, and MEDIA.md (the three media apps + a photo-gallery roadmap note + the Jellyfin “why docs-only” note). Unlike the first batch of apps (which bind-mount data from the exFAT SD), these keep their DB/index on ext4 at $HOME/.pocket/<app> and only the bulk media library sits on the SD. Several speak native/token authVaultwarden, Radicale, and the media apps’ Subsonic / OPDS / mobile clients — and need a Cloudflare Access service-token (or a per-path bypass) rather than the interactive login gate (see APP_AUTH.md).

How they all work (the common pattern)

Every app follows the same model, so once you understand one you understand all:

  • Loopback only. The backend binds 127.0.0.1 (or ${CADDY_BIND}); it is never exposed directly. The core Caddy reverse-proxies the public hostname to it, and Cloudflare Tunnel is the only ingress.
  • Self-contained vhost. Each installer writes /etc/caddy/apps/<app>.caddy inside the userland (the core Caddyfile imports /etc/caddy/apps/*.caddy) and runs caddy validate fail-closed, so a bad vhost never takes the edge down.
  • Data on the large volume. Anything that must survive a rootfs rebuild (databases, uploads, secrets) lives under ${DATA_DIR}/<app> and is bind-mounted into the userland — so wiping/reinstalling Debian keeps your data.
  • Pinned + verified downloads. Binaries and source tarballs are pinned to an exact version and checked against a sha256 fail-closed (see SECURITY.md).
  • Supervised + idempotent. Each long-running app runs under the same supervisor as the core stack (restarted on crash, survives reboot), and every installer is safe to re-run.

How they’re protected

By default the public hostname is gated at the edge by Cloudflare Access, and apps that have their own login keep it (registration is disabled — you create the first account, not the public). Optionally you can put the Matrix-SSO gateway in front instead, for one login across all your apps. Both are explained in APP_AUTH.md — each app’s vhost ships a commented forward_auth block showing exactly where the gateway hooks in.


Linkding — bookmarks (links.${DOMAIN})

A self-hosted bookmark manager. Built from the pinned upstream git tag into a Python virtualenv inside the userland and served with gunicorn.

  • Login: Linkding’s own Django login. The installer creates an initial superuser from ADMIN_USER / ADMIN_PASSWORD; public sign-up is off.
  • Data: ${DATA_DIR}/linkding (SQLite DB, the Django SECRET_KEY, cached favicons/previews). The persisted secret key means sessions survive restarts.

Pingvin Share — file sharing (share.${DOMAIN})

Share files via expiring links. A NestJS backend (127.0.0.1:8080) plus a Next.js frontend (127.0.0.1:3333); Caddy fronts both and splits /api/* to the backend.

  • Login: Pingvin’s own accounts; self-registration is off and the hostname is additionally gated at the edge.
  • Data: uploaded files live under ${DATA_DIR}/pingvin/uploads.
  • First run is very slow — two npm installs plus a Next.js and a Nest build on a phone can take 15–40+ minutes. It is the heaviest step in the whole stack; the build caps the V8 heap so it can’t OOM-kill the live stack. Re-runs skip the build.
  • Loopback-bind hardening: upstream’s backend defaults to 0.0.0.0 (all interfaces, i.e. LAN-exposed). The installer patches it to bind loopback, fail-closed — keep that patch on upgrades.

FreshRSS — RSS / Atom reader (rss.${DOMAIN})

A PHP feed reader. The pinned source tarball is installed to /opt/freshrss and served by a dedicated php-fpm pool via Caddy’s php_fastcgi.

  • Login: FreshRSS’s native form login (auth_type=form); open registration and anonymous access are off. The installer creates an initial admin from ADMIN_USER / ADMIN_PASSWORD (idempotent) — change the password after first login.
  • Data: ${DATA_DIR}/freshrss (SQLite DB, config, feed cache). Upgrades keep the existing data/ dir.

Memos — notes / quick capture (notes.${DOMAIN})

A lightweight note / micro-blog app. A single static Go binary at /opt/memos, supervised on 127.0.0.1:9110.

  • Login: Memos’ own accounts. ⚠ Disable open registration in Memos’ settings right after you create your first user (it ships registration on) — see the installer’s closing notes.
  • Data: ${DATA_DIR}/memos (SQLite DB + uploads); survives a rootfs rebuild.

Vikunja — tasks / kanban / GTD (tasks.${DOMAIN})

A task manager. The pinned arm64 “-full” build bundles the REST API and the Vue frontend in one binary at /opt/vikunja.

  • Login: Vikunja’s native login; registration is off and local login is on, so new accounts are created by an admin from inside Vikunja. Service/JWT secrets are persisted under ${DATA_DIR}/secrets so users aren’t logged out on restart.
  • Data: ${DATA_DIR}/vikunja (SQLite DB + attachments). Back this up before upgrading, then bump the version + sha256 and re-run.

SearXNG — private metasearch (search.${DOMAIN})

A privacy-respecting metasearch front-end that queries other engines and returns aggregated results — it stores no search history and has no user accounts. Built from source into a venv inside the userland and served via uWSGI.

  • Login: none of its own — protect the hostname with Cloudflare Access (or the SSO gateway) so only your people can use it.
  • First run compiles native wheels (a C toolchain is installed), so it is slower than a binary drop-in. Re-runs skip the build.

IT-Tools — developer toolbox (tools.${DOMAIN})

A large collection of client-side utilities (encoders, converters, generators, formatters, crypto helpers, …). The prebuilt release is served as a static site by Caddy — there is no backend.

  • Login: none (it’s static). To require a login, gate the whole site with the optional Matrix-SSO forward_auth block (commented in its vhost) or Cloudflare Access.
  • The installer self-hosts the figlet fonts so the ASCII-art tool works without a third-party CDN (a same-origin correctness fix).

Gatus — uptime / health dashboard (status.${DOMAIN})

A status page that probes your services and shows their health/history. Built from source on-device (a pinned Go toolchain compiles the pinned tag), then run in the userland behind the core Caddy.

  • Login: the status page has no app-level login by design — decide whether to expose it publicly or gate it at the edge.
  • First run is slow — downloading the Go toolchain and compiling on a phone can take 10+ minutes. Re-runs skip the build.

Enabling, disabling, and upgrading

  • Enable: set ENABLE_<APP>=true in .env, then ./scripts/install.sh. The app installs and its vhost is loaded on the next edge (re)start.
  • Disable: set the flag to false. The installer simply won’t (re)install or start it; remove its /etc/caddy/apps/<app>.caddy and stop its supervisor to take the live one down.
  • Upgrade: bump the app’s pinned version and its sha256 in its installer (never invent a hash — see SECURITY.md), back up its ${DATA_DIR}/<app> data first, then re-run the installer. Data persists on the large volume across the upgrade.

See also

  • APP_AUTH.md — Cloudflare Access vs the optional Matrix-SSO gateway.
  • MATRIX_AUTH_GW.md — the single sign-on gateway in depth.
  • ADMIN.md — health, restarts, and logs for every app from the panel.
  • SECURITY.md — the pinning / verification model and threat model.