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.yml
name: jellyfin
target: media       # Which target to deploy to
state: present      # "present" = running, "absent" = marked for removal

Providers#

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-kea

The backend choice lands in project.yml; the credentials land in secrets/providers.yml (SOPS-encrypted).

The Key Difference#

ServiceProvider
Runs on a target?YesNo
Needs a target?YesNo
Configured withpsw app addpsw provider configure
Lives inservices/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:

AppWhat It Does
PostgreSQLShared database — most apps store their data here instead of each running their own
DragonflyRedis-compatible in-memory data store (session cache for Authelia)
TraefikReverse proxy — gives every app a clean URL like https://jellyfin.yourdomain.ca with automatic HTTPS
LLDAPUser directory — one set of user accounts shared across all your apps
AutheliaSingle Sign-On — log in once, access everything. Protects apps with 2FA
ForgejoGit server — stores your project and triggers automatic deployments when you push changes
PSW DashboardWeb UI — see what’s running, monitor deployments, check status at a glance
PSW DocumentationStatic 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.

StackWhat’s Included
@mediaJellyfin , Prowlarr , Sabnzbd , Sonarr , Radarr , Bazarr , Lidarr
@media-fullEverything in @media + qBittorrent , Audiobookshelf , FlareSolverr , Seerr , Tdarr , Recyclarr
@observabilityPrometheus , Loki , Grafana , Alertmanager , Alloy , Node Exporter , Blackbox Exporter , Ntfy , Uptime Kuma
@download-pipelineProwlarr , Sabnzbd , Sonarr , Radarr
@home-automationHome Assistant , Zigbee2MQTT , Z-Wave JS UI , Mosquitto
@aiOllama , Open WebUI — see ai.md for the full picture (GPU passthrough, model storage, privacy invariants, recommended models)
@voiceWhisper , 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 media

Behind 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
  - sabnzbd

PSW 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 --cascade to 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 live

Removing an App#

Removal is a two-step process:

  1. Mark for removal: psw app remove jellyfin sets state: absent in the service manifest
  2. 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 keys

The 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 --available

Feature-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.

FeaturePlumbingYou use
Remote accessPangolin (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.