Install / Migrate Plex from Bare-Metal Ubuntu to Docker

Guides Nov 19, 2021

With easy-to-install Plex Media Server software and your Plex apps, available on all your favorite phones, tablets, streaming devices, gaming consoles, and smart TVs, you can stream your video, music, and photo collections any time, anywhere, to any device.

In plex for docker, there are essentially 3 networking modes.

  1. Host: It uses the IP address of the host running docker such that a container's networking appears to be the host rather than separate.
  2. Bridge: It creates an entirely new network within the host and runs containers within there. This network is connected to the physical network via an internal router and the docker configures this router to forward certain ports through to the containers within.
  3. Macvlan: It creates a new virtual computer on the network which is the container.

Host networking is the easiest, and if you are starting out, I'll recommend going with the host.

Host Networking

docker run \
-d \
--name plex \
--network=host \
-e TZ="<timezone>" \
-e PLEX_CLAIM="<claimToken>" \
-v <path/to/plex/database>:/config \
-v <path/to/transcode/temp>:/transcode \
-v <path/to/media>:/data \
plexinc/pms-docker

Docker-Compose/ Portainer Stack:

version: '2'
services:
  plex:
    container_name: plex
    image: plexinc/pms-docker
    restart: unless-stopped
    environment:
      - TZ=timezone
      - PLEX_CLAIM=claimToken
    network_mode: host
    volumes:
      - <path/to/plex/database>:/config
      - <path/to/transcode/temp>:/transcode
      - <path/to/media>:/data

Go down to the parameters section for more info.

Bridge Networking

With bridge networking, you will need to manually port forward ports in docker as well as your router if you want external access.

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

Docker-Compose/ Portainer Stack:

version: '2'
services:
  plex:
    container_name: plex
    image: plexinc/pms-docker
    restart: unless-stopped
    environment:
      - TZ=timezone
      - PLEX_CLAIM=claimToken
      - ADVERTISE_IP=http://<hostIPAddress>:32400/
    ports:
      - 32400:32400/tcp
      - 3005:3005/tcp
      - 8324:8324/tcp
      - 32469:32469/tcp
      - 1900:1900/udp
      - 32410:32410/udp
      - 32412:32412/udp
      - 32413:32413/udp
      - 32414:32414/udp
    hostname: <hostname>
    volumes:
      - <path/to/plex/database>:/config
      - <path/to/transcode/temp>:/transcode
      - <path/to/media>:/data

Note: In this configuration, you must do some additional configurations:

  • If you wish your Plex Media Server to be accessible outside of your home network, you must manually setup port forwarding on your router to forward to the ADVERTISE_IP specified above. By default, you can forward port 32400, but if you choose to use a different external port, be sure you configure this in Plex Media Server's Remote Access settings. With this type of docker networking, the Plex Media Server is essentially behind two routers and it cannot automatically set up port forwarding on its own.
  • (Plex Pass only) After the server has been set up, you should configure the LAN Networks preference to contain the network of your LAN. This instructs the Plex Media Server to treat these IP addresses as part of your LAN when applying bandwidth controls. The syntax is the same as the ALLOWED_NETWORKS below. For example, 192.168.1.0/24,172.16.0.0/16 will allow access to the entire 192.168.1.x range and the 172.16.x.x range.

Go down to the parameters section for more info.

Macvlan Networking

docker run \
-d \
--name plex \
--network=physical \
--ip=<IPAddress> \
-e TZ="<timezone>" \
-e PLEX_CLAIM="<claimToken>" \
-h <HOSTNAME> \
-v <path/to/plex/database>:/config \
-v <path/to/transcode/temp>:/transcode \
-v <path/to/media>:/data \
plexinc/pms-docker

Docker-Compose/ Portainer Stack:

version: '2'
services:
  plex:
    container_name: plex
    image: plexinc/pms-docker
    restart: unless-stopped
    environment:
      - TZ=<timezone>
      - PLEX_CLAIM=<claimToken>
    networks:
      physical:
        ipv4_address: <ipAddress>
    hostname: <hostname>
    volumes:
      - <path/to/plex/database>:/config
      - <path/to/transcode/temp>:/transcode
      - <path/to/media>:/data
networks:
  physical:
    external: true

Similar to Host Networking above with these changes:

  • The network has been changed to physical which is the name of the macvlan network (yours is likely to be different).
  • The --ip parameter has been added to specify the IP address of the container. This parameter is optional since the network may specify IPs to use but this parameter overrides those settings.
  • The -h <HOSTNAME> has been added since this networking type doesn't use the hostname of the host.

Go down to the parameters section for more info.

Parameters

  • -p 32400:32400/tcp Forwards port 32400 from the host to the container. This is the primary port that Plex uses for communication and is required for Plex Media Server to operate.
  • -p … Forwards complete set of other ports used by Plex to the container. For a full explanation of what you may need, please see the help article: https://support.plex.tv/hc/en-us/articles/201543147-What-network-ports-do-I-need-to-allow-through-my-firewall
  • -v <path/to/plex/database>:/config The path where you wish Plex Media Server to store its configuration data. This database can grow to be quite large depending on the size of your media collection. This is usually a few GB but for large libraries or libraries where index files are generated, this can easily hit the 100s of GBs. If you have an existing database directory see the section below on the directory setup. Note: the underlying filesystem needs to support file locking. This is known to not be default enabled on remote filesystems like NFS, SMB, and many many others. The 9PFS filesystem used by FreeNAS Corral is known to work but the vast majority will result in database corruption. Use a network share at your own risk.
  • -v <path/to/transcode/temp>:/transcode The path where you would like Plex Media Server to store its transcoder temp files. If not provided, the storage space within the container will be used. Expect sizes in the 10s of GB.
  • -v <path/to/media>:/data This is provided as an example for providing media into the container. The exact structure of how the media is organized and presented inside the container is a matter of user preference. You can use as many or as few of these parameters as required to provide your media to the container.
  • -e KEY="value" These are environment variables that configure the container. See below for a description of their meanings.

The following are the recommended parameters. Each of the following parameters to the container is treated as first-run parameters only. That is, all other parameters are ignored on subsequent runs of the server. We recommend that you set the following parameters:

  • HOSTNAME Sets the hostname inside the docker container. For example, -h PlexServer will set the servername to PlexServer. Not needed in Host Networking.
  • TZ Set the timezone inside the container. For example: Europe/London. The complete list can be found here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
  • PLEX_CLAIM The claim token for the server to obtain a real server token. If not provided, the server will not be automatically logged in. If the server is already logged in, this parameter is ignored. You can obtain a claim token to log in your server to your plex account by visiting https://www.plex.tv/claim. Remember, the claim token lasts only 4 minutes, So generate this at last after completing all steps and when you are starting the container for the first time.
  • ADVERTISE_IP This variable defines the additional IPs on which the server may be found. For example: http://10.1.1.23:32400. This adds to the list where the server advertises that it can be found. This is only needed in Bridge Networking.

These parameters are usually not required but some special setups may benefit from their use. As in the previous section, each is treated as first-run parameters only:

  • PLEX_UID The user id of the plex user-created inside the container. See Users/Groups below for more info.
  • PLEX_GID The group id of the plex group created inside the container. See Users/Groups below for more info.
  • CHANGE_CONFIG_DIR_OWNERSHIP Change ownership of config directory to the plex user. Defaults to true. If you are certain permissions are already set such that the plex user within the container can read/write data in its config directory, you can set this to false to speed up the first run of the container.
  • ALLOWED_NETWORKS IP/netmask entries that allow access to the server without requiring authorization. We recommend you set this only if you do not sign in to your server. For example, 192.168.1.0/24,172.16.0.0/16 will allow access to the entire 192.168.1.x range and the 172.16.x.x range. Note: If you are using Bridge networking, then localhost will appear to plex as coming from the docker networking gateway which is often 172.16.0.1.

Users/Groups

Permissions of mounted media outside the container do apply to the Plex Media Server running within the container. As stated above, the Plex Media Server runs as a specially created plex user within the container. This user may not exist outside the container and so the PLEX_UID and PLEX_GID parameters are used to set the user id and group id of this user within the container. If you wish for the Plex Media Server to run under the same permissions as your own user, execute the following to find out these ids:

$ id `whoami`

You'll see a line like the following:

uid=1001(myuser) gid=1001(myuser) groups=1001(myuser)

In the above case, if you set the PLEX_UID and PLEX_GID to 1001, then the permissions will match that of your own user.

Migrating Plex from Bare-Metal to Docker

Inside the docker container, the database is stored with a Library/Application Support/Plex Media Server in the config directory.

If you wish to migrate an existing directory to the docker config directory:

  • Locate the current config directory as directed here: https://support.plex.tv/hc/en-us/articles/202915258-Where-is-the-Plex-Media-Server-data-directory-located-
  • If the config dir is stored in a location such as, /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/, the config dir will be /var/lib/plexmediaserver.
  • If the config dir does not contain Library/Application Support/Plex Media Server/ or the directory containing Library has data unrelated to Plex, such as OS X, then you should:
  • Create a new directory which will be your new config dir.
  • Within that config dir, create the directories Library/Application Support
  • Copy Plex Media Server into that Library/Application Support
  • Note: by default Plex will claim ownership of the entire contents of the config dir (see CHANGE_CONFIG_DIR_OWNERSHIP for more information). As such, there should be nothing in that dir that you do not wish for Plex to own.

So, for example, if you want to use Host networking like me and migrate your existing Ubuntu 20.04 plex install to docker on the same machine,

version: '2'
services:
  plex:
    container_name: plex
    image: plexinc/pms-docker
    restart: "no"
    environment:
      - TZ=timezone
      - PLEX_CLAIM=claimtoken
    network_mode: host
    volumes:
      - /var/lib/plexmediaserver:/config
      - /dev/shm:/transcode
      - /media/Drive/Videos:/media/Drive/Videos

Note:

  • I am using restart=no flag since I am managing docker containers via systemd in ubuntu and not via docker restart policies. More info is below in Managing plex docker via systemd section.
  • The /dev/shm directory is the temporary storage of your RAM in your server. So basically, I am using RAM as a temporary directory for my transcodes. If you have low RAM available, you can use any other directory on your hard drive.
  • To avoid any complications, I have mounted the same /var/lib/plexmediaserver directory inside a docker container. You might wanna move that directory somewhere where you store all the data for all of your docker containers and mount that instead.
  • Also, to avoid any refresh of metadata inside the new docker installs, I have mounted the same path I had for my media /media/Drive/Videos in bare-metal install. You can obviously choose something else.

Useful information

  • Start the container: docker start plex
  • Stop the container: docker stop plex
  • Shell access to the container while it is running: docker exec -it plex /bin/bash
  • See the logs given by the startup script in real-time: docker logs -f plex
  • Restart the application and upgrade to the latest version: docker restart plex
  • For updating, although I think plex docker container auto-updates on the restart, I have never tried that, instead, I use watchtower for updating all my docker containers. Will post a guide soon on how to do so.

Intel Quick Sync Hardware Transcoding Support

If your Docker host has access to a supported CPU with the Intel Quick Sync feature set and you are a current Plex Pass subscriber, you can enable hardware transcoding within your Plex Docker container.

A list of current and previous Intel CPUs supporting Quick Sync is available on the Intel website.

Hardware transcoding is a Plex Pass feature that can be added to your Docker container by bind mounting the relevant kernel device to the container. To confirm your host kernel supports the Intel Quick Sync feature, the following command can be executed on the host:

lspci -v -s $(lspci | grep VGA | cut -d" " -f 1)

which should output Kernel driver in use: i915 if Quick Sync is available. To pass the kernel device through to the container, add the device parameter like so:

docker run \
-d \
--name plex \
--network=host \
-e TZ="<timezone>" \
-e PLEX_CLAIM="<claimToken>" \
-v <path/to/plex/database>:/config \
-v <path/to/transcode/temp>:/transcode \
-v <path/to/media>:/data \
--device=/dev/dri:/dev/dri \
plexinc/pms-docker

In the example above, the --device=/dev/dri:/dev/dri was added to the docker run command to pass through the kernel device. Once the Plex Media Server container is running, the following steps will turn on the Hardware Transcoding option:

  1. Open the Plex Web app.
  2. Navigate to Settings > Server > Transcoder to access the server settings.
  3. Turn on Show Advanced in the upper-right corner to expose advanced settings.
  4. Turn on Use hardware acceleration when available.
  5. Click Save Changes at the bottom.

NOTE: Intel Quick Sync support also requires newer 64-bit versions of the Ubuntu or Fedora Linux operating system to make use of this feature. If your Docker host also has a dedicated graphics card, the video encoding acceleration of Intel Quick Sync Video may become unavailable when the GPU is in use. If your computer has an NVIDIA GPU, please install the latest Latest NVIDIA drivers for Linux to make sure that Plex can use your NVIDIA graphics card for video encoding (only) when Intel Quick Sync Video becomes unavailable._

Your mileage may vary when enabling hardware transcoding as newer generations of Intel CPUs provide transcoding of higher resolution video and newer codecs. There is a useful Wikipedia page here that provides a handy matrix for each CPU generation's support of on-chip video decoding.

Managing plex docker via systemd

You can look into Managing Docker containers via systemd section in my Docker Ubuntu Guide for detailed info.

Here's my systemd file

[Unit]
Description=Docker plex
After=media-drive.mount
Requires=media-drive.mount
StartLimitIntervalSec=10
StartLimitBurst=5

[Service]
Type=simple
ExecStart=/usr/bin/docker start -a plex
ExecStop=/usr/bin/docker stop plex
Restart=always
RestartSec=60s

[Install]
WantedBy=media-drive.mount

Note: I start all my docker containers after I have mounted my external hard drive, therefore the media-drive.mount is for that.

For a more generic systemd service,

[Unit]
Description=Docker plex
After=network-online.target
Requires=network-online.target
StartLimitIntervalSec=10
StartLimitBurst=5

[Service]
Type=simple
ExecStart=/usr/bin/docker start -a plex
ExecStop=/usr/bin/docker stop plex
Restart=always
RestartSec=60s

[Install]
WantedBy=multi-user.target

Now to start, stop, or restart the plex docker container, you would just use,

sudo systemctl start plex.service
sudo systemctl stop plex.service
sudo systemctl restart plex.service

Note: If you are managing docker containers via systemd like this, DONT use docker restart policies and docker start/stop commands separately. If you stop a container directly via the docker stop command, this systemd service will just restart it after a 60s interval. Also, be sure to set a restart=no flag for your container.

References

Plex's official Docker GitHub repository here.
Plex's official Docker Hub here.
There's also a Linuxserver version of the plex docker image here. Do note, they have a slightly different configuration, but it's well documented in their docs.

Tags