The catalogue#
Thirty-eight entries: 36 container apps and two built-in feature modules. The tables
below are generated from agent/catalog.json , not retyped,
and every column is a field of the catalogue schema . The categories are in
the order the catalogue declares them; within a category, entries sort
case-insensitively by name.
Everything here is what the catalogue says , not what the agent tests. An entry is a
recipe for a container the router runs: an image, a set of volumes, some environment, and
a few things the web UI can honestly do with the result — link to it, probe whether it
answers, and offer the configuration change that wires it into the router.
Check the version you are running:
curl -sk "$R/api/apps/catalog" -H "Authorization: Bearer $T" | python3 -m json.tool | head -20
Every image uses a floating tag
All 36 container entries name a tag, not a digest. :latest on most of them. Digest
pinning is tooled and not applied , so what a
router pulls today and what it pulls next month may differ. Pin an image yourself at
install time if that matters — the install request's image is what gets staged.
How to read the tables#
Each category has two tables. The first says what an entry is ; the second says what
installing it does .
Column Meaning Ports container[/protocol][→ host N], from the entry's ports. Documentation, not configuration — nothing stages a port mapping.Volumes source → destination, with (file) for a single-file bind mount and ro for read-only. Sources are defaults; the operator may override each one at install.Environment Variable names. A * marks one the entry declares required. Integration What the UI does: the app's own web UI, the health probe, whether it appears on the Dashboard, and how many hints it offers. Notes Host networking, privilege, Linux capabilities, a memory limit, and any platform gap the entry says it closes.
The entries#
DNS & filtering#
Entry idWhat it is Image AdGuard Home adguardNetwork-wide DNS filtering with per-client policies and a query log. docker.io/adguard/adguardhome:latestPi-hole piholeThe original DNS sinkhole, with a long-established blocklist ecosystem. docker.io/pihole/pihole:latestdnsmasq dnsmasqWildcard DNS, split horizon, per-host DHCP options and an arch-conditional PXE matrix. docker.io/jpillora/dnsmasq:latestUnbound unboundA validating, recursive resolver — no upstream provider sees your queries. docker.io/mvance/unbound:latestBlocky blockyA small, fast DNS proxy with blocklists and per-client groups. docker.io/spx01/blocky:latest
idPorts Volumes Environment Integration Notes adguard53/tcp_udp, 3000 /config/apps/adguard/work → /opt/adguardhome/work, /config/apps/adguard/conf → /opt/adguardhome/conf— web UI :3000, probe :3000/, dashboard, 1 hint — pihole53/tcp_udp, 80 /config/apps/pihole/etc → /etc/pihole, /config/apps/pihole/dnsmasq.d → /etc/dnsmasq.dTZ, WEBPASSWORD*web UI :80/admin, probe :80/admin/, dashboard, 1 hint — dnsmasq53/tcp_udp, 8080 /config/apps/dnsmasq/dnsmasq.conf (file) → /etc/dnsmasq.conf— web UI :8080, probe :53, 2 hints caps: net-admin, net-bind-service; closes: Wildcard DNS · per-host DHCP options · PXE architecture matrix unbound53/tcp_udp /config/apps/unbound → /opt/unbound/etc/unbound— probe :53, 1 hint — blocky53/tcp_udp, 4000 /config/apps/blocky/config.yml (file) → /app/config.yml— probe :4000/, dashboard, 1 hint —
VPN & remote access#
Entry idWhat it is Image Tailscale tailscaleMesh VPN. Advertise the LAN as a subnet route or make the router an exit node. docker.io/tailscale/tailscale:latestNetBird netbirdOpen-source WireGuard mesh you can self-host end to end. docker.io/netbirdio/netbird:latestHeadscale headscaleSelf-hosted control server for Tailscale clients. docker.io/headscale/headscale:latestWG-Easy wg-easyA web UI for WireGuard peers, with QR codes for phones. ghcr.io/wg-easy/wg-easy:latestCloudflared cloudflaredExpose an internal service without opening a port on the WAN. docker.io/cloudflare/cloudflared:latestWireGuard wireguardThe router's own in-kernel WireGuard — tunnels and peers as native config. built-in feature
idPorts Volumes Environment Integration Notes tailscale— /config/apps/tailscale → /var/lib/tailscaleTS_AUTHKEY*, TS_HOSTNAME, TS_ROUTES, TS_STATE_DIR, TS_EXTRA_ARGSdashboard, 1 hint host network; privileged; caps: net-admin, net-raw, sys-module netbird— /config/apps/netbird → /etc/netbirdNB_SETUP_KEY*— host network; caps: net-admin, sys-admin headscale8080, 9090 /config/apps/headscale/config → /etc/headscale, /config/apps/headscale/data → /var/lib/headscale— web UI :8080, probe :8080/health — wg-easy51821, 51820/udp /config/apps/wg-easy → /etc/wireguardWG_HOST, PASSWORD_HASH web UI :51821, probe :51821/, 1 hint caps: net-admin, sys-module cloudflared— — TUNNEL_TOKEN*1 hint — wireguard— — — 1 hint —
Monitoring#
Entry idWhat it is Image ntopng ntopngPer-host flow analytics and historical top talkers. docker.io/ntop/ntopng:latestNetdata netdataPer-second system metrics with no configuration. docker.io/netdata/netdata:latestPrometheus prometheusScrape and retain metrics — including Wheelhouse's own. docker.io/prom/prometheus:latestGrafana grafanaDashboards over Prometheus, Loki or anything else you point it at. docker.io/grafana/grafana:latestNode Exporter node-exporterHost metrics for Prometheus, straight off the router. docker.io/prom/node-exporter:latestUptime Kuma uptime-kumaUptime monitoring with notifications, run from inside the network. docker.io/louislam/uptime-kuma:latestSpeedtest Tracker speedtest-trackerRuns a speed test on a schedule and keeps the history. ghcr.io/linuxserver/speedtest-tracker:latestOpenSpeedTest openspeedtestA LAN-side speed test target — measures the network, not the internet. docker.io/openspeedtest/latest:latestLibreSpeed librespeedA lighter LAN speed test with no Flash, Java or dependencies. docker.io/linuxserver/librespeed:latest
idPorts Volumes Environment Integration Notes ntopng3000 /config/apps/ntopng → /var/lib/ntopng— web UI :3000, probe :3000/, dashboard, 1 hint host network; caps: net-admin, net-raw netdata19999 /config/apps/netdata/config → /etc/netdata, /config/apps/netdata/lib → /var/lib/netdata, /proc → /host/proc ro, /sys → /host/sys ro— web UI :19999, probe :19999/api/v1/info, dashboard host network; privileged; caps: sys-admin prometheus9090 /config/apps/prometheus/prometheus.yml (file) → /etc/prometheus/prometheus.yml, /config/apps/prometheus/data → /prometheus— web UI :9090, probe :9090/-/healthy, dashboard, 1 hint — grafana3000 /config/apps/grafana → /var/lib/grafanaGF_SECURITY_ADMIN_PASSWORD*web UI :3000, probe :3000/api/health, dashboard — node-exporter9100 /proc → /host/proc ro, /sys → /host/sys ro— probe :9100/metrics host network uptime-kuma3001 /config/apps/uptime-kuma → /app/data— web UI :3001, probe :3001/, dashboard — speedtest-tracker80 /config/apps/speedtest-tracker → /configAPP_KEY*, DB_CONNECTIONweb UI :80, probe :80/, 1 hint — openspeedtest3000, 3001 — — web UI :3000, probe :3000/ — librespeed80 — — web UI :80, probe :80/ —
Security#
Entry idWhat it is Image CrowdSec crowdsecParses logs, detects attacks, and shares signals with a community blocklist. docker.io/crowdsecurity/crowdsec:latestZeek zeekNetwork security monitor that turns traffic into structured logs. docker.io/zeek/zeek:latestVaultwarden vaultwardenA Bitwarden-compatible password server, kept on your own hardware. docker.io/vaultwarden/server:latestSuricata IDS suricataThe IDS VyOS ships: Suricata watching interfaces you name, driven from the config tree. built-in feature
idPorts Volumes Environment Integration Notes crowdsec8080 /config/apps/crowdsec/config → /etc/crowdsec, /config/apps/crowdsec/data → /var/lib/crowdsec/data, /var/log → /var/log/host ro— probe :8080/health, 1 hint — zeek— /config/apps/zeek/logs → /usr/local/zeek/logs, /config/apps/zeek/etc → /usr/local/zeek/etc— — host network; caps: net-admin, net-raw vaultwarden80 /config/apps/vaultwarden → /dataADMIN_TOKEN, SIGNUPS_ALLOWEDweb UI :80, probe :80/alive — suricata— — — 1 hint —
Network services#
Entry idWhat it is Image Nginx Proxy Manager npmReverse proxy with a UI and automatic Let's Encrypt certificates. docker.io/jc21/nginx-proxy-manager:latestCaddy caddyA reverse proxy that gets its own certificates with no configuration. docker.io/library/caddy:latestTraefik traefikDynamic reverse proxy that discovers containers as they appear. docker.io/library/traefik:latestnetboot.xyz netbootxyzNetwork-boot installers for anything, without building a PXE tree. ghcr.io/linuxserver/netbootxyz:latestMosquitto mosquittoAn MQTT broker for the IoT half of the network. docker.io/library/eclipse-mosquitto:latest
idPorts Volumes Environment Integration Notes npm80, 443, 81 /config/apps/npm/data → /data, /config/apps/npm/letsencrypt → /etc/letsencrypt— web UI :81, probe :81/, dashboard, 2 hints — caddy80, 443 /config/apps/caddy/Caddyfile (file) → /etc/caddy/Caddyfile, /config/apps/caddy/data → /data— probe :80/ — traefik80, 443, 8080 /config/apps/traefik → /etc/traefik— web UI :8080, probe :8080/ping — netbootxyz3000, 69/udp, 80 /config/apps/netbootxyz/config → /config, /config/apps/netbootxyz/assets → /assets— web UI :3000, probe :3000/, 1 hint closes: PXE architecture matrix (with dnsmasq) mosquitto1883, 9001 /config/apps/mosquitto/config → /mosquitto/config, /config/apps/mosquitto/data → /mosquitto/data— probe :1883 —
Management#
Entry idWhat it is Image Portainer portainerContainer management for everything running on the router. docker.io/portainer/portainer-ce:latestHomepage homepageA start page that links every service on the network. ghcr.io/gethomepage/homepage:latestHomer homerA static start page — one YAML file, no runtime. docker.io/b4bz/homer:latestntfy ntfyPush notifications from scripts, over HTTP. docker.io/binwiederhier/ntfy:latestGotify gotifySelf-hosted push notifications with an Android client. docker.io/gotify/server:latestWatchYourLAN watchyourlanNotices when a device appears on the network that has not been seen before. docker.io/aceberg/watchyourlan:latestDozzle dozzleLive container logs in a browser, no storage, no setup. docker.io/amir20/dozzle:latestIT Tools it-toolsA pile of offline network and encoding tools in one page. ghcr.io/corentinth/it-tools:latestWiki.js wikijsSomewhere to write down why the config is the way it is. ghcr.io/requarks/wiki:latest
idPorts Volumes Environment Integration Notes portainer9443, 9000 /config/apps/portainer → /data, /run/podman/podman.sock → /var/run/docker.sock— web UI :9000, probe :9000/, 1 hint — homepage3000 /config/apps/homepage → /app/config— web UI :3000, probe :3000/ — homer8080 /config/apps/homer → /www/assets— web UI :8080, probe :8080/ — ntfy80 /config/apps/ntfy/cache → /var/cache/ntfy, /config/apps/ntfy/etc → /etc/ntfy— web UI :80, probe :80/v1/health — gotify80 /config/apps/gotify → /app/dataGOTIFY_DEFAULTUSER_PASS*web UI :80, probe :80/health — watchyourlan8840 /config/apps/watchyourlan → /dataIFACESweb UI :8840, probe :8840/, 1 hint host network; caps: net-admin, net-raw dozzle8080 /run/podman/podman.sock → /var/run/docker.sock ro— web UI :8080, probe :8080/ — it-tools80 — — web UI :80, probe :80/ — wikijs3000 /config/apps/wikijs → /wiki/dataDB_TYPE, DB_FILEPATHweb UI :3000, probe :3000/ —
The two built-in entries#
wireguard and suricata are kind: "builtin". They are not containers: they gate a
native page in the web UI, and "installing" one stages nothing at all . The agent
says so rather than pretending:
Copy
This is a built-in feature, not a container — installing it enables its page instantly
and stages nothing.
Each declares a config_path. If configuration exists under that path, the page stays
visible whether or not the feature is enabled — a page must never be hidden while the
configuration it edits is live on the router.
What is deliberately absent#
There is no UPnP entry
A miniupnpd entry was removed because no credible published image exists for it.
The Companions page reports the configuration state of the router's own service upnp
node and says the gap is open. Do not expect an app to close it.
Installing one#
Three things have to be true before the router will commit a container: the image must be
on the router, every volume source must exist, and the configuration must be valid. That
is why installing is four calls rather than one.
# 1. What would be staged, and is the image here yet?
curl -sk -X POST "$R/api/apps/plan" -H "Authorization: Bearer $T" \
-H 'Content-Type: application/json' \
-d '{"app_id":"adguard","name":"adguard","address":"10.99.0.10"}'
# 2. Pull the image onto the router.
curl -sk -X POST "$R/api/apps/pull" -H "Authorization: Bearer $T" \
-H 'Content-Type: application/json' -d '{"app_id":"adguard"}'
# 3. Create the volume sources.
curl -sk -X POST "$R/api/apps/prepare" -H "Authorization: Bearer $T" \
-H 'Content-Type: application/json' \
-d '{"app_id":"adguard","name":"adguard"}'
# 4. Stage the operations from step 1, then commit them in the Commit Bar.
Lifecycle is configuration too#
There is no control socket. Stopping an app stages set container name <n> disable;
starting it deletes that node; restarting is a two-commit bounce, and if the second
commit fails the app is left down and the API says so in those words.
See also#
Checked against#
agent/catalog.json ,
agent/catalog.go (loadCatalog, planInstall, probeApp),
agent/apps.go (handleAppPlan, handleAppPull,
handleAppPrepare, handleAppRestart, handleAppFeature, collectInstalled),
agent/catalog_test.go ,
scripts/check-images.py ,
docs/apps.md ,
PLAN.md §8.