Apps#
What Is an App?#
An app is anything PSW can deploy and manage for you — a media server, a database, a monitoring dashboard, a password manager. Each app has a deployment recipe (templates and metadata ) describing what it needs and how it fits into your self-hosted solution.
PSW comes with a catalog of ready-to-use apps. You pick the ones you want, and PSW handles the rest: creating targets , configuring networking, setting up secrets , wiring apps together .
Two Kinds of Apps#
Services#
A service is an app that runs on a target on your network.
Examples: Jellyfin (media streaming), Sonarr (TV management), Grafana (dashboards), Vaultwarden (passwords).
When you add a service, PSW creates a file like this:
services/jellyfin/service.ymlname: jellyfin
target: media # Which target to deploy to
state: present # "present" = running, "absent" = marked for removalProviders#
A provider is a connection to an external system that already exists on your network. It doesn’t run on a target — PSW just calls an API (Application Programming Interface — a way for software to communicate with other software) on it.
Examples: OPNsense (your router, for local DNS and DHCP), Cloudflare (for public DNS and the ACME DNS-01 challenge ).
You configure providers with psw provider configure:
psw provider configure local-dns opnsense-unbound --secret url=https://10.10.0.1 --secret-file ~/apikey.txt
psw provider configure wan-dns cloudflare --secret api_token=xxx
psw provider configure dhcp opnsense-keaThe backend choice lands in project.yml; the credentials land in secrets/providers.yml (SOPS-encrypted).
The Key Difference#
| Service | Provider | |
|---|---|---|
| Runs on a target? | Yes | No |
| Needs a target? | Yes | No |
| Configured with | psw app add | psw provider configure |
| Lives in | services/ | project.yml + secrets/providers.yml |
Core Apps#
When you first set up your self-hosted solution with PSW, it deploys a set of core apps automatically during the bootstrap process. These are the foundation that everything else builds on:
| App | What It Does |
|---|---|
| PostgreSQL | Shared database — most apps store their data here instead of each running their own |
| Dragonfly | Redis-compatible in-memory data store (session cache for Authelia) |
| Traefik | Reverse proxy — gives every app a clean URL like https://jellyfin.yourdomain.ca with automatic HTTPS |
| LLDAP | User directory — one set of user accounts shared across all your apps |
| Authelia | Single Sign-On — log in once, access everything. Protects apps with 2FA |
| Forgejo | Git server — stores your project and triggers automatic deployments when you push changes |
| PSW Dashboard | Web UI — see what’s running, monitor deployments, check status at a glance |
| PSW Documentation | Static site at https://docs.<domain>/ — every concept doc + the README, served alongside your platform |
You don’t add core apps yourself — the bootstrap process handles them. They’re the “plumbing” that makes everything else work.
Stacks #
A stack is a pre-built bundle of apps that work well together. Instead of adding apps one by one, you can install an entire stack with a single command.
| Stack | What’s Included |
|---|---|
| @media | Jellyfin , Prowlarr , Sabnzbd , Sonarr , Radarr , Bazarr , Lidarr |
| @media-full | Everything in @media + qBittorrent , Audiobookshelf , FlareSolverr , Seerr , Tdarr , Recyclarr |
| @observability | Prometheus , Loki , Grafana , Alertmanager , Alloy , Node Exporter , Blackbox Exporter , Ntfy , Uptime Kuma |
| @download-pipeline | Prowlarr , Sabnzbd , Sonarr , Radarr |
| @home-automation | Home Assistant , Zigbee2MQTT , Z-Wave JS UI , Mosquitto |
| @ai | Ollama , Open WebUI — see ai.md for the full picture (GPU passthrough, model storage, privacy invariants, recommended models) |
| @voice | Whisper , Piper , Home Assistant , Mosquitto , Ollama, Open WebUI — fully local “Hey Casa, turn off the kitchen” voice control. See ai.md § Voice |
# Install the whole media suite in one command
psw app add @media --target mediaBehind the scenes, a stack just expands into individual app names. Each app is still installed and managed independently.
Dependencies#
Some apps need other apps to work. For example, Sonarr needs a database (PostgreSQL), an indexer (Prowlarr), and a downloader (Sabnzbd).
These dependencies are declared in each app’s metadata :
# sonarr's meta.yml
requires:
- postgres
- prowlarr
- sabnzbdPSW enforces these relationships:
- When adding: if you try to add Sonarr without its dependencies installed, PSW tells you what’s missing. Core apps (like PostgreSQL) are treated as always available — you can add apps that depend on them even before bootstrap has run
- When removing: if you try to remove PostgreSQL while Sonarr still needs it, PSW blocks you (unless you use
--cascadeto remove everything that depends on it)
App Lifecycle#
Here’s what happens from adding an app to it running in your self-hosted solution:
1. psw app add jellyfin --target media
└── Creates services/jellyfin/service.yml
└── Generates [secrets](secrets.md) in secrets/apps.yml
└── Copies deployment recipe to roles/
2. git commit && git push
└── Your changes reach the Forgejo git server
3. [Convergence](convergence.md) detects the change
└── Creates the [target](infrastructure.md#targets) (if needed)
└── Deploys the app (renders config, copies files, starts container)
└── Waits for the app to be healthy (readiness check)
└── Runs the app's [setup reconcilers](wiring.md#setup-vs-integration-reconcilers) — creates admin account, extracts API key, configures internal auth
└── Starts sidecars (backup, metrics exporter) now that the app is fully configured
4. [Integration pass](conventions.md)
└── Traefik picks up the new route automatically
└── Authelia gets the SSO config (if the app supports it)
└── Prometheus starts scraping metrics (if the app exposes them)
└── Runs the app's [integration reconcilers](wiring.md#setup-vs-integration-reconcilers) — registers with Prowlarr, adds a download client, wires up notifications
5. Done — jellyfin.yourdomain.ca is liveRemoving an App#
Removal is a two-step process:
- Mark for removal:
psw app remove jellyfinsetsstate: absentin the service manifest - Convergence tears it down: on the next deployment cycle, the app is stopped, its data cleaned up, and integrations unwired
You can control what happens to your data:
teardown_options:
keep_data: true # Preserve app data (in case you want it back later)
keep_secrets: false # Erase passwords and API keysThe App Catalog#
PSW ships with a growing catalog of apps across several categories (the labels match each app’s category: field in meta.yml
):
Infrastructure: PostgreSQL , Dragonfly , Traefik , Authelia , LLDAP , Forgejo , Homepage , PSW Dashboard
Media — entertainment: Jellyfin , Sonarr , Radarr , Bazarr , Lidarr , Prowlarr , Sabnzbd , qBittorrent , Tdarr , Seerr , FlareSolverr , Audiobookshelf , Recyclarr
Media — photos & video library: Immich Server
(the @photos stack
)
Media — documents: Paperless-ngx
+ Tika
(text/metadata extraction) + Gotenberg
(PDF conversion) + paperless-ai
(LLM tagging) + paperless-gpt
(vision-LLM OCR) — the @office stack
Media — surveillance: Frigate
(NVR with neural-network object detection on Coral USB
, NVIDIA, or AMD GPU) — the @cctv stack
Observability: Prometheus , Grafana , Loki , Alertmanager , Alloy , Node Exporter , Blackbox Exporter , Uptime Kuma , Ntfy , Backrest
Home Automation: Home Assistant , Zigbee2MQTT , Z-Wave JS UI , Mosquitto
AI: Ollama + vLLM (two LLM backends — Ollama for queue-driven chat, vLLM for tagging-burst workloads), Open WebUI (chat UI + OpenAI-compatible relay), ComfyUI (graph-based diffusion / image-gen), Whisper + Piper (speech-to-text + text-to-speech for Home Assistant voice), Immich machine-learning (CLIP semantic search + face recognition sidecar) — see ai.md
Security: Vaultwarden
Networking (feature-owned, see below): Pangolin , Newt
You can see what’s available anytime:
psw app list --availableFeature-Owned Apps#
Some apps are not in the catalog because they’re plumbing for a feature rather than something you install directly. You use the feature’s own commands; the feature deploys the apps for you.
| Feature | Plumbing | You use |
|---|---|---|
| Remote access | Pangolin (tunnel server on a VPS), Newt (tunnel client on your targets) | psw remote setup, psw remote expose, psw remote unexpose, psw remote status, psw remote rotate-certs, psw remote rotate-vps |
These apps carry managed_by: <feature> in their metadata
, which is why psw app add pangolin refuses — you don’t add them directly. They show up only with psw app list --available --all.
The dashboard
mirrors this: the /apps page hides feature-owned plumbing by default (with a +N managed toggle to reveal), and each feature gets its own page (e.g. /remote) where you actually manage it.