Maintainerr Setup Guide - ajgillis04/GillisDockerDepot GitHub Wiki
Maintainerr is an automated media cleanup tool that integrates with Plex, Sonarr, Radarr, and Overseerr. It uses rule-based logic to identify stale or unwatched content and supports dry-run auditing, tagging, and safe deletion workflows.
- Docker installed
- API keys for Plex, Sonarr, Radarr, and Overseerr (if applicable)
Ensure your container is configured with correct user permissions and settings:
services:
maintainerr:
container_name: maintainerr.${HOST_NAME}
hostname: maintainerr.${HOST_NAME}.local
image: ghcr.io/jorenn92/maintainerr:latest
environment:
PUID: ${PUID}
PGID: ${PGID}
TZ: ${TZ}
DOMAINNAME: ${DOMAINNAME}
HOST_NAME: ${HOST_NAME}
networks:
- mediaserver
ports:
- "${MAINTAINERR_PORT}:6246"
volumes:
- ${DOCKERDIR}/maintainerr/data:/opt/data
- ${DOCKERDIR}/logs/maintainerr:/var/log
restart: unless-stopped
security_opt:
- no-new-privileges:true
labels:
- "com.centurylinklabs.watchtower.enable=true"
- "homepage.group=Media"
- "homepage.name=Maintainerr"
- "homepage.icon=maintainerr.png"
- "homepage.href=https://maintainerr.${DOMAINNAME}/"
- "homepage.description=Automated media cleanup and maintenance"PUID and PGID to match a valid user/group on the host. Run id <your-user> to confirm, and chown -R the mounted volumes accordingly.
Use the following command to start Maintainerr:
docker compose -p mediaserver -f docker-compose-server1.yaml up --detach
- Open your browser and navigate to
http://<your-ip-address>:6246. - Log in with your Maintainerr credentials (if enabled).
- Navigate to the Settings tab to begin setup.
Maintainerr supports direct integration with your media stack. This step ensures all services are connected and authenticated.
- Go to the Settings β Plex tab.
- Enter your Plex server IP and port (default:
32400). - Paste your Plex token (found via
Settings β Account β Show Advanced). - Click Test Connection to verify.
- Click Save.

- Go to Settings β Radarr.
- Enter your Radarr IP, port, and API key.
- Click Test Connection.
- Click Save.

- Go to Settings β Sonarr.
- Enter your Sonarr IP, port, and API key.
- Click Test Connection.
- Click Save.

- Go to Settings β Overseerr.
- Enter your Overseerr IP, port, and API key.
- Click Test Connection.
- Click Save.

- Go to Settings β Tautulli.
- Enter your Tautulli IP, port, and API key.
- Click Test Connection.
- Click Save.

- Go to Settings β Notifications.
- Choose your preferred method (e.g., Discord, Telegram, Gotify).
- Enter the webhook or token.
- Customize message templates if needed.
- Click Test Notification.
- Click Save.

Go to the Rules on the left side menu
- Uses
Plex.viewCount,Radarr.dateAdded, andRadarr.tagsto identify stale or unwatched movies. - Supports fallback logic if Radarr metadata is missing.
- Example: delete if not tagged
save, never watched, and added >2 years ago.
- Uses
Sonarr.status,Plex.sw_allEpisodesSeenBy,Plex.sw_lastWatched, andOverseerr.isRequested. - Example: delete if request fulfilled, show ended, fully watched, and last watched >182 days ago.
You can add tags in Radarr / Sonarr to ensure media is protected or included.
- Use
list-importfor items imported from lists (I added list-import on each Import List). - Use
saveto protect items from deletion.

TV Rules
mediaType: SHOWS
rules:
- "0":
- firstValue: Overseerr.isRequested
action: EQUALS
customValue:
type: boolean
value: "true"
- operator: AND
firstValue: Sonarr.tags
action: NOT_CONTAINS_PARTIAL
customValue:
type: text
value: save
- "1":
- firstValue: Overseerr.isRequested
action: EQUALS
customValue:
type: boolean
value: "true"
- operator: AND
firstValue: Sonarr.status
action: EQUALS
customValue:
type: text
value: ended
- operator: AND
firstValue: Plex.sw_allEpisodesSeenBy
action: CONTAINS
lastValue: Overseerr.addUser
- operator: AND
firstValue: Plex.sw_lastWatched
action: BEFORE
customValue:
type: custom_days
value: "182"
- "2":
- firstValue: Overseerr.isRequested
action: EQUALS
customValue:
type: boolean
value: "true"
- operator: AND
firstValue: Plex.sw_viewedEpisodes
action: EQUALS
customValue:
type: number
value: 0
- operator: AND
firstValue: Plex.sw_lastEpisodeAddedAt
action: BEFORE
customValue:
type: custom_days
value: "182"
Movies Rules
mediaType: MOVIES
rules:
- "0":
- firstValue: Overseerr.isRequested
action: EQUALS
customValue:
type: boolean
value: "true"
- operator: AND
firstValue: Plex.seenBy
action: CONTAINS
lastValue: Overseerr.addUser
- operator: AND
firstValue: Plex.lastViewedAt
action: BEFORE
customValue:
type: custom_days
value: "182"
- operator: AND
firstValue: Radarr.tags
action: NOT_CONTAINS_PARTIAL
customValue:
type: text
value: save
- "1":
- operator: OR
firstValue: Overseerr.isRequested
action: EQUALS
customValue:
type: boolean
value: "true"
- operator: AND
firstValue: Plex.viewCount
action: EQUALS
customValue:
type: number
value: 0
- operator: AND
firstValue: Plex.addDate
action: BEFORE
customValue:
type: custom_days
value: "730"
- operator: AND
firstValue: Radarr.tags
action: NOT_CONTAINS_PARTIAL
customValue:
type: text
value: save
- "2":
- operator: OR
firstValue: Radarr.tags
action: CONTAINS
customValue:
type: text
value: list-import
- operator: AND
firstValue: Plex.viewCount
action: EQUALS
customValue:
type: number
value: 0
- operator: AND
firstValue: Radarr.addDate
action: BEFORE
customValue:
type: custom_days
value: "730"
- operator: OR
firstValue: Plex.addDate
action: BEFORE
customValue:
type: custom_days
value: "730"
- operator: AND
firstValue: Radarr.tags
action: NOT_CONTAINS_PARTIAL
customValue:
type: text
value: save
- "3":
- operator: OR
firstValue: Radarr.tags
action: CONTAINS
customValue:
type: text
value: list-import
- operator: AND
firstValue: Plex.lastViewedAt
action: BEFORE
customValue:
type: custom_days
value: "730"
- operator: AND
firstValue: Radarr.tags
action: NOT_CONTAINS_PARTIAL
customValue:
type: text
value: save
Use cron expressions to control when Maintainerr runs:
| Job Type | Cron Expression | Description |
|---|---|---|
| Rule Handler | 0 3 * * * |
Identifies media for deletion |
| Collection Handler | 0 4 * * * |
Deletes stale/unwatched movies |
π All jobs run overnight to avoid peak usage hours.
Regular Backups:
- Backup
/opt/dataand/var/logvolumes weekly. - Include
maintainerr.dbandconfig.yamlif present.
Restoration Process:
- Start a new container with the same volume mounts.
- Copy backup files into the new containerβs data directory:
&&&
sudo cp /share/PathToOldContainer/maintainerr/data/config.yaml /share/PathToNewContainer/maintainerr/data/config.yaml
sudo cp /share/PathToOldContainer/maintainerr/data/maintainerr.db /share/PathToNewContainer/maintainerr/data/maintainerr.db &&& - Restart the container and verify rule integrity.
- Dry-run first: Always test rules before enabling deletion.
-
Logs: Check
/var/logfor rule matches and errors. - Permissions: Ensure UID/GID match host user or container will crash-loop.
- Support: Maintainerr GitHub