Getting started

Dashboarr is a single mobile app for your whole self-hosted media stack. It talks directly from your phone to your own servers using each service's official API. No accounts, no cloud service, and no server component required. This guide walks through a first-time setup in the order you will actually do it.

1. Install #

Dashboarr ships on three channels:

  • Apple App Store for iPhone and iPad.
  • Google Play for Android.
  • A signed APK attached to each GitHub release, for de-Googled or sideloaded installs. Sideloaded builds update manually: the app checks the releases page and opens it for you, but there is no in-app installer.

The optional push-notification backend is a separate, self-hosted Docker or Node.js service. You do not need it to use the app. Section 8 covers it.

2. First launch #

On a fresh install the Dashboard tab shows:

No services configured yet.
Go to Settings to add your first service.

A three-step intro carousel explains the workspace concept. You can skip it and replay it later from Settings, About, "Show workspace tour".

A few things already exist before you configure anything:

  • One dashboard named Default, holding three widget cards: Service Health, Radarr Queue and Calendar.
  • Three pinned bottom tabs: Downloads, Calendar and Services. Together with Dashboard and Settings, that is the five-icon bar you see on day one.
  • One placeholder instance for every supported service, all disabled with blank URLs. That is why you normally tap an existing row in Settings rather than creating anything new.

Want to look around first?

Turn on Settings, Backup & Storage, Advanced, Demo Mode ("Show sample data, no server required"). Every service switches to realistic sample content without touching your real configuration, and a banner reads DEMO MODE, Sample data only with an Exit link. Turn it off and everything comes back exactly as it was.

Note. With Demo Mode on, Test Connection always reports success. Do not use it to validate a real server.

3. Add your first service #

Everything lives on the Settings tab, subtitled "Applies to all dashboards". It has two groups:

  • Services, one row per service kind. Configured kinds sort first, the rest sit under a Not configured divider.
  • App: Network, Notifications, Appearance, Backup & Storage, About.

Tap a service kind to open its instance list, then tap the instance to open the editor.

The instance editor, top to bottom

CardWhat is in it
(first card)Name and an Enabled toggle
(second card)Local URL, Remote URL, Always use Remote URL, Allow invalid certificates
AuthenticationEither Username and Password, or a single API Key. Plex also gets a Connect with Plex button.
Custom HeadersOptional per-instance headers for reverse-proxy auth
Add DefaultsRadarr, Sonarr and Lidarr only: Quality Profile, Root Folder, and Metadata Profile for Lidarr
NotificationsPer-instance overrides for each notification category
Webhook AttributionOnly when a backend is paired, and only for Radarr, Sonarr, Tautulli, Seerr, Bazarr and Tracearr
(buttons)Test Connection and Save, side by side

URL rules

  • Enter the bare origin, for example http://192.168.1.100:7878. Dashboarr appends the API path itself. Never paste /api/v3 into the URL box.
  • A reverse-proxy sub-path is supported and preserved: https://media.example.com/radarr becomes https://media.example.com/radarr/api/v3/...
  • Typing a URL without a scheme auto-prefixes http:// when the field loses focus.
  • Only http:// and https:// are accepted. A trailing slash is harmless.
  • Saving a Remote URL on plain http:// raises a confirmation titled "Remote URL uses HTTP" with a Save anyway button, because API keys would cross the internet in cleartext. A local http:// URL is accepted silently, which is the normal case on a LAN.

What saves instantly, and what does not

Enabled, Always use Remote URL, Allow invalid certificates and the Add Defaults pickers apply the moment you flip them. Name, both URLs, credentials and Custom Headers only persist when you press Save. While changes are pending the editor title shows "unsaved" in amber, and backing out offers Save or Discard.

Test Connection

Test Connection probes the values currently typed in the form, so you can validate before saving. It tests exactly one URL, whichever the app would use right now, and tells you which:

  • Connected via local URL in 42ms
  • Auth failed (remote URL): ...
  • Could not reach local URL: ...

The probe times out after 8 seconds. It never tests both URLs in one tap.

Then attach it to a dashboard

The first time you save an instance that has both a URL and a credential, an Add to dashboards sheet appears so you can pick which workspaces include it.

Enabling a service does three separate things that are easy to conflate: it makes the instance live, it may be attached to one or more dashboards, and its widget still has to be added by hand. Open a dashboard and tap "Add widget" to put a service's card on it.

Status dots

Once a kind has at least one enabled instance, its Settings row shows a coloured dot: green for reachable, amber for reachable but credentials rejected, red for unreachable. While the first health check is running there is no dot at all. Health polls every 30 seconds, so Settings can lag a config change by up to half a minute. Test Connection is the only immediate probe.

Multiple instances

Every service supports multiple instances: two Radarrs, an anime Sonarr, a second household. Use "Add another instance" at the bottom of the instance list. New instances auto-name themselves (Radarr, then Radarr 2). With two or more, each row gains reorder arrows and a delete button, and that order is the order shown in the per-screen instance switcher. You cannot delete the last remaining instance of a kind, so disable it instead.

When a kind has two or more enabled instances attached to the current dashboard, its screen header shows the instance name with a chevron. Tapping it opens a Switch instance sheet.

4. Local and remote URLs #

This is the most important concept in Dashboarr, and the source of most confusion.

The default: auto-switch is off

Settings, Network, "Auto-switch network" is off on a fresh install. With it off, every service always uses its Local URL (or the Remote URL if no local one is set), on any network. Nothing else in this section applies until you turn it on. If you only use the app at home, or you address everything through Tailscale, you can leave it off and skip ahead.

With auto-switch on

The app uses a service's Local URL only when it has positively confirmed you are on one of your saved home WiFi networks. Anywhere else it uses the Remote URL only and deliberately never falls back to the local one. The reason is concrete: 192.168.1.50 on airport WiFi belongs to a stranger, and falling back would hand them your API key.

Consequences to be aware of:

  • A service that only has a Local URL is simply unavailable while you are away. That is intended, not a bug.
  • "Away" is the default at every cold start, until the WiFi name is read.
  • If auto-switch is on, you have saved no home networks, and "Treat VPN as home" is off, the Settings row turns amber: "Add at least one, without it the app stays on remote URLs".

Adding a home network

Settings, Network, Home Networks. Use "Add current WiFi" to auto-detect and pre-fill, or "Add manually". The form has two fields:

  • WiFi Name (SSID), required, matched exactly and case-sensitively.
  • Access Point MAC (BSSID), optional. Leave empty to match any access point with that name. Setting it guards against a rogue AP cloning your SSID.

Up to 20 networks can be saved. If your router broadcasts separate 2.4 GHz and 5 GHz SSIDs, save both.

Pin the BSSID only if you mean it. On a mesh or multi-AP house a pinned BSSID matches only the one radio you pinned, and if the OS refuses to report the BSSID the match fails closed and you are treated as away. SSID-only entries are the safer default.

Location permission

Reading the WiFi network name requires Location permission on both iOS and Android. Without it the app can never confirm you are home, so it silently stays on remote URLs.

On iOS you also need Precise Location enabled for Dashboarr. With Approximate location the system still shows the location indicator, so permission looks granted, but iOS will not reveal the network name. The Home Networks screen surfaces this with an amber "Using remote URLs" card and a Grant Location permission button, plus a Retry detection path when Location is allowed but the name still will not appear. Toggling WiFi off and on usually clears it.

VPNs and Tailscale

Under a VPN the OS normally masks the WiFi name, so the app reads you as away and uses remote URLs. Turn on Settings, Network, "Treat VPN as home" (visible only while auto-switch is on) to use local URLs whenever a VPN is connected. The app can only detect that some VPN is up, not which one, so enable this only if your VPN actually reaches your home LAN.

The simplest setup of all, if you use Tailscale. Address services by their Tailscale or MagicDNS name, turn on Always use Remote URL, and leave the Local URL empty. One address that works on WiFi and on cellular, with no home-network configuration at all. Dashboarr deliberately does not treat Tailscale's 100.64.0.0/10 range as LAN-only, so those addresses are never blocked when you are away.

Diagnostics

Settings, Network, Home Networks, "Network diagnostics" (collapsed, labelled "For bug reports") shows Platform, VPN native module, VPN detected, VPN flag, On WiFi, Auto-switch, Treat VPN as home, and Away from home as yes (remote-only) or no (local OK). Quote this panel in bug reports.

Per-dashboard home networks

Each dashboard can restrict which home networks count as home for it, under Edit dashboard, "Home networks", All / Custom. Selecting Custom with nothing ticked forces that workspace onto remote URLs everywhere, and that wins even when the global auto-switch toggle is off.

Custom headers and Wake-on-LAN

  • Custom Headers exist at two levels: globally (Settings, Network, Custom Headers, sent on every request to every enabled service) and per instance. Per-instance headers override global ones on a name collision, and the service's own auth always wins. Up to 32 headers each. Useful for Cloudflare Access, Authelia and similar static-header schemes. They cannot fix cookie or session based SSO.
  • Wake-on-LAN lives at Settings, Network, Wake-on-LAN. Fields are Device Name, MAC Address, Broadcast Address and Port; only name and MAC are required. Defaults are 255.255.255.255 and port 9, but a subnet-directed broadcast such as 192.168.1.255 is far more reliable, since many routers drop the global broadcast. MACs may be typed with colons, dashes, dots or nothing at all. The MAC is validated at send time, not when you save the device. A Wake button appears in the Services tab header once you have devices saved.

5. Service reference #

Dashboarr stores exactly three credential fields per instance (API key, username, password) plus optional custom headers. The editor shows one of two shapes: Username and Password for five services, API Key for everything else. Plex adds a "Connect with Plex" button on top of the API Key field.

The ports below are the upstream defaults, for reference only. Dashboarr never prefills or appends a port, so type the full URL yourself.

ServicePortAuthKey gotcha
qBittorrent8080User + passwordWebUI must be enabled. qBittorrent 5.2.0 changed the login response, so you need app v1.6.3+ (and backend v1.2.5+) or valid credentials are rejected.
rTorrent / ruTorrent8080User + passwordEnter the bare origin of the web server in front of rtorrent, not rtorrent's SCGI port. Dashboarr appends the conventional /RPC2 XML-RPC mount itself, so only add a prefix if your mount is nested. Deleting a torrent with its files needs ruTorrent's erasedata plugin.
Transmission9091User + password (both optional)The RPC server must be enabled and its host whitelist must allow your phone. A blocked host answers 403, which looks like a credentials error.
SABnzbd8080API keyThe key travels in the URL query string, so a reverse proxy that strips or logs query params will break or leak it.
NZBGet6789User + passwordThese are ControlUsername and ControlPassword from nzbget.conf, not a separate web login.
Radarr7878API keyTest Connection sends your API key to /api/v3/system/status and only accepts a JSON 200, so a wrong port fails the test rather than passing silently. Enter the bare origin with no /api/v3 suffix.
Sonarr8989API keySame v3 API and the same authenticated /api/v3/system/status probe as Radarr.
Lidarr8686API keyUses the v1 API, not v3. Only matters if you hand-write proxy path rules.
Prowlarr9696API keyNo webhook integration, so it is poll-only on the backend.
Jackett9117API keyKey travels in the query string, same proxy caveat as SABnzbd. Jackett's admin password protects its indexer management endpoints, so the app cannot add, configure or disable indexers; listing them, per-indexer Search and Test, search and grab all work with just the API key.
Seerr5055API keyIf CSRF protection is enabled in Seerr, the API is read-only and submitting a request fails with 403 "invalid csrf token". Turn CSRF protection off.
Tautulli8181API keyKey travels in the query string, same proxy caveat as SABnzbd. Webhooks are ingested but do not raise a push yet.
Tracearr3000API key (trr_pub_ token)Webhook-only for notifications, and its webhook URL must carry ?instance=<id> or its per-instance toggles are ignored.
Jellystat3000API keyAt least one API key must exist in Jellystat itself. With none configured the server answers 404.
Plex32400Token, or in-app sign-inConnect with Plex auto-fills the server URLs, token and name. With more than one server you get a server picker.
Jellyfin8096API keyA server-wide API key or a user token both work.
Emby8096API keySame default port as Jellyfin. If you run both on one host, re-port one of them.
Bazarr6767API keyWebhooks are ingested but do not raise a push yet.
Glances61208User + password (optional)Glances must run in web server mode. A Dockerized Glances reports no disks unless the host root is bind-mounted read-only (/:/rootfs:ro); CPU and RAM still work, which makes it look like an app bug.
unRAID80API keyThe GraphQL API must be enabled first (native on unRAID 7.2+ under Settings, Management Access). The key also needs array-read permission. The API is GraphQL only, so opening the URL in a browser looks broken even when it is correct.

Where to find each API key is documented by each upstream project, generally under that service's own General or Security settings.

Self-signed certificates

Turn on Allow invalid certificates in the instance editor. The bypass is keyed to hostname and covers both that instance's local and remote hostnames, which means any other service on the same hostname is relaxed too.

Behind a forward-auth SSO proxy

With Authentik or Authelia in front, the proxy hands API calls an HTML login page instead of JSON. Dashboarr detects this and says so rather than falsely reporting the service healthy. The fix is on the proxy side: exclude the API path from authentication. For Authentik, add ^/api and ^/ping to the Proxy Provider's Unauthenticated Paths. This is safe because the *arr API is already protected by its own API key. Custom Headers cannot solve this, because proxy auth here is cookie based, not header based.

6. Dashboards and widgets #

Dashboarr supports multiple named dashboards, internally called workspaces. Each holds its own widget cards, icon and accent colour, pinned tabs, attached service instances, and home-network selection.

Switching and creating

Tap the dashboard name in the Dashboard header to open the Dashboards sheet. From there you can switch, reorder with the chevrons, duplicate with the copy icon, delete with the trash icon, or tap Add dashboard.

Creating is submit-only: type the name (40 characters max) and confirm with the keyboard's Done key or the check button. Tapping elsewhere discards it. The new dashboard opens straight into its editor, and it starts genuinely empty: no widgets, no attached instances, and only the Services tab pinned.

Duplicating produces <name> copy and clones widgets, per-widget settings, attachments, pinned tabs, tab icons, home networks and tile order. The last remaining dashboard cannot be deleted. There is no cap on how many you create.

Two different "edit" surfaces

This trips people up, so be precise:

  • The pencil icon toggles widget edit mode: reorder, remove, copy and configure the cards on the dashboard.
  • The sliders icon (or the gear on a row in the Dashboards sheet) opens Edit dashboard: name, icon, colour, attached instances, pinned tabs, home networks.

Renaming a dashboard only exists in Edit dashboard, Name. Tapping a name in the Dashboards sheet just switches to it.

Widget edit mode

The pencil appears once at least one instance is enabled anywhere. In edit mode a banner reads "Reorder, remove, or add widgets", every card gains a dashed border, and a control row appears above each one with:

  • up and down chevrons to reorder. There is no drag-and-drop for cards, and cards cannot be resized: they are always full width, stacked.
  • a copy icon (only with more than one dashboard) that opens "Copy widget to..." and creates an independent copy with the same settings.
  • a gear for widget settings.
  • a red X to remove, with no confirmation.

Add widget at the bottom opens a sheet grouped into Available and Requires setup. "Requires setup" items are widgets whose service is attached to this dashboard but currently disabled in Settings. Widgets for services that are not attached at all are hidden entirely, so if a widget you expect is missing, attach its service first.

The widget catalogue

Twenty-six widgets are available:

Service Health, Server Stats, Speed Stats, Downloads, SABnzbd Queue, NZBGet Queue, Radarr Queue, Sonarr Queue, Lidarr Queue, Recently Downloaded, Calendar, Still Pending, Stream Activity, Streaming Bandwidth, Seerr Requests, Now Playing (All Servers), Plex Now Playing, Jellyfin Now Playing, Emby Now Playing, Prowlarr Stats, Jackett Indexers, Bazarr Wanted, Wake-on-LAN, Disk Space, Health Alerts, unRAID Array.

Recently Downloaded collapses episodes of the same series into one tile, badged with how many arrived, so a batch of episodes cannot push everything else out of the row. Tapping that tile lists the individual episodes with their quality, size and import date, and any of them opens the series. Turn it off with Episodes, "Group by series" in the widget settings.

The same widget can be placed more than once on one dashboard, each copy with its own settings, which is how you show two download clients or two Radarrs side by side. Two widgets have no options and therefore no gear: Wake-on-LAN and unRAID Array.

Most settings sheets include a Visibility, "Hide when empty" toggle. Cards hidden that way still appear in edit mode, marked with a crossed-out-eye icon, so a "vanished" widget is easy to find. Service Health and Health Alerts have the same toggle worded "Hide when all healthy", since neither is ever literally empty: Service Health hides while every service it shows is online, Health Alerts hides while Sonarr, Radarr, Prowlarr and Lidarr report no health issues, and both come back the moment something goes wrong. Widgets for services that support several instances also expose an instance chip row with an All instances chip.

Attached instances and auto-attach

Edit dashboard, Attached instances controls which service instances this workspace can see. Attachment is per instance, not per kind, so a "Home" Radarr and a "Cabin" Radarr can live on different dashboards.

The starting Default dashboard is in auto-attach mode: it implicitly includes every service and instance you ever add. While that is true, the Dashboard tab shows an "Auto-attach mode" banner with a Curate shortcut.

This looks like a bug and is not. When you open the editor on an auto-attach dashboard, every checkbox starts unticked. If you tick anything and save, the dashboard leaves auto-attach mode permanently and only the ticked instances will ever appear on it. To change just the colour or icon without committing to a curated list, change it and save without touching a checkbox.

Instances that are disabled in Settings cannot be newly attached. Their rows are dimmed and read "Enable in Settings to attach".

Appearance

Edit dashboard, Appearance gives each dashboard an icon (61 to choose from) and one of eight accent colours: Blue (default), Red, Amber, Green, Teal, Purple, Pink and Slate. The accent tints the dashboard chrome, the Save button and the active tab icon in the bottom bar, so switching dashboards visibly recolours the app. A live preview tile at the top of the editor shows the result.

7. Tabs and screens #

The bottom bar is Dashboard first, Settings last, and up to three tabs of your choosing in between. It shows icons only, no labels.

Configure them in Edit dashboard, "Pinned tabs": tap a chip to pin, use the chevrons to reorder, the red X to unpin, and tap a pinned tab's small icon chip to change which icon represents it in the bar.

The sixteen pinnable tabs are:

Downloads, Calendar, Services, Movies, TV, Library, Music, Requests, Activity, Indexers, Plex, Jellyfin, Emby, Glances, Bazarr, unRAID.

A tab becomes pickable when the workspace has at least one attached instance of a backing service. Movies needs Radarr, TV needs Sonarr, Music needs Lidarr, Requests needs Seerr, Indexers needs Prowlarr or Jackett, Downloads needs any download client, Calendar needs Sonarr or Radarr, Library needs Radarr or Sonarr, and Activity needs any of Tautulli, Tracearr, Jellystat, Jellyfin or Emby. Services is always pickable.

Notes worth knowing:

  • Pins are per dashboard. Switching dashboards rebuilds the whole bar, including order, icons and accent colour. If the tab you are on is not pinned on the new dashboard, the app takes you back to Dashboard so you are never stranded.
  • Un-attaching a service hides its pinned tab but keeps the pin stored, so re-attaching restores it. Merely disabling the instance does not hide the tab.
  • Unpinned tabs are not unreachable. Tapping a tile in the Services tab, or a service in the Service Health widget, opens that screen directly.
  • Movies + TV and Library overlap: Library is those same two libraries behind one switcher. Pinning all three spends your slots on duplicate content.

What is on each screen

Most service tabs are one screen with a chip row acting as sub-tabs, plus pull-to-refresh.

  • Downloads: a segmented control across enabled clients, hidden when you only have one. Torrent view has down and up speed pills, a speed-limits control (qBittorrent, rTorrent and Transmission), Add Torrent with a paste-magnet field, and a filter and sort sheet. Long-press a row for multi-select with Pause, Resume, Category (qBittorrent only) and Delete. Deleting always asks Delete versus Delete + Files. The Usenet view adds queue-wide pause and resume, and Add NZB by URL or file upload.
  • Calendar: a month grid merging Sonarr air dates with Radarr release dates (Digital, Physical or In Cinemas). Green dots mark episode days, amber dots movie days. Chips are All / TV / Movies plus an Include unmonitored toggle that is remembered between launches.
  • Activity: merges Tautulli, Tracearr, Jellystat, Jellyfin and Emby. Sub-tabs are Streams and History; History only appears when a source that supports it is configured, because Jellyfin and Emby report live sessions only. Streams refresh every 5 seconds, history every 60.
  • Movies (Radarr): Library, Queue, Wanted, with a filter and sort sheet and long-press action sheets.
  • TV (Sonarr): Library and Calendar, with filter and sort and a "Search Missing Episodes" confirmation.
  • Music (Lidarr): Library, Queue, Wanted.
  • Requests (Seerr): Discover, Search, Requests, with per-row approve and decline, and a Customize link to reorder your Discover sliders.
  • Indexers (Prowlarr and Jackett, with a source switcher when both are attached): Indexers, Search, and (Prowlarr only) Stats. Prowlarr indexers toggle with a power button and grabbing a release confirms first; each Jackett indexer has a Search button that opens the Search tab pinned to that one tracker (tap the pill to unpin) and a Test button that browses it and reports back, and grabbing opens a sheet to send the release to one of your torrent clients or open its details page.
  • Plex: Now Playing, Recent, On Deck, Libraries. Jellyfin and Emby share a screen: Now Playing, Recent, Continue Watching, Libraries.
  • Glances (headed "Server"): CPU, Memory, GPU, Disks, Disk I/O, Network and Containers cards. Containers are read-only here.
  • unRAID: Array, Pools, Unassigned and Containers, with Start, Restart and Stop actions per container.
  • Bazarr: Movies, Episodes, History, with a per-item search button for missing subtitles.
  • Services: a two-column grid of your enabled, attached services with a status dot each. Long-press a tile to drag-reorder, and the order saves per dashboard. When a tile is not green the reason is printed under its name rather than leaving a silent red dot.

Queue issues (Radarr, Sonarr, Lidarr)

Sometimes a grab gets stuck. Most often it finished downloading but the *arr refuses to import it: non-media files in the release, a sample-only folder, episodes missing from the pack, no matching movie or series. It can also be the download itself going wrong, such as a torrent stalled with no connections. When that happens the Movies, TV and Music screens show a queue issues banner above the chip row. Amber means a warning, red means the download failed outright. It only appears when something is actually stuck, and it follows the instance you currently have selected.

Tapping it lists every stuck grab with the reason the service gave and the release name. Tap one for three actions:

  • Remove from queue: drops the grab and deletes it from the download client. The release stays eligible, so the same copy can be grabbed again.
  • Blocklist & Search: also blocks the release so it is never grabbed again, then starts a search for a replacement. This is the one you want for a bad copy.
  • Blocklist only: blocks the release with no replacement search.

All three ask for confirmation first, and blocklisted releases show up under System → Blocklist in Radarr, Sonarr or Lidarr itself.

Global search

The magnifier in the Dashboard header opens Search. It needs two characters and covers Radarr, Sonarr, Lidarr, Seerr, Prowlarr and Jackett, grouped into Movies, TV Shows, Music, Requests and Releases. Each section loads independently, so a slow indexer search never blocks the rest. Plex, Jellyfin and Emby library search is not included, and the magnifier is hidden entirely when none of those six services is attached.

Movies, shows and artists you already have appear first. They are matched against the library the app has already loaded, so they show up as you type instead of waiting on the Radarr, Sonarr or Lidarr metadata lookup. Those rows carry an In library badge and open the item's own screen instead of the add flow. The same ordering applies on the dedicated search screens behind the magnifier in the Movies, TV and Music tabs.

Magnet links

Dashboarr registers itself as a magnet handler. Tapping a magnet link in a browser opens the Downloads tab with the add card pre-filled. With more than one torrent destination you first get an Add Torrent To sheet. SABnzbd and NZBGet are not magnet destinations.

8. Notifications and the optional backend #

Without a backend

Dashboarr raises local notifications while the app is running. There is no background task, so a closed or force-quit app produces nothing. This is the number one notification question, and the honest answer is: the toggles work, but only while the app is open.

Settings, Notifications has a master Enable notifications switch and, when on, seven category toggles: Torrent completed, SABnzbd completed, NZBGet completed, Movie downloaded, Episode downloaded, Service offline, New Seerr request.

Every instance editor also has a Notifications card where each category is a three-way picker: Use default, Always notify, Never notify. Every service kind has at least "Service offline". Tracearr additionally exposes seven categories that exist only per instance, with no global toggles.

Two notifications for one download is expected. Your download client fires "Download complete" when the file finishes, and Radarr or Sonarr fires "Movie downloaded" or "Episode downloaded" a minute later when it imports. Silence whichever you prefer.

What the backend adds

The optional self-hosted backend moves the watching server-side, so events reach your lock screen with the app closed. It also adds webhook ingestion, a service-offline watchdog, and an optional Apprise fan-out to Discord, Telegram, ntfy or email.

Minimum docker-compose.yml:

services:
  dashboarr-backend:
    image: ghcr.io/renzobeux/dashboarr-backend:latest
    container_name: dashboarr-backend
    ports:
      - "4000:4000"
    volumes:
      - ./data:/data
    restart: unless-stopped

Then docker compose up -d and docker logs -f dashboarr-backend. All state lives in one SQLite file at /data/dashboarr.db. On Synology Container Manager, create the lowercase data folder inside your project folder in File Station first, or the build fails with a bind-mount error.

It also runs without Docker on Node.js 20+: clone the repo, cd backend/dashboarr-backend, then npm install, npm run build, npm start.

Environment variables, all optional

VariableDefaultNotes
PORT4000
HOST0.0.0.0
DATA_DIR/data in Docker
LOG_LEVELinfo
PUBLIC_URLunsetSet this to your public https URL and the startup QR embeds it, so pairing is one scan.
PUSH_RECEIPTSfalse
TRUST_PROXYfalse
OFFLINE_THRESHOLD3Consecutive failed 30-second health checks before a "service offline" push. 3 is about 90 seconds; use 10 for about 5 minutes if your DDNS is slow.
BACKEND_USE_REMOTEfalsePoll services over their local URLs (default) or remote URLs. The app's own local/remote toggle is always ignored server-side.
CONFIG_ENCRYPTION_KEYunsetAt least 16 characters. Encrypts stored service credentials at rest with AES-256-GCM. A shorter value makes the process refuse to start. Losing an in-use key means re-pushing config from the app.

Pairing

The pairing QR and token are printed to the backend's startup log only. There is no web pairing page. Read them with docker logs -f dashboarr-backend.

In the app: Settings, Notifications, Backend. Enter the Backend URL, then either Scan pairing QR or Enter token manually.

  • The token is a 32-character hex string, valid for about 10 minutes, and single-use. If it expires, restart the container to print a fresh one. There is no regeneration endpoint by design.
  • With PUBLIC_URL set, the QR carries the URL and the token together. Without it, the QR carries only the token, so type the backend URL into the app first.
  • On success the app pushes its full config snapshot to the backend. After that, any config change re-syncs automatically after a couple of seconds, so make settings changes while your phone can see the backend.
  • If pairing fails with "Push permissions denied or projectId missing", the OS notification permission is off. Turn on notifications for Dashboarr in your phone's settings, reopen the app, and pair again with a fresh token.

Once paired, the Backend screen offers Send test push, Rotate secret and Unpair, and tells you the state of the local fallback. The app polls the backend's health every 60 seconds and needs two consecutive failures before handing back to local watchers, so allow a couple of minutes for that transition.

Exposure

The backend does not need to be exposed to the internet. It needs outbound access to reach Expo's push service, and to be reachable by your phone on your home network. Pushes are delivered through Expo and then Apple or Google, so they still arrive over cellular with nothing port-forwarded. LAN-only is the recommended setup.

If you do expose it, the backend does not terminate TLS itself. Put a reverse proxy or Cloudflare Tunnel in front and set PUBLIC_URL to the https URL, otherwise an http to https redirect silently turns the pairing POST into a GET and 404s.

Webhooks

Webhooks make Radarr, Sonarr, Seerr and Tracearr events near-instant instead of waiting for a poll. Only six kinds have endpoints today: Radarr, Sonarr, Seerr, Bazarr, Tautulli and Tracearr, and of those Bazarr and Tautulli events are ingested and logged but do not raise a push yet.

Get your exact URLs and the shared secret from the file the backend writes at startup:

docker exec dashboarr-backend cat /data/webhook-urls.txt

That file is mode 0600 and the secret is deliberately never printed to stdout, because logs often get shipped elsewhere.

Two URL shapes are supported: the secret in an X-Dashboarr-Secret header (preferred, keeps it out of proxy logs), or the secret as a path segment for services that cannot send custom headers. Configure Radarr and Sonarr as a Custom webhook connection on the Download event, and Seerr on MEDIA_PENDING. Each of those sends a test event that produces a confirmation push, so you can verify the wiring without waiting for a real download. Tracearr is path-secret only, configured as its JSON Webhook agent, and must include ?instance=<uuid>.

The ?instance=<uuid> parameter is optional elsewhere. Adding it prefixes push titles with the instance name and namespaces dedupe keys, so two Radarrs grabbing the same release produce two pushes. Copy the UUID from the Webhook Attribution card in the instance editor, which appears once a backend is paired. A wrong or stale value never drops the event; the backend quietly falls back to kind-only attribution.

Apprise

Settings, Notifications, Backend, "Apprise notifications" also sends to Discord, Telegram, ntfy or email via an Apprise server. Add your service URLs in the Apprise server's own config under a key, then paste that server's full /notify/<key> URL into the Apprise notify URL field, with optional Tags to filter which saved URLs fire. There is a Send Apprise test button. Apprise is additive: Expo push keeps working, the same toggles apply, and Apprise fires even with no phone paired. It requires a paired backend.

9. Backup and restore #

Settings, Backup & Storage. Export settings writes a single file named dashboarr-config.json and hands it to the system share sheet, so you choose where it lands. Despite the extension, the contents are encrypted: a small envelope wrapping AES-256-GCM ciphertext, with the key derived from your passphrase.

  • A passphrase is mandatory, minimum 8 characters, typed twice. The dialog says it plainly: there is no recovery, and forgetting the passphrase makes the backup useless.
  • Export also requires your device biometric or passcode. Cancelling that OS prompt silently aborts the export.
  • "Remember on this device" stores the passphrase in the Keychain or Keystore behind biometrics, and adds a Use saved passphrase button next time. It only helps on the same device; migrating to a new phone always needs the typed passphrase.

What is in the file: every configured instance (names, local and remote URLs, per-instance flags) and its secrets (API key, username, password, custom headers), all dashboards with their widgets and per-widget settings, the active dashboard, home networks, the auto-switch and treat-VPN-as-home settings, Services tile order, Wake-on-LAN devices, haptics, global custom headers, UI scale, theme, notification settings, and your backend pairing.

Treat the file like a password vault. It contains every API key and your backend shared secret.

What is not in it: cached posters, live network state, onboarding flags (so a restored install replays the intro), and anything about your media servers' content.

Import settings confirms first, then opens a file picker, then asks for the passphrase. It is a full replace, not a merge, so export your current setup first if you might want it back. Older unencrypted backups from previous app versions still import without a passphrase. Backups are forward-migrated automatically; a backup made by a newer app version is refused with a message telling you to update the app first.

Two deliberate behaviours after an import:

  • Any pinned access-point BSSIDs on your home networks are stripped, because a pin recorded on the old phone will not match on the new one. Matching falls back to SSID only; re-pin manually if you want it.
  • If auto-switch is on, services fall back to remote URLs until the home WiFi is re-confirmed, and the app re-asks for Location.

Backend push must be re-paired on the new phone, since the push token belongs to the physical device. Restart the backend for a fresh pairing token.

The same screen also holds Clear image cache, for when cached posters and backdrops add up, and the Demo Mode toggle under Advanced.

10. Appearance and updates #

Settings, Appearance has three controls:

  • UI Scale: Normal, Large (+15% fonts, spacing and icons) or Extra Large (+30%). It applies live across the whole app. Poster grids intentionally drop from three columns to two at Large and above.
  • Theme: Default (neutral zinc), Ember (warm red-brown), Midnight (deep navy), Forest (dark green) and Violet (dark purple). All five are dark; Dashboarr is dark-mode only and has no light theme. A theme re-tints backgrounds, surfaces and borders but deliberately leaves accent colours alone, so the difference is subtle by design.
  • Haptic feedback: vibrations on taps, toggles and refreshes.

Settings, About holds View on GitHub, Report an issue (pre-filled with your version, runtime and platform), Support development, Show workspace tour, and the app version card with a Check for updates button.

The app checks for a newer version automatically at most once every 24 hours, a few seconds after launch. Sideloaded Android builds check the GitHub releases page; store installs check their store. The prompt offers Update now and Skip this version; dismissing it counts as "later" and silences it for seven days, while "Skip this version" is permanent for that version number.

Because updates can ship over the air, the store listing may lag behind what you are actually running. The version at the bottom of Settings, About is the truth, and it is the one to quote in a bug report.

11. Troubleshooting #

Everything is red, or shows "Remote", and I am at home.

Auto-switch is on and the app has not confirmed you are on a saved home WiFi. Check Settings, Network, Home Networks, Network diagnostics: if "Away from home" reads yes (remote-only), the app cannot read your network name. Grant Location permission, enable Precise Location on iOS, and make sure the network you are on is saved (SSID-only if you have a mesh). A service with only a Local URL is unreachable while away, by design.

Everything broke when I turned on my VPN.

Enable Settings, Network, "Treat VPN as home", or switch to addressing services by their VPN hostname with "Always use Remote URL".

Test Connection passes but the screen is empty or 404s.

Test Connection authenticates against each service's API root, so a wrong port normally fails it outright rather than passing. When the test passes but a screen stays empty, confirm the URL is the bare origin with no /api/... suffix, and that the instance points at the service you think it does.

A service is green in Settings but missing from the Services tab.

The Services tab only shows services attached to the dashboard you are on. Attach it via Dashboard, sliders icon, Attached instances.

A widget I expect is not in the Add widget sheet.

Its service is not attached to this dashboard. Attach it first. If it appears greyed out under "Requires setup", the service is attached but disabled in Settings.

A widget disappeared from the dashboard.

Either its service was disabled or un-attached (it is hidden, not deleted, and returns when you restore it), or it has "Hide when empty" on, or, for Service Health and Health Alerts, "Hide when all healthy". Enter widget edit mode to see hidden cards, marked with a crossed-out-eye icon.

qBittorrent says wrong username or password.

qBittorrent 5.2.0 changed its login API. Update to app v1.6.3 or newer, and backend v1.2.5 or newer. Also check the URL is the bare origin with no /api/v2 suffix.

Radarr or Sonarr behind Authentik or Authelia will not connect.

The proxy is returning its HTML login page instead of JSON. Exclude the API path from the proxy: for Authentik, add ^/api and ^/ping to Unauthenticated Paths. Custom Headers cannot fix cookie-based SSO.

Seerr requests fail with HTTP 403 "invalid csrf token".

Turn off CSRF protection in Seerr, which otherwise limits the API to read-only.

Glances shows no disks.

A Dockerized Glances cannot see the host filesystem. Bind-mount the host root read-only (- /:/rootfs:ro). CPU and RAM keep working because they come from /proc, which is why it looks like an app bug.

Backend paired but no notifications arrive.

Check, in order: the master switch under Settings, Notifications is on; the backend only notifies about events that complete after it starts, so anything already finished is treated as seen; for Radarr, Sonarr and Seerr the webhook test event should produce a confirmation push; and Bazarr and Tautulli webhooks are logged but do not push yet.

Wake-on-LAN does nothing.

Set a subnet-directed broadcast such as 192.168.1.255 rather than the default 255.255.255.255, which many routers drop, and confirm you are on the same LAN as the target. The MAC is only validated when you press Wake, so an invalid MAC surfaces as a send-time error.

The Health Alerts widget is empty.

That is correct. With nothing wrong it shows "All services healthy". To drop the card off the dashboard entirely while everything is clean, turn on Visibility, "Hide when all healthy" in its widget settings.

Filing a bug.

Use Settings, About, "Report an issue", which pre-fills your version and platform, and include the Network diagnostics rows if the problem is connectivity related.