iptv-proxy is a free, open source media & streaming project written in Go and released under GPL-3.0. It has 718 GitHub stars, 204 forks and 17 open issues, and was last pushed 8 hours ago. On this registry it ranks #29 of 35 tracked projects in Media & Streaming, with 5 head-to-head comparisons available.

What is iptv-proxy?

iptv-proxy is a Go reverse proxy that republishes an IPTV provider's M3U/M3U8 playlist or Xtream Codes account from your own address, behind a username and password you choose, for anyone who wants their players to talk to them instead of to the provider.

What it is

iptv-proxy sits between your IPTV players and your provider. You hand it either an M3U/M3U8 playlist (a URL or a local file) or an Xtream Codes account, and it serves the same channels, movies and series from your own hostname with credentials you define through PROXY_USER and PROXY_PASSWORD. Players such as TiviMate, IPTV Smarters, VLC, and Kodi — or Plex and Jellyfin through an M3U playlist — connect only to the proxy and never see the provider's address or credentials. The project is written in Go, licensed under GPL-3.0, and ships as one binary or one container.

The concrete problem it solves is credential exposure and address instability: sharing IPTV at home normally means handing out the provider's login to every device and family member, and losing access whenever the provider changes its URL. By fronting the provider, iptv-proxy lets you share streams without disclosing the upstream login, put the provider behind your own domain, HTTPS or VPN, and give every device a single address that does not change.

Key capabilities

  • Serves an M3U/M3U8 playlist supplied either as a remote address (M3U_URL) or as a file mounted read-only into the container (for example -v "$PWD/iptv.m3u:/iptv.m3u:ro").
  • Proxies a full Xtream Codes account through XTREAM_BASE_URL, XTREAM_USER and XTREAM_PASSWORD, republished under your own PROXY_HOSTNAME, PROXY_USER and PROXY_PASSWORD.
  • Exposes the standard Xtream-style endpoints so both login styles work: http://…:8080/iptv.m3u?username=…&password=…, /get.php?…&type=m3u_plus&output=ts, and /xmltv.php for the electronic guide.
  • Provides HLS support alongside direct stream proxying.
  • Runs as a single binary or a single container configured entirely from flags or environment variables, with GIN_MODE=release for production runs.
  • Publishes multi-architecture Docker images as pierro777/iptv-proxy on Docker Hub and ghcr.io/pierre-emmanuelj/iptv-proxy on GitHub, with latest plus versioned tags (v3.12.0, v3.12, v3) working on amd64 and ARM (Raspberry Pi, Apple silicon) since v3.12.0.
  • Requires no database and offers no web interface to configure, keeping the deployment to a container and its environment block.

Who uses it and how

  • A household shares one paid IPTV subscription across the family by handing out PROXY_USER/PROXY_PASSWORD while the provider's credentials stay inside the container.
  • An operator places the provider behind their own domain, HTTPS or VPN so traffic and logins never leave their infrastructure in raw form.
  • A user with many devices — TiviMate, IPTV Smarters, VLC, Kodi, Plex or Jellyfin — points them all at one stable address on the LAN rather than at a provider that may change URL.
  • A player that only accepts a playlist is fed /get.php with type=m3u_plus&output=ts, while the guide is served from /xmltv.php.
  • A self-hoster deploys via the repository's docker-compose.yml with restart: unless-stopped so the proxy comes back after a reboot.

Getting started

Run docker run -d -p 8080:8080 with pierro777/iptv-proxy:latest and the M3U_URL (or XTREAM_BASE_URL, XTREAM_USER, XTREAM_PASSWORD), PROXY_HOSTNAME, PROXY_USER and PROXY_PASSWORD environment variables, or bring the repository's docker-compose.yml up with docker compose up -d.

How it compares

No comparable proxy projects are named alongside it in the available facts, so it stands alone in this registry for this role. Its closest neighbours in the documentation are the players it fronts — TiviMate, IPTV Smarters, VLC, Kodi, Plex and Jellyfin — which remain the clients rather than alternatives.

When to use it — and when not

It suits someone comfortable operating a container and managing configuration through environment variables only, since there is no database, no web interface and no web-based settings screen to fall back on; you must also ensure players can reach PROXY_HOSTNAME on the exposed port. Do not pick it if you need multi-user account management or a graphical admin panel, and note that 17 issues are currently open on the repository, so edge cases may still be rough.

project readme (upstream, from github) — read inline

iptv-proxy

CI Release Docker pulls

iptv-proxy is a reverse proxy for IPTV: give it an M3U/M3U8 playlist or an Xtream Codes account, and it serves the same channels, movies and series from your own address, with a user and a password you choose.

Your players (TiviMate, IPTV Smarters, VLC, Kodi, Plex or Jellyfin through an M3U playlist...) talk to the proxy and never see the provider's address or credentials. Use it to share IPTV at home without handing out the provider's login, to put a provider behind your own domain, HTTPS or VPN, or to give every device one address that does not change.

It is one binary or one container, set up with a few flags or environment variables. There is no database and nothing to configure in a web interface.

Quick start

In the examples, 192.168.1.10 is the machine running the proxy, as your players reach it, and family / choose-a-password are the credentials you give to your players. Replace them with your own.

Docker, with an M3U playlist

docker run -d --name iptv-proxy -p 8080:8080 \
  -e M3U_URL="http://provider.example:8080/playlist.m3u" \
  -e PROXY_HOSTNAME=192.168.1.10 \
  -e PROXY_USER=family \
  -e PROXY_PASSWORD=choose-a-password \
  -e GIN_MODE=release \
  pierro777/iptv-proxy:latest

Give this address to your player:

http://192.168.1.10:8080/iptv.m3u?username=family&password=choose-a-password

To check it from a terminal:

curl "http://192.168.1.10:8080/iptv.m3u?username=family&password=choose-a-password"

The playlist can also be a local file: mount it and give its path.

docker run -d --name iptv-proxy -p 8080:8080 \
  -v "$PWD/iptv.m3u:/iptv.m3u:ro" \
  -e M3U_URL=/iptv.m3u \
  -e PROXY_HOSTNAME=192.168.1.10 \
  -e PROXY_USER=family \
  -e PROXY_PASSWORD=choose-a-password \
  -e GIN_MODE=release \
  pierro777/iptv-proxy:latest

Docker, with an Xtream Codes account

docker run -d --name iptv-proxy -p 8080:8080 \
  -e XTREAM_BASE_URL="http://provider.example:8080" \
  -e XTREAM_USER=xtream_user \
  -e XTREAM_PASSWORD=xtream_password \
  -e PROXY_HOSTNAME=192.168.1.10 \
  -e PROXY_USER=family \
  -e PROXY_PASSWORD=choose-a-password \
  -e GIN_MODE=release \
  pierro777/iptv-proxy:latest

In your player, choose the Xtream Codes login and enter:

Server:   http://192.168.1.10:8080
Username: family
Password: choose-a-password

A player that only takes a playlist can use the usual Xtream addresses:

Playlist: http://192.168.1.10:8080/get.php?username=family&password=choose-a-password&type=m3u_plus&output=ts
Guide:    http://192.168.1.10:8080/xmltv.php?username=family&password=choose-a-password

Docker images

Registry Image
Docker Hub pierro777/iptv-proxy
GitHub ghcr.io/pierre-emmanuelj/iptv-proxy

Tags: latest, and for a given version v3.12.0, v3.12 or v3. Since v3.12.0 they work on amd64 and on ARM (Raspberry Pi, Apple silicon): Docker pulls the image of your machine. The -amd64 and -arm64 tags (pierro777/iptv-proxy:latest-arm64) name one architecture, as before.

Docker Compose

services:
  iptv-proxy:
    image: pierro777/iptv-proxy:latest
    restart: unless-stopped
    ports:
      # the same port as PORT below
      - 8080:8080
    environment:
      # Either an M3U playlist (an address, or a file mounted in the container)...
      M3U_URL: "http://provider.example:8080/playlist.m3u"
      # ...or an Xtream Codes account (remove M3U_URL then)
      # XTREAM_BASE_URL: "http://provider.example:8080"
      # XTREAM_USER: xtream_user
      # XTREAM_PASSWORD: xtream_password
      PORT: 8080
      # Name or IP your players reach this machine at
      PROXY_HOSTNAME: 192.168.1.10
      # What your players log in with
      PROXY_USER: family
      PROXY_PASSWORD: choose-a-password
      GIN_MODE: release
docker compose up -d

The docker-compose.yml of this repository does the same, building the image from the sources.

Binary

Download the archive for your system (Linux, macOS, Windows; amd64 and arm64; also .deb and .rpm) from the releases, or build it with Go 1.27 or later:

git clone https://github.com/pierre-emmanuelJ/iptv-proxy.git
cd iptv-proxy
go build -o iptv-proxy .
GIN_MODE=release ./iptv-proxy \
  --m3u-url "http://provider.example:8080/playlist.m3u" \
  --hostname 192.168.1.10 \
  --user family \
  --password choose-a-password

Quote the playlist address: it usually contains ? and &.

Options

Every option is a flag, an environment variable (the flag's name in upper case, with _ instead of -) or a line of the configuration file. A flag wins over the variable, and the variable over the file.

Flag Environment variable Default What it does
--m3u-url, -u M3U_URL Provider playlist: an http(s) address or a local file.
--xtream-base-url XTREAM_BASE_URL Provider's Xtream Codes address, e.g. http://provider.example:8080.
--xtream-user XTREAM_USER Provider's Xtream Codes user.
--xtream-password XTREAM_PASSWORD Provider's Xtream Codes password.
--hostname PROXY_HOSTNAME (or HOSTNAME) Name or IP your players reach the proxy at. It is written in every address the proxy gives out.
--port PORT 8080 Port the proxy listens on.
--listen-address LISTEN_ADDRESS every interface IP address the proxy listens on, e.g. 127.0.0.1 behind a reverse proxy on the same machine.
--advertised-port ADVERTISED_PORT value of --port Port written in the addresses the proxy gives out, when it differs from the listening port (behind a reverse proxy, or a different published Docker port).
--https HTTPS false Write https:// instead of http:// in the addresses the proxy gives out. The proxy itself always listens in plain HTTP: put a reverse proxy in front for TLS.
--user PROXY_USER (or USER) usertest User your players log in with.
--password PROXY_PASSWORD (or PASSWORD) passwordtest Password your players log in with.
--m3u-file-name M3U_FILE_NAME iptv.m3u Name of the playlist the proxy serves: http://host:port/iptv.m3u.
--custom-endpoint CUSTOM_ENDPOINT Prefix put before every path: http://host:port//iptv.m3u.
--custom-id CUSTOM_ID random First path element of M3U track addresses. Random at each start by default; set it to keep the same track addresses across restarts.
--m3u-cache-expiration M3U_CACHE_EXPIRATION 1 Hours a playlist fetched from an Xtream provider is kept before asking for it again.
--xtream-api-get XTREAM_API_GET false Build the get.php playlist (live channels) from the provider's API, for providers that disabled get.php.
--xtream-api-get-movies XTREAM_API_GET_MOVIES false Add the provider's movies to that generated playlist. Off by default: a catalogue of tens of thousands of movies makes a playlist some players cannot load. Series are not added (see below).
--user-agent USER_AGENT User-Agent sent to the provider instead of the player's. Some providers only answer known players.
--no-stream-sharing NO_STREAM_SHARING false Open one provider connection per player for a live stream, instead of sharing one between the players watching it.
--group-regex GROUP_REGEX Keep only the live channels whose group matches this regular expression. See Filtering channels.
--channel-regex CHANNEL_REGEX Keep only the live channels whose name matches this regular expression.
--group-exclude-regex GROUP_EXCLUDE_REGEX Leave out the live channels whose group matches this regular expression.
--channel-exclude-regex CHANNEL_EXCLUDE_REGEX Leave out the live channels whose name matches this regular expression.
--iptv-proxy-config .iptv-proxy.yaml in the home or current directory YAML file holding the same options, named as the flags (m3u-url: ...).

Good to know:

  • Change --user and --password: the defaults are public.
  • USER and HOSTNAME are also ordinary system variables: a shell sets USER to your login name, and Docker sets HOSTNAME to the container's ID. Since v3.11.0, prefer PROXY_USER, PROXY_PASSWORD and PROXY_HOSTNAME: they win over the old names, which keep working. Since v3.12.0, user, password and hostname in the configuration file also win over the old names.
  • GIN_MODE is not an option of the proxy but of its web framework. Since v3.10.0 the proxy runs in release mode unless you set it. Before that, set GIN_MODE=release as the examples do: the debug mode lists the routes at startup, and they contain your user and password. The access log masks them in both modes.

M3U playlists

The proxy reads the provider's playlist once, at startup, and serves it at /iptv.m3u with every track address replaced by its own. All other lines (names, logos, groups, guide IDs, player options) are kept as the provider wrote them. Restart the proxy to pick up a new version of the playlist.

The provider's playlist:

#EXTM3U
#EXTINF:-1 tvg-id="news.example" tvg-name="News" tvg-logo="http://provider.example/logos/news.png" group-title="News",News HD
http://provider.example:8080/live/news/1.ts
#EXTINF:-1 tvg-id="sport.example" tvg-name="Sport" tvg-logo="http://provider.example/logos/sport.png" group-title="Sport",Sport HD
http://provider.example:8080/live/sport/2.m3u8?token=abc

What your players get:

#EXTM3U
#EXTINF:-1 tvg-id="news.example" tvg-name="News" tvg-logo="http://provider.example/logos/news.png" group-title="News",News HD
http://192.168.1.10:8080/e3c0c308/family/choose-a-password/0/1.ts
#EXTINF:-1 tvg-id="sport.example" tvg-name="Sport" tvg-logo="http://provider.example/logos/sport.png" group-title="Sport",Sport HD
http://192.168.1.10:8080/e3c0c308/family/choose-a-password/1/2.m3u8

e3c0c308 is the --custom-id. Tokens and other query parameters of the provider's addresses stay on the proxy.

Some playlists name the provider's credentials outside of the track addresses too: a guide address in url-tvg, a catch-up address in catchup-source, a player option. Such an attribute or line is removed, so that the credentials never reach a player. (In an Xtream Codes playlist, they point to the proxy instead: see below.)

Xtream Codes

The proxy answers the Xtream Codes client API like the provider does: live, movies, series, catch-up and the guide. Your players log in with the proxy's address and credentials, the proxy asks the provider with the real ones.

Provider Proxy
Server http://provider.example:8080 http://192.168.1.10:8080
User xtream_user family
Password xtream_password choose-a-password

The provider's answers are passed on as they are, so the fields and quirks of any provider reach the player. Only what names the provider is rewritten: the account and server of the login answer, and the addresses holding its credentials. In the get.php playlist, the guide (url-tvg) and catch-up (catchup-source) addresses of the provider become the proxy's, and work through it.

Endpoints served:

Endpoint What it is
/player_api.php The client API (login, categories, streams, movie and series details, short guide).
/get.php The M3U playlist, with the same parameters as the provider's (type, output).
/xmltv.php The full guide (XMLTV), streamed from the provider.
/apiget A playlist of the live channels built from the API.
///, /live/..., /movie/..., /series/..., /timeshift/... The streams.

/player_api.php, /get.php, /xmltv.php and /apiget take username=...&password=....

If you give --m3u-url the provider's get.php address (http://provider.example:8080/get.php?username=xtream_user&password=xtream_password&type=m3u_plus&output=ts), the Xtream options are read from it: everything above is served, and that playlist is also available at /iptv.m3u.

With the Xtream options alone, /iptv.m3u is the account's playlist too (the same as /get.php).

The playlist built from the API (/apiget, or /get.php with --xtream-api-get) holds the live channels, and the movies with --xtream-api-get-movies. Series are not in it: the API gives the episodes of one series at a time, so listing them all would take one request per series. Players that speak the Xtream API get movies and series from it directly.

HLS

HLS streams (.m3u8) work in both modes, whatever the provider. Every address an HLS playlist names (variants, segments, keys, audio tracks) is replaced by an address of the proxy, /hls//, so the whole stream goes through the proxy, on any host and after any redirect. The token is the provider's address, encrypted: a player never sees the provider's host, credentials or session tokens. Nothing is stored, and tokens stay valid across restarts.

Filtering channels

Four options keep the live channels you want and leave out the others. Each takes a regular expression matched against a channel's group (group-title in a playlist, the category in the Xtream API) or its name:

  • --group-regex, --channel-regex: keep only what matches.
  • --group-exclude-regex, --channel-exclude-regex: leave out what matches, even if it was kept by the first two.

A channel is kept when it passes all of them. The expressions are case sensitive; start one with (?i) to ignore case.

  -e GROUP_REGEX='^(FR|UK) ' \
  -e GROUP_EXCLUDE_REGEX='(?i)adult|xxx' \
  -e CHANNEL_EXCLUDE_REGEX='(?i)backup|test' \

The filters apply to:

  • the M3U playlist (/iptv.m3u) and the Xtream playlists (/get.php, /apiget): a channel left out is not in it, and in M3U mode it has no address on the proxy;
  • the live categories and channels of the Xtream API;
  • the guide (/xmltv.php): only the channels kept, and their programmes, are in it. A guide of thousands of channels shrinks to the ones you watch.

Movies and series are not filtered. In a get.php playlist that holds movies, the same rules apply to them, by their group and name.

The filters choose what players are shown; they are not access control. A player that already knows the id of a channel left out can still play it through the Xtream API.

Live streams: one connection to the provider

An IPTV account allows a few connections at a time, often a single one. When several players watch the same live channel, the proxy opens one connection to the provider and shares it: the first player opens the stream, the next ones join it where it is, and the connection is closed when the last one leaves.

If the provider drops the stream, or leaves it silent for 20 seconds, the proxy opens it again while someone is watching, so the player does not have to reconnect. After a few attempts that fail, the stream ends.

Movies, series and catch-up are files: each player reads its own, from where it wants. --no-stream-sharing gives every player its own connection for live streams too.

Behind a reverse proxy, with HTTPS

The proxy listens in plain HTTP. To serve it on your own domain with HTTPS, put a reverse proxy (Traefik, Caddy, nginx...) in front, and tell iptv-proxy which addresses to give out:

    environment:
      PORT: 8080             # where iptv-proxy listens
      PROXY_HOSTNAME: iptv.example.com
      ADVERTISED_PORT: 443   # the port your players use
      HTTPS: 1               # addresses given out start with https://

Your players then use https://iptv.example.com:443/iptv.m3u?username=...&password=....

The proxy's user and password travel in every address, so use HTTPS as soon as the proxy is reachable from the Internet.

A complete example with Traefik and Let's Encrypt is in the traefik folder. From the root of the repository:

cp -r ./traefik/* .
mkdir -p Traefik/etc/traefik Traefik/log

Then replace iptv.proxyexample.xyz with your domain in docker-compose.yml, set your e-mail address in Traefik/traefik.yaml, and start it:

docker compose up -d

What it does not do

  • It does not provide channels or streams: you need a playlist or an account from a provider.
  • It does not edit the catalogue: no channel editor, no renaming, no guide mapping. Players get what the provider sends.
  • It does not transcode, record or cache streams: they are passed on as they come.
  • It has one provider and one user and password, shared by all your players.
  • It has no web interface.

Roadmap

Planned, not available yet:

  • Keeping the last good playlist, lists and guide when the provider fails, and reloading an M3U playlist without a restart.
  • HDHomeRun emulation, for Plex.
  • Several users, each with their own credentials and limits.
  • Several providers behind one proxy, with failover.

See the changelog for what each release brought.

License

GPL-3.0.

Built with cobra and gin.

If the project is useful to you, you can buy me a beer:

paypal

Frequently asked questions

Is iptv-proxy free to use?

iptv-proxy is open source under the GPL-3.0 licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does iptv-proxy do?

IPTV reverse proxy for M3U/M3U8 playlists and Xtream Codes accounts, with HLS. One binary or Docker container.

What is iptv-proxy written in?

iptv-proxy is primarily written in Go. Its source is publicly available at https://github.com/pierre-emmanuelJ/iptv-proxy, and it has 718 GitHub stars.