···11+---
22+title: Plex Remote Access with Docker Compose
33+description: >-
44+ Configuring Plex in Docker to work with Remote Access
55+author: FoxxMD
66+date: 2024-09-05 12:00:00 -0400
77+categories: [Tutorial]
88+tags: [plex, docker, iplan, networking]
99+pin: false
1010+---
1111+1212+**So, you are running Plex in Docker and thought [Remote Access](https://support.plex.tv/articles/200289506-remote-access/) would be easy.**
1313+1414+{: width="650" height="325" }
1515+1616+Whether it works for you or not seems to be like rolling dice. Here's what I learned, and the hoops you'll need to jump through, to _maybe_ get Remote Access working when running Plex with docker (compose).
1717+1818+## What Plex Wants
1919+2020+### Forwarded Port
2121+2222+It needs ingress into your network so it can reach your hosted Plex server. This is the easiest part. As long as you aren't behind a NAT/CGNAT this is as simple as forwarding a port on your router to the machine where Plex is hosted. The plot thickens below though...
2323+2424+### Unrestricted Port Access
2525+2626+You'll find countless solutions on Reddit and in google searches recommending that Plex **not** be in a container using [bridge networking](https://docs.docker.com/engine/network/drivers/bridge/) because it needs access to many ports. While there are known ports that can be mapped it seems Plex chooses to use random ports for reachability as well. From `Plex Media Server.log`:
2727+2828+```
2929+Sep 04, 2024 22:21:43.080 [140124105521976] INFO - [Req#63] [PlexRelay] Allocated port 26243 for remote forward to 127.0.0.1:32401
3030+Sep 04, 2024 22:21:43.161 [140124105521976] INFO - [Req#79] [PlexRelay] Allocated port 18837 for remote forward to 127.0.0.1:32401
3131+Sep 04, 2024 22:21:43.553 [140124105521976] WARN - [Req#ab] MyPlex: attempted a reachability check but we're not yet mapped.
3232+Sep 04, 2024 22:21:43.621 [140124103150392] WARN - [Req#ba] MyPlex: attempted a reachability check but we're not yet mapped.
3333+```
3434+3535+### Private IP on First Interface
3636+3737+Most importantly (and most annoyingly) **Plex listens on the first listed network interface in the container.** You can determine what this address by checking the `Private IP` on the Remote Access settings page. Alternatively, use `ifconfig` in the container (installed with `apt install net-tools`) to see all interfaces available in the container.
3838+3939+If this interface is not the one that has forwarded traffic from your router then Plex will consider your server as unreachable and fallback to Relay (if you have it enabled). This is especially infuriating since it could just listen on `0.0.0.0`, to all interfaces, to get that traffic and establish a direction connection.
4040+4141+## What You Need
4242+4343+#### Host Networking?
4444+4545+If you're familiar with docker/compose your first thought might be "Why can't I just use [host networking](https://docs.docker.com/engine/network/drivers/host/)?" and honestly this might work for you. You'll fix port access and don't have to worry about bridge networking at all. Unfortunately, this is also dependent on the [correct interface being first on your host.](#private-ip-on-first-interface) If it isn't first there's nothing you can do about it as host networking mode gives up all networking control for the container. Short of re-naming/ordering the interfaces on your host machine this is not a situation that can be fixed.
4646+4747+If you try host networking and it works for you, then more power to you. You can stop reading now and should be good to go.
4848+4949+If your interfaces are not the correct order or you just want more reproducibility for the future, read on...
5050+5151+### IPvlan Network
5252+5353+The solution to unfettered port access and one interface, in isolation, is [IPvlan networking.](https://docs.docker.com/engine/network/drivers/ipvlan/) This allows us to give our Plex container its own IP address on the LAN without any port restrictions. It also means that, by itself, there is only one interface attached to the container so Plex will always use the correct Private IP.
5454+5555+> Use caution when setting up ipvlan and assigning an IP address to your container. Since you are manually setting these there is the potential for address collision which will cause serious issues for your network. Ideally you should use a subnet not in use by the rest of your LAN or reserve the IP address on your router so it can't be assigned anywhere else.
5656+{: .prompt-warning }
5757+5858+Find the parent interface your ipvlan will communicate on by using `ifconfig` or `ip a show`. Look for the interface with the IP address of the host machine on your LAN, EX `inet 192.168.0.150/24`, and then use the interface name given for that entry, IE `eth0`.
5959+6060+```yaml
6161+services:
6262+ plex:
6363+ container_name: plex
6464+ image: linuxserver/plex:latest
6565+ # ...
6666+ networks:
6767+ plexnet:
6868+ # An address you manually assign here
6969+ ipv4_address: 192.168.0.233
7070+# ...
7171+networks:
7272+ plexnet:
7373+ driver: ipvlan
7474+ driver_opts:
7575+ # the interface we found above
7676+ parent: eth0
7777+ ipvlan_mode: l2
7878+ ipam:
7979+ config:
8080+ # same subnet and gateway as the interface we found above
8181+ - subnet: 192.168.0.0/24
8282+ gateway: 192.168.0.1
8383+```
8484+8585+Recreate your stack to get the network to be created (and any time you edit it):
8686+8787+```bash
8888+docker compose down
8989+docker compose up
9090+```
9191+9292+Inspecting the container `docker container inspect plex` should result in a Networking setting that looks similar to this:
9393+9494+```json
9595+{
9696+ "Networks": {
9797+ "plexnet": {
9898+ "IPAMConfig": {
9999+ "IPv4Address": "192.168.0.233"
100100+ },
101101+ "Links": null,
102102+ "Aliases": [
103103+ "plex",
104104+ "plex"
105105+ ],
106106+ "MacAddress": "",
107107+ "DriverOpts": null,
108108+ "NetworkID": "3b63c4cb58d336307feba0798a5209f0d2c1547ccf292103127412697e173c57",
109109+ "EndpointID": "23af84f3e64d04ec7396dcff121412119ae982eb94769f7e44c742e77823a0e6",
110110+ "Gateway": "192.168.0.1",
111111+ "IPAddress": "192.168.0.233",
112112+ "IPPrefixLen": 24,
113113+ "IPv6Gateway": "",
114114+ "GlobalIPv6Address": "",
115115+ "GlobalIPv6PrefixLen": 0,
116116+ "DNSNames": [
117117+ "plex",
118118+ "79fa89e12c61"
119119+ ]
120120+ },
121121+122122+ }
123123+}
124124+```
125125+126126+At this point you only need to [change the forwarded IP address](#forwarded-port) in your router to newly assigned IP and you should be good to go.
127127+128128+Now...the downside of using IPvlan is that **the new interface is isolated from all other docker networks.** Even though from your host machine you can access Plex at `192.168.0.233` you will find that this is not the case from any other container. This is problematic if you use any other services with Plex such as [Tautulli](https://tautulli.com/) or [Overseer](https://overseerr.dev/).
129129+130130+To fix this we need to re-introduce bridge networking but with a small quirk...
131131+132132+### Bridge Network with Priority
133133+134134+As mentioned in the previous section, you only need to complete this step if you have other containers/services in your compose stack that need access to Plex.
135135+136136+The issue with this is that we still have to deal with that [pesky first interface problem Plex has.](#private-ip-on-first-interface) Docker does not have an explicit way to name interfaces within a container, or give priority to attaching, but it _does_ have This One Weird Trick™️ for working around this problem. Thanks to [h4ck3rk3y](https://github.com/moby/moby/issues/25181#issuecomment-1410883805) and [limscoder](https://github.com/moby/moby/issues/35221#issuecomment-537102824) in the Docker github issue comments for discovering that Docker attaches interfaces **based on alphabetically order of their names!**
137137+138138+Using this trick we can now _force_ our ipvlan interface to be attached first by giving it a name alphabetically earlier than our bridge (default) network:
139139+140140+```yaml
141141+services:
142142+ plex:
143143+ image: linuxserver/plex:latest
144144+ # ...
145145+ networks:
146146+ plexnet:
147147+ ipv4_address: 192.168.0.233
148148+ default: # specify we want our container attached to default (bridge) network, as well
149149+# ...
150150+networks:
151151+ plexnet:
152152+ name: aaa # give an early alphabetical name
153153+ driver: ipvlan
154154+ driver_opts:
155155+ parent: ens18
156156+ ipvlan_mode: l2
157157+ ipam:
158158+ config:
159159+ - subnet: 192.168.0.0/24
160160+ gateway: 192.168.0.1
161161+ default:
162162+ name: zzz # specify default network name, later than plexnet name
163163+```
164164+165165+Now when we inspect our container:
166166+167167+```json
168168+{
169169+ "Networks": {
170170+ "aaa": {
171171+ "IPAMConfig": {
172172+ "IPv4Address": "192.168.0.233"
173173+ },
174174+ "Links": null,
175175+ "Aliases": [
176176+ "plex",
177177+ "plex"
178178+ ],
179179+ "MacAddress": "",
180180+ "DriverOpts": null,
181181+ "NetworkID": "3b63c4cb58d336307feba0798a5209f0d2c1547ccf292103127412697e173c57",
182182+ "EndpointID": "23af84f3e64d04ec7396dcff121412119ae982eb94769f7e44c742e77823a0e6",
183183+ "Gateway": "192.168.0.1",
184184+ "IPAddress": "192.168.0.233",
185185+ "IPPrefixLen": 24,
186186+ "IPv6Gateway": "",
187187+ "GlobalIPv6Address": "",
188188+ "GlobalIPv6PrefixLen": 0,
189189+ "DNSNames": [
190190+ "plex",
191191+ "79fa89e12c61"
192192+ ]
193193+ },
194194+ "zzz": {
195195+ "IPAMConfig": null,
196196+ "Links": null,
197197+ "Aliases": [
198198+ "plex",
199199+ "plex"
200200+ ],
201201+ "MacAddress": "02:42:ac:0d:00:02",
202202+ "DriverOpts": null,
203203+ "NetworkID": "5fe40284188df02697e0437d4c3d6495b2ad379f167cb094a8873b32b00e3a83",
204204+ "EndpointID": "c4a058c9543d546bbb752593a75221cc11f2c01601f4a601941c9d2e9a475810",
205205+ "Gateway": "172.13.0.254",
206206+ "IPAddress": "172.13.0.2",
207207+ "IPPrefixLen": 24,
208208+ "IPv6Gateway": "",
209209+ "GlobalIPv6Address": "",
210210+ "GlobalIPv6PrefixLen": 0,
211211+ "DNSNames": [
212212+ "plex",
213213+ "79fa89e12c61"
214214+ ]
215215+ }
216216+ }
217217+}
218218+```
219219+220220+We see it has two attached networks with the names we gave. Inspect `ifconfig` output from the container:
221221+222222+```shell
223223+# ifconfig
224224+eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
225225+ inet 192.168.0.233 netmask 255.255.255.0 broadcast 192.168.0.255
226226+ ether bc:24:11:06:95:d8 txqueuelen 0 (Ethernet)
227227+ RX packets 40175 bytes 58455639 (58.4 MB)
228228+ RX errors 0 dropped 2578 overruns 0 frame 0
229229+ TX packets 36774 bytes 137290812 (137.2 MB)
230230+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
231231+232232+eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
233233+ inet 172.13.0.2 netmask 255.255.255.0 broadcast 172.13.0.255
234234+ ether 02:42:ac:0d:00:02 txqueuelen 0 (Ethernet)
235235+ RX packets 6790 bytes 586521 (586.5 KB)
236236+ RX errors 0 dropped 0 overruns 0 frame 0
237237+ TX packets 9632 bytes 2310195 (2.3 MB)
238238+ TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
239239+...
240240+```
241241+242242+we see that our ipvlan interface is listed first as eth0, so Plex grabs the correct interface to listen on.
243243+244244+## Conclusion
245245+246246+This could all be avoided if Plex just listened on `0.0.0.0`.
247247+248248+{: height="325" }