Plex, the Docker & Ansible Way! pt. 2 - Plex on Docker

Testing with Plex and Docker

Now, at this stage in my life, I know what a container is, what Docker is, why it is important, and even a little bit of use. However, I see these nice, long, well documented Docker Hub descriptions, I get a little intimidated. It's not because I don't feel like I can get it working, but because I haven't worked with Docker enough to know what most of the syntax they spit out me does.

But wait! By laying down my expected results, I am sure that I can get this working. My expected results, based off what I know about the capabilities of Docker, are that I have two containers running, both accessible outside the host (meaning I will need to expose the ports, and even change the port mappings on one), with different media libraries that exist on the host (or network share), not the container. At one point in time, I would have thought that something like this would be hard or impossible. But with Docker, I am sure it will be easy.

My test will be to get just one of the Docker containers up and running, with a generic name, to see if it produces the desired result. Because the kids media has additional constraints, I will use this one as it is the effect that I am most concerned about.

Looking at the Docker Hub description, I found an example use case that I feel closely matches mine:

docker run \
-d \
--name plex \
-p 32400:32400/tcp \
-p 3005:3005/tcp \
-p 8324:8324/tcp \
-p 32469:32469/tcp \
-p 1900:1900/udp \
-p 32410:32410/udp \
-p 32412:32412/udp \
-p 32413:32413/udp \
-p 32414:32414/udp \
-e TZ="<timezone>" \
-e PLEX_CLAIM="<claimToken>" \
-e ADVERTISE_IP="http://<hostIPAddress>:32400/" \
-h <HOSTNAME> \
-v <path/to/plex/database>:/config \
-v <path/to/transcode/temp>:/transcode \
-v <path/to/media>:/data \
plexinc/pms-docker

Looking at that, I (almost) immediately know that:
-d runs Docker
--name names the instance that is running
-p exposes the port in host:container format
-e is for extra parameters
-h appears to be for the hostname. Probably what Plex sees the server name as.
-v is not for verbosity, but for volumes!

Alright, looks like the few minutes I spent watching Docker on LinuxAcademy is paying off so far. However, I need to find out what those extra options do exactly.
I can safely say that the first is for timezone. The next one though... what the heck is a plex claim?! Well, looking through the well documented description on Docker Hub, I found that this will automatically log in with my Plex account... NICE! This will save me a step in the setup phase.

All I need to do to get this Plex Claim token is navigate to https://www.plex.tv/claim, login, and generate a token. I will do so, but as it will be private, I will leave this option <in brackets> for the remainder of this article.

Based off of what I now know, and that I want to change the port that is exposed on the host, I might try the command like so, replacing <hostIPAddress> with the IP address of my server:

docker run \
-d \
--name kids1 \
-p 32401:32400/tcp \
-p 3006:3005/tcp \
-p 8325:8324/tcp \
-p 32470:32469/tcp \
-p 1901:1900/udp \
-p 32510:32410/udp \
-p 32512:32412/udp \
-p 32513:32413/udp \
-p 32514:32414/udp \
-e TZ="America/Indiana/Tell_City" \
-e ADVERTISE_IP="http://<hostIPAddress>:32401/" \
-h KidsTest1 \
-v /srv/plex/config:/config \
-v /srv/plex/transcode:/transcode \
-v /srv/samba/kids:/data \
plexinc/pms-docker

Okay, lets run this down:

  • -d runs the container in daemon mode
  • --name gives the container a name (which must be unique among other containers)
  • -p options map different ports from host:container. Note that while I merely increment most ports by 1, Plex defaults for 32410 and 32412-32413 would run into each other, so I incremented those by 100.
  • -e TZ="America/Indiana/Tell_City" is the timezone I am using. This is Central Time zone, so you may choose to use another.
  • -e ADVERTISE_IP="http://:32401/" will be the server IP address followed by the port that we first defined above
  • -h KidsTest1 is what Plex will see the server as being named
  • -v </srv/plex/config>:/config will be our Plex database directory, with all of the meta data
  • -v </srv/plex/transcode>:/transcode will be our Plex transcoder temp files will go
  • -v </srv/samba/kids>:/data is the root of the kids media directory that I created earlier
  • plexinc/pms-docker is the name of the Docker Hub image we will want to use.

Alright, let's create the transcode and config directories, stop the Plex server that I created previously (without Docker), then try this bad boy out!

sudo mkdir -p /srv/plex/config/kids
sudo mkdir -p /srv/plex/transcode/kids
sudo mkdir -p /srv/plex/config/adults
sudo mkdir -p /srv/plex/transcode/adults
sudo systemctl disbale plexmediaserver.service
sudo systemctl stop plexmediaserver.service

And finally:

docker run \
-d \
--name kids1 \
-p 32401:32400/tcp \
-p 3006:3005/tcp \
-p 8325:8324/tcp \
-p 32470:32469/tcp \
-p 1901:1900/udp \
-p 32510:32410/udp \
-p 32512:32412/udp \
-p 32513:32413/udp \
-p 32514:32414/udp \
-e TZ="America/Indiana/Tell_City" \
-e ADVERTISE_IP="http://192.168.171.112:32401/" \
-h KidsTest1 \
-v /srv/plex/config/kids:/config \
-v /srv/plex/transcode/kids:/transcode \
-v /srv/samba/kids:/data \
plexinc/pms-docker

Alright! Now let's verify that the container is started:

ben@ub1604-ben-dev:~$ sudo docker ps
CONTAINER ID        IMAGE                COMMAND             CREATED             STATUS                             PORTS                                                                                                                                                                                                                                NAMES
4be4ac44a2cd        plexinc/pms-docker   "/init"             23 seconds ago      Up 22 seconds (health: starting)   0.0.0.0:1901->1900/udp, 0.0.0.0:3006->3005/tcp, 0.0.0.0:8325->8324/tcp, 0.0.0.0:32401->32400/tcp, 0.0.0.0:32510->32410/udp, 0.0.0.0:32512->32412/udp, 0.0.0.0:32513->32413/udp, 0.0.0.0:32514->32414/udp, 0.0.0.0:32470->32469/tcp   kids1

Looks the like the container is in the starting state, which is fine by me. Grab a cup of coffee while you wait and check again in a few minutes.

My container started in about 3 minutes. Now is the moment of truth. I will use the browser on my PC to try to connect to the Plex Server over the non-standard port and see if it works!

More Pain with Plex

Well, we did all of that work to find out that you can hit the web interface, but Plex can't find the server. After doing some online research, I found that Plex must run on port 32400. To get around this, you have to log into the local Plex server web interface and set the external port that it listens on (as if you were going to use a router/firewall to access it from the outside).

Fantastic. I won't go on a rant about why I feel that this is terrible application design, and instead go on to finding out how we can do this without losing all of the work.

We could use ssh or execute a bash shell on the container, use lynx or w3m to try to log into the web interface and set the port that way... But it's clunky. I began looking at ways to use the standard port on first run, set the port in the web interface, and then remapping the port on the container again. It doesn't seem clear cut, but a post on Stack Overflow seems to have the answer:
1.) Create the container, and make the changes we need
2.) Commit the container
3.) Remove the first container
4.) Create a new container based off of the first container

Okay, yeah, I dig it. It will work. It still seems a little clunky. Scrolling down the same post reveals another answer:
1.) Stop the container
2.) Modify the container's hostconfig.json
3.) Restart Docker
4.) Start the container

One of the comments to the second answer sums it up nicely:

"Perfect... There was no need to create an image with this approach"

With that being said, let's try this out. From the same answer, I learned that the aforementioned file is located in '/var/lib/docker/containers/[hash_of_the_container]/hostconfig.json'
Okay, let's get the container hash:

sudo docker inspect --format="{{.Id}}" kids1
4be4ac44a2cdc6b621d6519699c7bdff6118a9b6c57f96c0b7070f081e7f45cc

Now, let's modify the file:

sudo docker stop kids1
sudo vim /var/lib/docker/containers/4be4ac44a2cdc6b621d6519699c7bdff6118a9b6c57f96c0b7070f081e7f45cc/hostconfig.json

I simply searched for "32401" and then changed it to "32400" -- the default port.
Now let's restart the docker service, start the container, and check the results:

sudo systemctl restart docker
sudo docker start kids1
sudo docker ps -a

Success! Let's check the web portal to be sure.
Well, it would seem that I still cannot find the server.
Let's take a look here...

The real problems (I was wrong)

The first real problem was that none of my ports were standard (assuming that they need ALL standard). Secondly, it seems like a claim might be required for this setup. My guess is that it uses the claim to automatically log in to set the settings.

To prove my point, let's remove our kids1 test container and create a new one with the standard ports, this time mapping the IP as well:

sudo docker stop kids1
sudo docker rm kids1


docker run \
-d \
--name kids2 \
-p 192.168.171.112:32400:32400/tcp \
-p 192.168.171.112:3005:3005/tcp \
-p 192.168.171.112:8324:8324/tcp \
-p 192.168.171.112:32469:32469/tcp \
-p 192.168.171.112:1900:1900/udp \
-p 192.168.171.112:32410:32410/udp \
-p 192.168.171.112:32412:32412/udp \
-p 192.168.171.112:32413:32413/udp \
-p 192.168.171.112:32414:32414/udp \
-e TZ="America/Indiana/Tell_City" \
-e PLEX_CLAIM="<Your Plex Claim>" \
-e ADVERTISE_IP="http://192.168.171.112:32400/" \
-h KidsTest2 \
-v /srv/plex/config/kids:/config \
-v /srv/plex/transcode/kids:/transcode \
-v /srv/samba/kids:/data \
plexinc/pms-docker

The lightbulb was that we already set the "external port" using the "-e ADVERTISE_IP" option.

After the container fully starts we can verify by navigating to the web portal.
And... IT WORKS!

Great... So now we are at a third problem. Now we only can we only use standard ports, at least on the inside, and we have those ports bound on our single network interface, so we cannot start up a second container. While I may be able to use a non-standard "outside" port, I have another idea...

Let's Get Linuxy

We will simply create a sub-interface on our interface and add another IP. Now, it's worth noting that the process is different on CentOS/RHEL, so I did have to do a little bit of research in this instance, but the idea is the same. It's also worth noting that you usually need to do this when you need to enable one or more VLANs on a Linux system.

# /etc/network/interfaces
auto eno1:0
iface eno1:0 inet static
address 192.168.171.231
netmask 255.255.255.0
gateway 192.168.171.1

Note that your network interface may be "eth0" or something different. Also, we can create as many of these as we need, as long as the interface name:id is unique. We will also need to restart the networking service or restart the server for the new IP address to take place.

Testing Two Containers Concurrently

Spinning up another container to test is easy, as we have already done the dirty work before, but we should get a new claim token just in case before proceeding:

docker run \
-d \
--name adult1 \
-p 192.168.171.231:32400:32400/tcp \
-p 192.168.171.231:3005:3005/tcp \
-p 192.168.171.231:8324:8324/tcp \
-p 192.168.171.231:32469:32469/tcp \
-p 192.168.171.231:1900:1900/udp \
-p 192.168.171.231:32410:32410/udp \
-p 192.168.171.231:32412:32412/udp \
-p 192.168.171.231:32413:32413/udp \
-p 192.168.171.231:32414:32414/udp \
-e TZ="America/Indiana/Tell_City" \
-e PLEX_CLAIM="<Your Plex Claim>" \
-e ADVERTISE_IP="http://192.168.171.231:32400/" \
-h AdultTest1 \
-v /srv/plex/config/adults:/config/ \
-v /srv/plex/transcode/adults:/transcode \
-v /srv/samba/:/data \
plexinc/pms-docker

After a few minutes of waiting, we see that the container is now running. Let's try to hit the Plex web portal on the newly created sub interface.

Success! It is worth noting here that if your config directories are the same on both containers, then the name will change to the newest one and they will both be the same. This can be an actual use case, but not today.

We can now setup both containers with separate media (and even accounts if you want to keep it truly separated -- just make sure to get Plex claims from the correct account(s)).

In part 3 of this series, we will look at automating this setup with Ansible!

Show Comments