FigJam Diagram: Media Controller — Automated Media Lifecycle (expires 2026-04-13)
Automated controller that manages media library lifecycle within the media namespace. It orchestrates Jellyfin, Jellyseerr, Radarr, and Sonarr to maintain two library tiers: auto-managed (time-limited) and permanent (kept indefinitely).
| Public URL | https://media-controller.k3s.strommen.systems |
| Namespace | media |
| Image | harbor.k3s.internal.strommen.systems/production/media-controller:sha-0bbd4a9 |
| Port | 8080 |
| UID/GID | 10017 / 10000 (svc-media-ctrl / media-services) |
| Default mode | DRY_RUN=true (no destructive actions without explicit override) |
Auth: Protected by Authentik
authentik-forward-authmiddleware (added 2026-04-05). The IngressRoute inkubernetes/apps/media/media-controller.yamlnow includes the middleware referencing thepublic-ingressnamespace. Previously unauthenticated — see git history.
| Tier | NFS Path | Purpose |
|---|---|---|
auto-managed/movies |
/media/auto-managed/movies |
Movies with time-limited retention |
auto-managed/tv |
/media/auto-managed/tv |
TV shows with time-limited retention |
permanent/movies |
/media/permanent/movies |
Manually curated, never auto-deleted |
permanent/tv |
/media/permanent/tv |
Manually curated, never auto-deleted |
The controller discovers content in auto-managed/, evaluates retention rules, and either promotes items to permanent/ or schedules deletion.
| Env Var | Value | Purpose |
|---|---|---|
DRY_RUN |
"true" |
Safety — no destructive actions until explicitly set to false |
MAX_REQUESTS_PER_RUN |
5 |
Limits Jellyseerr requests processed per cycle |
AUTO_MANAGED_ROOT |
/media/auto-managed |
Base path for auto-managed tier |
PERMANENT_ROOT |
/media/permanent |
Base path for permanent tier |
LOG_LEVEL |
INFO |
Logging verbosity |
These environment variables are injected into the container and configure how the controller reaches integrated services within the cluster:
| Env Var | Value | Purpose |
|---|---|---|
JELLYFIN_URL |
http://jellyfin.media.svc.cluster.local:8096 |
Jellyfin API |
JELLYSEERR_URL |
http://jellyseerr.media.svc.cluster.local:5055 |
Request management |
RADARR_URL |
http://radarr.media.svc.cluster.local:7878 |
Movie management |
SONARR_URL |
http://sonarr.media.svc.cluster.local:8989 |
TV management |
DATABASE_URL |
postgresql+asyncpg://$(DB_USER):$(DB_PASS)@media-controller-postgres.media.svc.cluster.local:5432/media_controller |
Async DB connection |
| Secret | Keys | Purpose |
|---|---|---|
media-controller-secrets |
jellyfin-api-key, jellyseerr-api-key, radarr-api-key, sonarr-api-key, jellyfin-user-id, slack-webhook-url (optional) |
API keys for integrated services |
media-controller-postgres-credentials |
username, password |
PostgreSQL access |
Container env vars: DB credentials are mounted as
DB_USERandDB_PASS(from keysusernameandpasswordin the secret).
Bootstrap:
kubectl create secret generic media-controller-secrets -n media \
--from-literal=jellyfin-api-key=<key> \
--from-literal=jellyseerr-api-key=<key> \
--from-literal=radarr-api-key=<key> \
--from-literal=sonarr-api-key=<key> \
--from-literal=jellyfin-user-id=<user-guid> \
--from-literal=slack-webhook-url=<url> # optional — manifest has optional: true
kubectl create secret generic media-controller-postgres-credentials -n media \
--from-literal=username=media_controller \
--from-literal=password=$(openssl rand -base64 24)
PostgreSQL backup CronJob added 2026-04-05.
| Schedule | Daily 4:00 AM UTC |
| Method | pg_dump → gzip → S3 k3s-homelab-backups-855878721457/postgres-backups/media-controller/ |
| Retention | 7 days |
| Manifest | kubernetes/apps/media/media-controller-postgres-backup.yaml |
Bootstrap AWS credentials:
kubectl create secret generic postgres-backup-aws-credentials \
--namespace media \
--from-literal=AWS_ACCESS_KEY_ID=<from-terraform-output> \
--from-literal=AWS_SECRET_ACCESS_KEY=<from-terraform-output>
Prometheus metrics at /metrics (port 8080). ServiceMonitor label: release: kube-prometheus-stack. Also scraped via pod annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
kubernetes/apps/media/
media-controller.yaml -- PostgreSQL StatefulSet + PVC,
Deployment, Service, IngressRoute
(with authentik-forward-auth middleware),
Certificate, ServiceMonitor
media-controller-postgres-backup.yaml -- Daily PostgreSQL backup CronJob to S3