Repository navigation
Updates
wranglarr keeps the stack's images current: it checks the registries, backs up what an update touches, swaps the container, and puts the old one back if the new one doesn't come up. Updates in the sidebar lists every managed container. The sidebar badge counts the containers with a newer image.
Requires the Docker socket (/var/run/docker.sock mounted, [docker] socket
set). Both are in the default compose.yaml.
| Container | Role | Included when |
|---|---|---|
every app with container = "…" in wranglarr.toml
|
app |
always |
arrs-postgres ([postgres] container) |
postgres |
any app has databases, and include_postgres = true
|
arrs-vpn ([vpn] container) |
vpn |
the VPN is enabled, and include_vpn = true
|
names in [updates] extra
|
extra |
listed |
[updates] exclude removes names from all of these. wranglarr's own
container has a row too, but Update all and auto-update leave it alone: it
is updated only when you ask, or on its own opt-in schedule
(Keeping wranglarr itself up to date).
Each container's Update track selects the image tag to check and update. Leave it blank to follow the deployed image's tag or digest. Suggestions are specific to the image publisher, so LinuxServer and Hotio containers get their own choices. Any valid custom Docker tag also works, including for extra containers and private repositories. Track selection keeps the existing image repository; it does not switch publishers or change the app's internal branch setting.
Save track stores a preference without pulling, stopping or restarting anything. Press Check now, then Update to switch. Scheduled updates also use the selected track, subject to holds, blocked images and backup checks. Changing track invalidates the previous check. Selecting a tag on a digest-pinned image explicitly switches from the pin to that tag; leaving it blank preserves the pin.
| Image publisher / app | Suggested tracks |
|---|---|
| wranglarr | Stable (latest), Development from main (edge) |
| LinuxServer Radarr, Lidarr, Prowlarr | Stable (latest), Development (develop), Nightly (nightly) |
| LinuxServer Sonarr | Stable (latest), Development (develop) |
| LinuxServer Bazarr | Stable (latest), Prerelease (development) |
| LinuxServer SABnzbd | Stable (latest), Prerelease (unstable), Nightly (nightly) |
| LinuxServer NZBGet | Stable (latest), Testing (testing) |
| LinuxServer qBittorrent | Stable with libtorrent v2 (latest), stable with libtorrent v1 (libtorrentv1) |
| Official Seerr | Stable (latest), Development (develop) |
| Hotio Radarr, Lidarr, Prowlarr, SABnzbd | Stable (release), Testing (testing), Nightly (nightly) |
| Hotio Sonarr, Bazarr | Stable (release), Nightly (nightly) |
| Hotio qBittorrent, NZBGet | Stable (release) |
| Gluetun | Development (latest); use a custom vX.Y.Z tag for a specific release |
| Postgres | Keep the deployed major tag, or enter a custom tag; major upgrades remain refused |
The presets were checked on 2026-10-27 against the publishers' documentation:
LinuxServer,
Hotio,
Seerr, and
Gluetun.
The page links each recognized image to its publisher's track descriptions.
Unknown images receive no guessed development channels. In particular,
Gluetun's latest is development, and the listed qBittorrent images do not
advertise a separate development channel.
Development tracks can migrate databases. Returning to a stable track may require restoring the pre-update backup as well as changing the image.
UI preferences live in wranglarr's state database. They do not edit your
Compose files or .env. Keep the Compose image on the selected tag so that a
later docker compose up does not change the track back. For example, for
LinuxServer Radarr, set RADARR_IMAGE=lscr.io/linuxserver/radarr:develop in .env.
With the per-app VPN override, keep the Hotio repository instead.
For wranglarr's development track, select edge on its own row. The next check
compares against edge, and Update now (or its own schedule, see
Keeping wranglarr itself up to date)
switches to it. Also set the track in .env, so a later docker compose up
stays on it:
WRANGLARR_IMAGE=ghcr.io/wranglarrr/wranglarr:edgeMain builds publish edge; release tags publish latest. The setup wizard
launched from an edge image keeps that image track.
[updates.tracks]
wranglarr = "edge"
radarr = "develop"
arrs-postgres = "17"Keys are container names. A UI selection wins over the file. Clearing the input explicitly follows the deployed image; Reset removes that UI override and returns to the file default. Reset does not undo an already applied image change. Track preferences are separate from the Automation settings and their reset.
| Status | Meaning |
|---|---|
| up to date | the registry's digest for the tag matches the running image |
| update available | the tag has moved on the registry |
| update blocked | this exact new image was tried and rejected (failed to start, or a refused Postgres major upgrade). It's skipped automatically until the registry publishes a newer one; Try again forces it |
| held | you (or a rollback) pinned it: skipped by Update all and auto-update, but a per-container Update still works |
| pinned | the compose file references a digest (image@sha256:…), so there's nothing to follow |
| local build | not from a registry |
| not running | no container by that name (profile off, or a different container_name) |
| check failed | the registry lookup failed (rate limit, private registry without credentials, network) |
For each container, wranglarr asks the Docker daemon for the registry's current
digest of the image tag the container was created from
(GET /distribution/{ref}/json). That is a manifest lookup, so nothing is
pulled. If the digest isn't among the running image's RepoDigests, an update
is available. The default schedule is every 6 hours. Check now runs a
check on demand.
Registries throttle bursts (GHCR, behind lscr.io, answers 429 toomanyrequests for a moment), and a busy or filtering DNS resolver (AdGuard,
Pi-hole) can drop a lookup. So:
- a "too many requests" or a network blip is asked again three times (after 2, 10 and 30 seconds) before the container shows check failed;
- a registry's answer is kept for 10 minutes, so the check, automatic update and self-update jobs, and the check after each update, don't ask about the same tag again (Check now always asks afresh);
- a schedule on a set minute (
0 */6 * * *,0 5 * * *) starts 5 to 90 seconds after it, rather than in the same second as everyone else's checker.
A check failed that persists for every container from one registry while other lookups work usually means something on the host is blocking or rate-limiting DNS for it.
-
Pull every selected image first, so a registry problem can't leave the stack half-updated. If the pulled image is the one already running, nothing happens.
-
Safety checks. A Postgres image whose
PG_MAJORdiffers (for example 17 → 18) is refused, because a major upgrade needs dump/restore, not a container swap (Service: Postgres). The digest is then blocked. -
Back up first (on by default). Each suite with an app being replaced is backed up, with trigger
pre-update. A Postgres update backs up every suite that has databases. An app whose part of that backup failed is not updated unless the request setsforce. Pre-update versions are kept apart from the suite's own retention (Pre-update versions).wranglarr then checks that this backup could put the app's data back with the app stopped (step 6). If it couldn't, a failed update couldn't be undone, so the app is skipped unless the request sets
force. That's the case when the backup can't be read, or for an arr on SQLite whose config folder isn't mounted into wranglarr, or is mounted read-only. The stockcompose.yamlmounts the arrs' folders read-only, which is all the arrs on Postgres need; for one on SQLite, drop the:rofrom its line. -
Recreate. wranglarr inspects the old container and creates a new one with the same settings:
- Kept: mounts, environment, labels (including the compose labels, so
docker composestill owns it), restart policy, capabilities, devices, published ports, networks and aliases. - Reattached: anonymous volumes.
- Dropped: values the container only had because the old image defined
them (env, labels, cmd, entrypoint, exposed ports), so the new image's
defaults apply, as with
docker compose up.
The old container is stopped and parked as
<name>-wranglarr-olduntil the new one is up. - Kept: mounts, environment, labels (including the compose labels, so
-
Wait for healthy, up to
health_timeoutseconds (default 300):- A container with a Docker healthcheck must report
healthy. - A container without one must stay running.
- Then the app's own API must answer. For Postgres, it must accept connections.
- A container with a Docker healthcheck must report
-
On failure, the new container is removed. If wranglarr had asked Docker to start it, the new version may already have migrated the app's database or config files, which the old version can't read. That counts even when Docker's answer never came back. So the app's data goes back from the pre-update backup first, with the app stopped:
- Postgres databases, through
pg_restore; - for an arr on SQLite, its database file (
radarr.dband the like) from the backup zip. Its-wal,-shmand-journalfiles go, since they belong to the newer database, and the replaced files are kept in wranglarr's state folder in case you need them. The rest of the config folder (posters, logs, the app's own backups) stays as it is; - for the download clients and the other apps without a backup API, their config folder.
Then the old container is renamed back and started, and wranglarr waits for it to come up healthy. An arr on Postgres gets its own backup zip back through its API once it's up. The tag is pointed back at the old image so a later
docker compose updoesn't switch to the rejected one, and the new digest is blocked. Other containers on the same tag aren't tried on that image in the same run.When the data can't go back, nothing is started on it. If the backup can't be read any more, or a restore fails halfway, the old container stays stopped and parked as
<name>-wranglarr-old. Starting either version could make things worse: the old one on data the new one migrated, or either on half-restored data. The run log says what had been put back before it stopped, and the container's row on the Updates page says what to do. If Docker won't remove the new container, wranglarr stops it, and the row says whether it might still be running. Restore the backup by hand (Backups and Restore), then rename the parked container back and start it.Recovery distinguishes the container coming back from its data being restored. If the API disappears before config upload, the report says the upload was not attempted. If an upload's response is lost, its outcome stays unknown even if the app answers again; inspect its configuration before retrying. A refused upload from an app that still answers is reported separately from an app that fails to return after restarting.
A failed update also leaves a failure report: see below.
- Postgres databases, through
-
Shared network. Apps on
network_mode: service:vpnlive in the VPN container's network namespace, which is tied to its container id. Whenarrs-vpnis replaced, every container in its namespace is recreated onto the new one automatically (history actionrecreate). Without this they'd lose the network, which is the "VPN on, apps have no network" entry in Troubleshooting. The new VPN stays either way. If an app can't be recreated onto it, that app is marked failed, the run ends partial, and the app has no network until you rundocker compose up -d <app>in the stack folder.A namespace-only recreation preserves each dependent's exact running image, even if its tag was pulled elsewhere or it follows a different track. A dependent selected for an image update goes through its own backed-up update step after following the VPN; held or unselected dependents are not upgraded.
Order within one run: VPN, then Postgres, then extras, then apps. If the VPN or Postgres fails, the run stops there.
When an update fails, the new container's last log lines and exit state are kept before it's removed. wranglarr turns them into a report you can file the problem with: the app, the image, the versions, the error, the exit code, whether it ran out of memory, its health check output, what happened afterwards, and the log lines that matter (the errors and their stack traces, or the last lines when nothing looks like an error). "Afterwards" says how Docker shows the container once wranglarr is done: the old version back and healthy, back but unhealthy or not started, or left stopped for you to recover. When the new container couldn't be removed, it says so, and whether it may still be running, which is the first thing to sort out. It also says whether the data went back, and whether the new version is now skipped.
Before anything is kept, wranglarr takes these out of every part of it, including the image name and versions:
- every API key, password and username wranglarr knows, and the Postgres and VPN keys;
- anything shaped like a secret:
apikey=,token=,password=,passkey=and the like (quoted values whole),Authorizationand other login headers, cookies, logins in URLs, JWTs, and long runs of key characters (the arrs' 32-character keys, tracker passkeys); - email addresses, public IP addresses (your LAN's stay), your domain (from the reverse proxy settings and the apps' URLs) with every name under it, other domain names in any case, a private registry's name, and the user name in home folders.
This is a best effort, not a promise. A secret in a shape wranglarr doesn't know can get through, and log lines can still name films, series or files in your library. So the button below asks you to read the report first.
Each report has a fingerprint, wf- and ten characters. It comes from
the app, the image, the new version, the error and the first error line, with
times, numbers and ids left out, so the same failure gets the same
fingerprint on every machine. The issue title carries it, so one search finds
an earlier report of the same thing. Each failure keeps its own report, so
two containers failing the same way in one run each have theirs, and a report
says how often its fingerprint has been seen here.
The run log and the update_failed notification point to the report's page,
/updates/report/<run>-<container>, also linked from the container's row on
the Updates page. Its Check whether it's been reported button asks you to
confirm you've read the report, copies it to your clipboard and asks GitHub
for issues in the app's repo, open or closed, with that fingerprint:
- Reported already: the oldest such issue opens. Paste the report in a comment there, or add a thumbs-up.
- Not yet: a new issue opens with the title, fingerprint, app, versions and the report filled in. A report too long for a link is pasted by hand.
wranglarr asks GitHub itself, so the search carries the fingerprint and
nothing about your setup. A browser asking would send GitHub your wranglarr's
address. For the same reason, links from wranglarr to other sites don't send
them the page they came from (Referrer-Policy: same-origin). The page also
has the plain search and new-issue links, and the run log has the search
link, for doing it by hand.
Problems with wranglarr's apps, Postgres and the VPN go to wranglarr's repo.
An extra container's go to the repo its image names in its
org.opencontainers.image.source label, with a note that it isn't
wranglarr's; one whose image names none gets a report and no links. The last
25 reports are kept; Forget it on a report's page removes one.
Roll back is offered while the host still has the image from before the
last successful update. It tags that image back onto the container's tag
(lscr.io/linuxserver/radarr:latest → the previous image), recreates the
container on it, and holds the container, so the next auto-update doesn't
undo the rollback. Release the hold when a fixed image is out.
An app may migrate its database on the new version, and the old one can then refuse to start. So the Roll back dialog offers to restore its data from the backup taken just before that update, ticked by default and showing that backup's date. Everything the app changed since then is lost: new imports, added items, settings. The data goes back while the container is stopped, before the old version starts, and an arr's own backup zip after it's up.
For an arr on SQLite, its database file goes back with the app stopped, as after a failed update (step 6 above). That needs its config folder mounted into wranglarr read-write; without it, a rollback with the data is refused and says so. Roll back without the data, or mount the folder.
If the rollback fails once it has begun putting data back, neither version is
started on that data: the container stays stopped and parked as
<name>-wranglarr-old, and the run log says what had been put back. Recover
it as below.
When that backup isn't kept any more, the dialog says so and the rollback only swaps the program. Restore another version by hand if the app doesn't start (Backups and Restore).
docker image prune removes untagged old images, and with them the ability
to roll back.
After a track change, the container's tag is the new track's: rolling back from
develop tags the stable image as radarr:develop on this host, so compose up
keeps the rolled-back program. The next pull of develop replaces that tag.
While it swaps containers, wranglarr parks the old one as
<name>-wranglarr-old. If wranglarr is stopped in the middle (host restart,
docker kill), the run is marked failed when it starts again, and the parked
container stays. The same goes for an update or rollback that stopped because
the data couldn't go back. The Updates page says so on the container's row, and an
update or rollback that would swap it again is refused until it's sorted out by
hand: if the container works, remove <name>-wranglarr-old; if not, remove the
container, put its data back if the row says to, and rename <name>-wranglarr-old
back to its name, then start it.
wranglarr can't swap its own container from inside it, since stopping that container stops wranglarr. It hands the job to a short-lived helper instead.
Update now. When a check finds a newer wranglarr image, the dashboard
shows a banner with the release notes link and an Update now button.
wranglarr's row on the Updates page gets the same button. You have to be
signed in on wranglarr's pages to press it: an API key can't start it. The
self_update_available notification says the same and gives the shell
command as a fallback.
On a schedule. In Automation, tick Update wranglarr and set its
schedule (self_update and self_schedule in [updates], default off and
30 5 * * *). The schedule runs a check first and updates only when there's
a newer image that wasn't rejected before. Neither Update all nor the
stack's auto-update ever includes wranglarr.
What happens (one run under Activity, suite system, kind self-update):
- wranglarr pulls its image. If nothing new came down, the run ends there.
- It copies its state database (
VACUUM INTO) andwranglarr.tomlinto<state folder>/self-update/<time>/. The newest three copies are kept. - It starts a helper container,
<name>-wranglarr-updater, from the new image. The helper gets the Docker socket and wranglarr's state folder, wranglarr's user, groups and security options, and nothing else: no network, no capabilities, a read-only root. It is removed when it exits. - The helper stops wranglarr and parks it as
<name>-wranglarr-old, the same swap any other container gets (step 4 of What an update does). It runswranglarr-init(the one-shot that prepares the folders) on the new image, then creates the new wranglarr with the old one's settings. - The new wranglarr counts as up when
wranglarr healthanswers inside it withinhealth_timeout. The helper then removes the parked containers. - The new wranglarr reads the helper's report from the state folder. It copies the helper's log into the run, records the history row, sends the usual update notification and finishes the run. The run's page picks up by itself once wranglarr is back.
If the new one doesn't come up, the helper removes it, puts the state
database back from the copy (the new version may have changed it), restores
the old wranglarr-init, renames the old wranglarr back and starts it. It
points the tag back at the old image and blocks the new digest, so the
schedule skips that image. Try again on the row forces it. The old
wranglarr then finishes the run as failed.
When wranglarr is running, a helper that dies without a final report fails the run, as does a report past its deadline. wranglarr ignores and removes a report older than a day, or one it can't read. If no wranglarr is running, inspect the helper's report and parked containers before attempting recovery.
When it isn't offered. The row then says why, and the banner shows the shell command instead:
- Docker control is off, or wranglarr isn't running in a container it can
find (by
HOSTNAME). - The image is one you built (
wranglarr:local) or is pinned to a digest (image@sha256:…). There's no newer image to pull for those. - The Docker socket or the state folder isn't a mount of wranglarr's container.
Then update it from the stack folder:
docker compose pull wranglarr && docker compose up -d wranglarror, for an image you built, build it again (The wranglarr image).
On rootless Podman this works the same through Podman's socket. The helper
finds wranglarr-init from the labels docker compose puts on wranglarr's
container. podman-compose doesn't add those labels, so there the helper
skips that step. The folders wranglarr-init set up stay as they were. That
matters only when a release changes what it sets up, and its release notes then
say so under Before you update.
If the new wranglarr fails and its saved state cannot be restored, the helper
leaves the old container stopped under <name>-wranglarr-old. It retains the
snapshot and self-update.json report in wranglarr's state folder. Inspect the
report and restore a verified state snapshot before renaming and starting the
old container; starting it against state changed by the failed candidate may
be unsafe.
| Setting | Default | |
|---|---|---|
| Enabled | on | off = no scheduled checks or updates |
| Check schedule | 0 */6 * * * |
cron, container TZ; blank = manual only |
| Auto-update | off | apply every update available (not held, not blocked) on the schedule below |
| Auto-update schedule | 0 5 * * * |
runs a fresh check first |
| Back up first | on | |
| Update wranglarr | off | update wranglarr itself on the schedule below (above) |
| wranglarr's schedule | 30 5 * * * |
runs a fresh check first; skips an image that was rejected before |
Values come from [updates] in wranglarr.toml. Changes in the UI are stored
in the state db and win over the file until reset to config file.
Don't run Watchtower or another auto-updater against the same containers: it would replace them without the backup and health checks, and break the VPN namespace link described above. That includes wranglarr's own container: use Update wranglarr instead.
[updates]
enabled = true
check_schedule = "0 */6 * * *"
auto_update = false
auto_schedule = "0 5 * * *"
backup_first = true
pre_update_keep = 1 # pre-update backups kept per suite, apart from its retention
pre_update_settle_days = 7 # updates closer than this are a run, which keeps its first backup too
self_update = false # update wranglarr itself on self_schedule
self_schedule = "30 5 * * *"
health_timeout = 300 # seconds
include_postgres = true
include_vpn = true
extra = [] # more container names to manage, e.g. ["prowlarr", "overseerr"]
exclude = [] # never touch these[postgres] container (default arrs-postgres) and [vpn] container
(default arrs-vpn) name the infrastructure containers.
GET /api/updates containers + status, settings, next runs, history
POST /api/updates/check check now (synchronous) -> same shape
POST /api/updates/apply {"containers": ["radarr"], "force": false} | {} = all available -> {"run": id}
POST /api/updates/rollback {"container": "radarr", "restore_data": true} -> {"run": id}
POST /api/updates/hold {"container": "radarr", "hold": true}
POST /api/updates/self update wranglarr itself (a signed-in browser only) -> {"run": id}
PUT /api/updates/settings {"enabled":true,"check_schedule":"…","auto_update":false,"auto_schedule":"…","backup_first":true,"self_update":false,"self_schedule":"…"}
DELETE /api/updates/settings back to the config file
PUT /api/updates/tracks/{container} {"tag":"develop"} | {"tag":null} = follow deployed image
DELETE /api/updates/tracks/{container} back to that container's file default
GET /api/updates/reports the kept failure reports, newest first
GET /api/updates/reports/{key} one, with its Markdown, title and links
GET /api/updates/reports/{key}/reported the issue it was already reported in, or {"issue": null}
DELETE /api/updates/reports/{key} forget it
Update and rollback runs appear under Activity with suite system.
This wiki is built from wiki/ in the repo, and edits made here are replaced on the next build: change the page there instead. Also at https://wranglarrr.github.io/Wranglarr/.
Start here
- Overview
- Quickstart
- Setup
- Bringing in existing apps
- TrueNAS SCALE
- Environment variables
- Login
- App logins and AuthTunnel
- Security
Using wranglarr
- Dashboard, links and logins
- Notifications
- Live status
- Connections: media servers, Seerr
- Backups and restore
- Container updates
- Release notes
- Roadmap
- App connections
- Indexers
- Community configs
- Shared settings
Services
Networking
Reference