TPTito Pinardo Gutiérrez
← Back to the rack
Español
1U · LIVE TVDispatcharr · 52 channels · guide Testing

Slot 14 of 25 in the rack · Lab

1U unit · Media

Live TV

Configured through the API from Ansible

A live TV channel manager in its own container. It loads a public list of free-to-air channels with their programme guide and serves it three ways: to Plex, which sees it as an aerial tuner; to home apps, as a playlist; and, later on, to people outside with a user each.

Why outside Plex
In Plex, live TV is only for people who live at home, never the friends the library is shared with. A separate manager can give it to whoever I want.
Why its own container
Once it opens to the internet it mustn't share a container with the internal door or the library. And streaming stays isolated.
How it's configured
Nothing by hand: Ansible creates the admin, list, guide and channels through the API, and only creates what's missing.
  • 52channels picked in an explicit list
  • 3,078programmes in the guide
  • 0changes on the playbook's second pass
  • 2.5 %CPU at idle, no GPU

Inside live TV

A channel manager that Plex thinks is a tuner. Pick a flow.

Hermesvisiting the unit

Click a part or pick a flow. If you came from a skill, the parts where I use it are already lit.

      I watch a channel in Plex
      1. The TV — I pick a channel on the TV.
      2. Plex — Plex asks its «tuner» for it.
      3. Emulated tuner — The emulated tuner receives the request.
      4. Remuxing — The manager asks the source for the video.
      5. Public free-to-air list — The source sends it.
      6. Remuxing — It's repackaged, not converted.
      7. Plex — Plex receives it; if the TV needs it, it converts it on the GPU.
      8. The TV — And it plays, with the guide.
      I watch it in an app
      1. Home IPTV apps — A home IPTV app loads the playlist.
      2. Playlist for apps — The manager's playlist.
      3. Programme guide — With its matched guide.
      4. Playlist for apps — I pick a channel.
      5. Remuxing — It's repackaged.
      6. Home IPTV apps — And it plays in the app.
      Built from scratch
      1. Terraform — Terraform creates the container.
      2. Ansible role — Ansible installs the manager.
      3. Configuration through the API — And configures it through the API.
      4. Channel list — The list.
      5. Programme guide — The guide.
      6. Chosen channels — The 52 chosen channels.
      7. Daily refresh — The refresh every 24 hours.
      8. Two passes — Second pass: no changes. It's idempotent.
      The next phase: people outside
      1. People outside (pending) — A friend, away from my home.
      2. Internal door — They'll come in through a door of their own.
      3. Users and permissions — With their user and channels.
      4. Playlist for apps — They'll get their playlist.
      5. Remuxing — And the video, repackaged from home.
      1. Public free-to-air list

        Free-to-air Spanish channels, with IDs to match the guide.

      2. Plex

        Sees it as an aerial tuner and adds its live TV and recorder.

      3. Home IPTV apps

        On a phone or TV, with the playlist and guide.

      4. The TV

        Watching a channel through Plex.

      5. People outside (pending)

        The next phase: each with their own user and channels.

      6. Channel list

        The source list, loaded into the manager.

        • Docker
      7. Programme guide

        The source guide, matched channel by channel.

        • Docker
      8. Chosen channels

        An explicit list in the repository, instead of importing whole groups.

        • Ansible
      9. Daily refresh

        The list and guide are downloaded again every day.

      10. Emulated tuner

        The manager pretends to be a network tuner: Plex discovers it and uses it like an aerial.

      11. Playlist for apps

        A playlist with the guide included, for any IPTV app.

      12. Users and permissions

        Each user can have their own channel selection.

      13. Remuxing

        The manager doesn't convert video, it only repackages it. If conversion is needed, Plex does it on its GPU.

        • Docker
      14. Terraform

        Creates the unprivileged container.

        • Terraform
      15. Ansible role

        Installs the manager on Docker and configures it.

        • Ansible
      16. Configuration through the API

        Admin, list, guide, refresh and channels, through the manager's API. If something already exists, it's left alone.

        • Ansible
      17. Two passes

        The playbook runs twice: the second must change nothing.

        • Ansible
      18. Internal door

        The manager's web UI, only from home or the VPN, with two-factor and then its login.

        • Linux / SSH hardening
      19. Container firewall

        Admin only from my machines and the VPN; the service only from the home network and the VPN.

        • Proxmox VE
        • Linux / SSH hardening
      20. Monitoring

        Like the rest of the fleet.

        • Zabbix
      21. Security

        Like the rest of the fleet.

        • Wazuh

      What I use from my skill tree

      Pick a skill: the parts where I use it light up on the map and I tell you what for.

      Terraform
      Creates the container.
      Ansible
      Installs the manager and configures it through its API: admin, list, guide, refresh and channels, without overwriting what exists.
      Proxmox VE
      Container firewall with admin and the service scoped.
      LXC
      An unprivileged container with nesting for Docker.
      Docker
      The channel manager on Docker, with its database and queue inside.
      Zabbix
      Watches the container like the rest.
      Wazuh
      Watches the container like the rest.
      Linux / SSH hardening
      Web UI only behind the internal door with two-factor and ports scoped to home and the VPN.

      Incident log

      Real failures. Read the symptom, pick the cause you think it was and see how it was fixed.

      1. #1

        A list that never refreshed

        03-10-2026 · configuration

        The channel list created through the API loaded once and was never downloaded again.

        What do you think was going on?

        Show the fix
        Cause
        From the web UI, the daily refresh is set by default. Created through the API, the interval is zero: never.
        Fix
        Set 24 hours when creating it and correct it on the existing ones too.
        Lesson
        Automating through an API isn't the same as clicking: defaults can differ. Check the result, not just that it returned 200.
      2. #2

        A 400 with «'str' object has no attribute 'get'»

        03-10-2026 · Ansible

        When sending the channel group selection, the API answered 400 with a Python error.

        What do you think was going on?

        Show the fix
        Cause
        A loop inside an Ansible template returns a string that looks like a list. The API expected objects and got text.
        Fix
        Build the list only with filters, which return real structures.
        Lesson
        In Ansible, what looks like a list isn't always one. Filters keep the type; templates turn it into text.
      3. #3

        The firewall changed on every pass

        03-10-2026 · idempotence

        Every time the playbook ran, the container's firewall file came out as modified, even though nothing had changed.

        What do you think was going on?

        Show the fix
        Cause
        That file is written through a command's standard input, and Ansible appends a newline. The comparison never matched.
        Fix
        Tell the module not to add that newline. The second pass now reports zero changes.
        Lesson
        If the second pass changes anything, it isn't idempotent even if the result is the same. And the same pattern is probably in other roles: noted to review them.

      How it was built

      1. September 2026

        An earlier attempt

        • A first live TV stack that never worked and was retired during a migration.
      2. 03-10-2026

        Its own container, from code

        • An unprivileged container with Terraform and the manager configured through the API from Ansible.
        • Plex reaches the tuner and gets valid video.
        • An explicit list of 52 channels in the repository.

      Decisions (and why)

      • Which manager

        An active one, with users

        The previous one had stalled. This one merges sources, matches the guide, has per-channel permissions and speaks the three formats needed.

      • Convert on the server?

        No, just repackage

        With good upload, everyone can get the original quality. If a TV at home needs it converted, Plex already does that on its GPU.

      What I learned

      • Configuring through an API means checking the result: defaults aren't the web UI's.
      • Two playbook passes: the second tells you if it's really idempotent.

      What's next

      • Test it for real on a TV with Plex's recorder.
      • Open it to people outside, each with their own user, through a door of their own.

      Shall we talk?

      I can walk through any of these decisions in detail in an interview.

      Download CV Get in touch ← Back to the rack

      Only free-to-air Spanish channels. No addresses or internal names.