Wheelhouse docs

Install an app: directories, image, diff, commit#

You will end up with a container running on the router, declared in the configuration tree, with its own nav entry and a link to its web interface. Because it is declared in the tree, the install is a diff you review, a commit you can roll back, and state that survives an image upgrade.

That is the whole idea: an app is not an opaque package with its own lifecycle. It is configuration.

Before you start#

  • The operator role and a licence.
  • Container support on the router, and a route to the registry.
  • The agent running on the router for step 1 — see below.

The three steps, and only one of them is configuration#

VyOS validates a container at commit time and rejects it if either prerequisite is missing. Both are actions, not configuration, so they happen before the diff exists.

StepWhat it doesWhy it is separate
1 · Directoriesmkdir -p each volume sourceVyOS: Volume "conf" source path "…" does not exist
2 · Pull imageadd container image <ref>VyOS: Image "…" does not exist locally
3 · Stage installset container name … into the Commit BarThis is the only part that is configuration

Step 1 — Choose an entry#

Services → Apps. The catalogue is 38 entries — 36 containers and two built-in feature modules — grouped by category: DNS and filtering, VPN and remote access, monitoring, security, network services, management.

Each entry declares what it is, why you would put it on a router specifically, its ports, its volumes, its environment variables, and its notes: the things that will bite you, like a port-53 conflict or a need for host networking.

A feature module is different. kind: "builtin" marks a native VyOS feature whose Wheelhouse page is gated by installation — WireGuard and Suricata IDS. Installing one is instant and stages nothing: there is no image, no volume and no commit, because the flag is a UI-surface preference in the agent's own state rather than router behaviour. And configuration outranks the flag: if the feature is configured on the router by anything at all, its page shows regardless.

Step 2 — Fill in what it needs, and read the plan#

The install panel takes a container name, an address on the apps network, the volume sources, and any environment variables the entry declares. POST /api/apps/plan renders the operations without staging them, and the panel shows them.

a typical container install
set container network apps prefix 10.99.0.0/24
set container name adguard image docker.io/adguard/adguardhome:latest
set container name adguard network apps address 10.99.0.5
set container name adguard volume work source /config/apps/adguard/work
set container name adguard volume work destination /opt/adguardhome/work
set container name adguard volume conf source /config/apps/adguard/conf
set container name adguard volume conf destination /opt/adguardhome/conf

The container network line appears only when the network does not already exist and the app is not on host networking. The prefix 10.99.0.0/24 is the catalogue's own.

The plan also carries warnings, and they are worth reading:

WarningMeans
Runs on the host network: it shares the router's interfaces and any port it binds is exposed on the router itself.The container has no separate address. Treat its ports as the router's ports.
Runs privileged. It can reach the router's kernel interfaces directly — install it only if you trust the image.What it says.
No address chosen — pick one inside 10.99.0.0/24 before committing.Fill in the address field.
Environment variable X is required and has no value.The entry marks it required; the container will not work without it.

The agent validates what you typed before VyOS would: a container name is letters, digits and hyphens only, and a volume source must be an absolute path with no spaces or shell characters. Those are refused here, where the message can name the field, rather than at commit time where VyOS would only say "Set failed".

Step 3 — Commit the diff#

Read it. This is the step the whole model exists for.

Step 4 — Use it#

Once it is running, the entry's integration declarations take effect:

  • nav_label gives the app its own left-nav entry under Installed, with a dot when it is declared but not running.
  • web_ui gives it an Open link built from the container's real address.
  • health makes the agent probe the port — TCP, plus an HTTP request when the entry gives a path. Deliberately not an app-specific API call: the product does not claim knowledge of an app's internals it does not have.
  • hints put an offer on another Wheelhouse page, with the app's real address substituted in and the exact operations attached. Install AdGuard and the DNS page grows a card offering to point the resolver at it, with a one-click Stage this change.

Hints stage; they never apply. The Commit Bar is still the only thing that commits.

Check it worked#

Apps → Installed shows declared beside running, with the health verdict.

bash
show container

The health probe is honest about what it does not know. In off-router development mode the agent has no route to the container bridge, so a failed probe would say nothing about the app — it reports not probed — this agent runs off-router rather than a red "not answering" that would be a lie.

The app's logs are on its detail page, or:

bash
sudo podman logs adguard

When the pull fails#

SymptomCause
toomanyrequestsDocker Hub rate-limits anonymous pulls per source address. Retry later, or pull the image by hand on the router. There is no registry mirror or credential setting.
Image "…" does not exist locally at commitStep 2 did not happen or did not finish.
Volume "…" source path does not exist at commitStep 1 did not happen. Off-router, run the mkdir -p the agent handed you.

Undoing it#

Stop, uninstall, and what stays behind.

See also#


Checked against docs/apps.md · agent/apps.go · agent/catalog.go · agent/catalog.json · ui/src/pages/Apps.tsx

Updated 2026-09-02 apps containers catalogue