Routine Maintenance
Most UltraTorrent incidents are slow-burning: a disk that fills, a torrent count that creeps past rTorrent's crash ceiling, an index that never got rebuilt, a backup that has silently been failing for three months. None of them announce themselves. All of them are caught by a routine that takes minutes.
Purpose
To catch problems while they are still cheap.
When to use this
Set a recurring reminder. The daily check is 60 seconds; the monthly one is half an hour.
Prerequisites
- Shell access to the host.
- A working backup.
Video coming soon.
The routine at a glance
Daily (60 seconds)
A single command tells you most of what you need:
cd /path/to/ultratorrent
# 1. Is everything up and healthy?
docker compose ps
# 2. Anything screaming?
docker compose logs --since 24h backend | grep -iE "error|fatal|refused" | tail -20
# 3. Is the engine online?
docker compose exec backend wget -qO- http://127.0.0.1:4000/api/system/live
What you are looking for:
| Red flag | Meaning | Go to |
|---|---|---|
A container shows Restarting | It is crash-looping | Startup and boot |
A container shows unhealthy | The healthcheck is failing (e.g. rTorrent's SCGI port is not listening) | Engines |
blocked internal address | The SSRF guard is blocking your indexer | SSRF |
internal_error: priority_queue_insert | rTorrent's crash bug | rTorrent |
| Downloads at 0 B/s across the board | Possibly dead torrents holding every queue slot | Queue |
restart: unless-stopped means Docker keeps bringing it back, so docker compose ps can look healthy while the process dies every twenty minutes. The truth is in
RestartCount — see the weekly check.
Weekly (10 minutes)
1. Disk space
The most common cause of a stack that suddenly stops working is a full disk. Postgres in particular fails in confusing ways when it cannot write.
df -h
# Which Docker volumes are eating it?
docker system df -v | head -30
# The downloads tree is usually the culprit
du -sh "$(docker volume inspect ultratorrent_downloads --format '{{ .Mountpoint }}')"
If Docker itself is bloated (old images, dead build cache):
docker system prune -f # safe: removes stopped containers, dangling images
docker builder prune -f # safe: removes build cache
docker system prune --volumesIt deletes unused volumes — and it can take your database with it. Never add
--volumes on a host that runs UltraTorrent.
2. Is the engine quietly crashing?
docker inspect --format '{{.Name}} restarts={{.RestartCount}}' \
$(docker compose ps -q) 2>/dev/null
Write the number down. A RestartCount that climbs week over week is a
crash-loop, even if the container looks fine. For rTorrent, that is the
0.9.8 crash bug
and it is telling you that you have outgrown the engine.
3. Stuck jobs
Because job bodies run in-process, a restart orphans them. Boot-time reconciliation now fails those out automatically — but check that nothing is wedged while running:
docker compose exec postgres psql -U ultratorrent -d ultratorrent -c "
SELECT type, status, progress, \"createdAt\"
FROM media_processing_jobs
WHERE status IN ('queued','running')
ORDER BY \"createdAt\";"
A job that has been running at 0% for hours is orphaned. See
Jobs stuck running.
4. Failed downloads
Check the audit trail for download.failed. But know the false positive:
never registered within 6s false alarmOn older builds, magnets were marked failed if they did not register in ~6 seconds — while downloading perfectly. In one real case, 256 of 257 "failures" had actually loaded, at a median of ~53 seconds. If you see a flood of these, you need the fix, not an investigation.
5. Did the backup actually run?
ls -lh /mnt/backup/ultratorrent-*.dump | tail -5
Check the date and the size. A 0-byte dump is a failed backup that exited 0.
6. Prune dead torrents
Torrents with zero seeders never finish, and — critically — a 0-seeder magnet still occupies an active-download slot the whole time it tries. Left alone, they head-of-line block your entire queue. Remove them, or enable the parking queue.
Monthly (30 minutes)
1. Upgrade
See Upgrading for the full procedure. The short form:
# 1. BACK UP FIRST. Always.
docker compose exec -T postgres pg_dump -U ultratorrent -d ultratorrent -Fc \
> "pre-upgrade-$(date +%F).dump"
cp .env "pre-upgrade-env-$(date +%F)"
# 2. Pull and rebuild.
git pull
docker compose up -d --build
# 3. Watch the migration land. This is where P3009 appears if it is going to.
docker compose logs -f backend
# 4. PROVE the new code is running (not a cached image):
docker compose exec backend wget -qO- http://127.0.0.1:4000/api/system/version
That last step matters more than it looks. The version endpoint reports the git commit baked into the image. If the commit is unchanged after a rebuild, your image did not rebuild and you are still running the old code while believing otherwise.
| Upgrade risk | Watch for |
|---|---|
| A migration fails mid-flight | P3009 → resolve it |
| Long index builds | Now built at runtime in the background — the app stays up but is slow until they finish |
| In-flight jobs | Killed by the restart; reconciled (failed out) at boot. Re-run any scan/import that was running |
| New required env var | The backend refuses to boot. Read the log; check Environment |
2. Review the audit log
The audit trail records the actor, action, object, result, IP and user agent for every security-relevant and destructive action — and now names the media a row targeted rather than showing an opaque id.
Look for:
- Failed logins (
auth.loginwithresult: failure) — recorded with the attempted username. A cluster is meaningful. Note that a pending-2FA challenge is deliberately not counted as a failure, so what you see is real. torrents.delete_data— someone deleted data from disk.files.delete/files.cleanup— file-manager destruction.- Role changes — who granted what.
- Settings changes — especially
settings.update_root_path.
Access it under Audit in the UI (needs audit.view), or GET /api/audit.
See Audit.
3. Database housekeeping
# Refresh planner statistics — a stale plan is a slow plan.
docker compose exec postgres psql -U ultratorrent -d ultratorrent -c "ANALYZE;"
# Check for bloat / dead tuples
docker compose exec postgres psql -U ultratorrent -d ultratorrent -c "
SELECT relname, n_live_tup, n_dead_tup,
pg_size_pretty(pg_total_relation_size(relid)) AS size
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;"
Autovacuum normally handles the rest. If a table has a very high dead-tuple count and autovacuum is clearly not keeping up:
docker compose exec postgres psql -U ultratorrent -d ultratorrent \
-c "VACUUM (ANALYZE, VERBOSE) media_items;"
And check your trigram indexes are still valid — an interrupted rebuild leaves an INVALID index that the planner silently ignores forever:
SELECT c.relname, i.indisvalid
FROM pg_class c JOIN pg_index i ON i.indexrelid = c.oid
WHERE c.relname LIKE '%trgm%';
All must be true. See
Performance.
4. Run a restore drill
This is the item people skip, and it is the one that matters. A backup you have never restored is a rumour.
Follow the drill. The two checks that actually prove your backup is sound:
- 2FA works on a restored account → your
ENCRYPTION_KEYmatched the dump. - An indexer test passes → the encrypted API key decrypted.
If either fails, your .env and your dump are mismatched, and you have just
discovered — cheaply — that your backup was incomplete.
5. Review indexer health
- Does every indexer have a
minSeedersset? An indexer without one will hand you 0-seeder corpses that clog the queue. - Is any indexer failing every request? Per-indexer failures are isolated — a broken indexer does not fail the whole search, so it can rot unnoticed for months.
- If one returns HTTP 429 on everything, read the 429 entry before you waste an afternoon tuning rate limits.
Quarterly / annually
- Rotate the JWT secrets. Cheap — everyone is logged out, nothing else happens. See Rotating secrets.
- Review users and roles. Remove people who have left. Remember
POWER_USERincludes allfiles.*, including delete. - Re-check your exposure. Are engine/companion ports still unpublished? Run the security audit snippet.
- Re-evaluate your engine. If your torrent count has grown into the hundreds, you are on borrowed time with rTorrent.
ENCRYPTION_KEY as routine maintenanceIt is destructive — it invalidates every TOTP secret, indexer API key, Prowlarr key and engine password. Rotate it only in response to a leak, and follow the procedure.
Examples
A one-shot weekly health script
#!/usr/bin/env bash
# weekly-check.sh — run from the compose directory
set -euo pipefail
cd "$(dirname "$0")"
echo "=== Containers ==="
docker compose ps
echo -e "\n=== Restart counts (a climbing number = a crash loop) ==="
docker inspect --format '{{.Name}} restarts={{.RestartCount}}' $(docker compose ps -q)
echo -e "\n=== Disk ==="
df -h / | tail -1
echo -e "\n=== Errors, last 7 days ==="
docker compose logs --since 168h backend 2>/dev/null \
| grep -icE "error|fatal" || echo 0
echo -e "\n=== Stuck jobs ==="
docker compose exec -T postgres psql -U ultratorrent -d ultratorrent -tAc \
"SELECT count(*) FROM media_processing_jobs WHERE status IN ('queued','running');"
echo -e "\n=== Trigram indexes valid? (all must be t) ==="
docker compose exec -T postgres psql -U ultratorrent -d ultratorrent -tAc \
"SELECT c.relname || '=' || i.indisvalid FROM pg_class c
JOIN pg_index i ON i.indexrelid = c.oid WHERE c.relname LIKE '%trgm%';"
echo -e "\n=== Latest backup ==="
ls -lht /mnt/backup/ultratorrent-*.dump 2>/dev/null | head -1 || echo "NO BACKUP FOUND"

Troubleshooting
If the routine surfaces something, the fix is almost certainly in Troubleshooting. Fast links:
Tips
- Write down
RestartCounteach week. The absolute number is meaningless; the trend is the whole signal. - Upgrade regularly, in small steps. Most of the incidents in this documentation were fixed in a release. Staying current is the cheapest form of maintenance there is.
- Re-run interrupted scans after every upgrade. The restart killed them.
FAQ
How often should I upgrade? Monthly is a good cadence. Many of the failure modes documented across this site are fixed in later builds — running old code means keeping old bugs.
Do I need to run VACUUM manually?
Usually not — autovacuum handles it. Run ANALYZE after a large import so the
planner has fresh statistics.
Is it safe to restart the backend as routine maintenance? It is safe, but it interrupts in-flight jobs. They are reconciled (failed out) at boot, not resumed. Restart when nothing is scanning.
Why is my RestartCount high but nothing seems wrong?
Because restart: unless-stopped is hiding a crash loop from you. If it is
rTorrent, that is the 0.9.8 crash bug and it is load-driven — you have outgrown the
engine.
Should I prune Docker images?
Yes — docker system prune -f and docker builder prune -f are safe. Never
add --volumes.
Checklist
Daily
-
docker compose ps— all healthy - No new errors in the backend log
- Downloads are moving
Weekly
- Disk space is fine
-
RestartCountis not climbing - No jobs stuck
running - No unexplained failed downloads
- The backup actually ran (check date and size)
- Dead / 0-seeder torrents pruned
Monthly
- Upgraded (with a backup taken first)
-
/api/system/versionconfirms the new commit is running - Audit log reviewed (failed logins, deletions, role changes)
-
ANALYZErun; trigram indexes still valid - Restore drill run — 2FA and an indexer test both passed
- Every indexer has a
minSeeders
Quarterly
- JWT secrets rotated
- Users and roles reviewed
- Exposure re-checked (no published engine ports)
- Engine still appropriate for your torrent count
See also
- Upgrading
- Backup & Restore — especially the restore drill
- Troubleshooting
- Performance
- Security
- Configuration Profiles
- Audit · System