Plex on SmartOS

Plex on SmartOS

Plex is a client-server media player suite combined with a hybrid video streaming service, and is currently the most popular do-it-yourself method of media streaming.

While the Plex Media Player is open source, the Plex Media Server is closed source, and only distributed by Plex to a discrete set of platforms, excluding Illumos or SmartOS.  Fortunately, Plex works fine within an LX branded zone, which is exactly how we will be setting it up today.

SmartOS Zone Configuration

While Void Linux's incredibly light default memory footprint would be ideal for this, Plex tends to update their media server software regularly, and doesn't distribute packages for Void's package manager (XBPS); I would prefer a well integrated update mechanism, so this project is probably best suited for a Debian or Ubuntu image.

This zone will also be using a read-only lofs filesystem mount between this zone and the file server zone featured in a previous article.  This ensures the principle of least privilege, in that the Plex Media Server can only read media (and not modify or delete it) while also isolating our bulk storage in a different zone.  This specific approach unfortunately blocks this zone from automatically booting (as the source path is not available until the other zone has fully booted), and it will need to be manually started after your global zone boots.

Below is an example manifest, please note that {uuid} refers to the uuid of the bulk storage zone:

{
  "image_uuid": "63d6e664-3f1f-11e8-aef6-a3120cf8dd9d",
  "brand" : "lx",
  "kernel_version": "4.20",
  "alias": "plex",
  "hostname": "plex",
  "cpu_cap": 200,
  "max_physical_memory": 2048,
  "quota": 20,
  "delegate_dataset": true,
  "filesystems": [
    {
      "type": "lofs",
      "source": "/zones/{uuid}/root/home/brian/media",
      "target": "/media",
      "options": [ "ro" ]
    },
  ],
  "resolvers": [ "1.1.1.1" ],
  "nics": [
    {
      "nic_tag": "admin",
      "ips": [ "dhcp" ],
      "primary": true
    }
  ],
  "customer_metadata": {
    "root_authorized_keys": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDrStZlHS0yfE8n71meairBGvFnc5mlDFNKAJy7tQMi2",
    "user-script": "/usr/sbin/mdata-get root_authorized_keys > /root/.ssh/authorized_keys"
  }
}

For more details about the properties configured for this manifest, please read this article.

We're expecting regular and predictable CPU and memory usage per transcoded stream from this zone, if you're experiencing transcoding performance issues, you may need to increase the values for cpu_cap and max_physical_memory.  In testing I've found that 1GiB of memory is suitable for a single viewer who is direct streaming video.  These requirements are likely much higher for multiple transcoding users.

The quota value of 20GB is fine for many collections, as that only applies to this zone (which only holds the plex databases and meta-files, not the actual media files, which have been mounted from another zone).  Depending on how large your media collection grows, this value may need to be increased.

Ansible Play

This Ansible play is relatively simple and straight forward.  It's broken up into two parts, a common-debian role that mirrors the common role for smartos:

  • The PATH variable includes executables under /native.
  • The hostname has been set.
  • The default delegated ZFS filesystem has been unmounted.
  • All packages have been upgraded.
  • SSH server configured to only accept public key authentication.

As well as a plex role that performs plex specific configuration on the zone:

  • The Plex package signing key has been added to apt.
  • The Plex repository has been added to apt.
  • A ZFS filesystem is mounted at /var/lib/plexmediaserver.
  • A recordsize tuned ZFS filesystem has been mounted for the Plex sqlite3 databases.
  • Ownership of /var/lib/plexmediaserver is set to 999:999 (necessary for plex to install to it.)
  • Installing Plex Media Server
  • Enabling Plex Media Server
  • Ensuring that system will update all packages regularly, including Plex Media Server.

Below is an example ansible play:

---
- name: 'Testing Plex'
  hosts: plex
  roles:
  - plex
  vars:
    hostname: plex

Plex Configuration

This Plex Media Server will now need to be associated with your Plex account.  Point a web browser to https://<ip address>:32400/web to continue.

Port Forwarding

If your network employs NAT, you may want to port forward your plex server at your router to ensure that clients outside of your local network can have access.

If you are using a SmartOS Zone to route IP traffic for your network, ensure that the following line exists within /etc/ipf/ipnat.conf, where <external ip> is your router's public IP address and <internal ip> is your plex server's IP address:

# Plex Redirection
rdr net0 <external ip>/32 port 32400 -> <internal ip> port 32400 tcp

Conclusion

The read-only lofs filesystem mount might be better employed mounting a ZFS source filesystem that exists outside of any zone, with a second read-write lofs filesystem mount connecting that same source filesystem to another zone to allow for editing.  This configuration might end up being more trouble than it's worth as well, since ZFS and non-ZFS filesystem mounting is not interlaced when Zones boot up.

For now, I just remind myself to manually start the Plex zone whenever I reboot the global zone.  Perhaps there's an opportunity to extend SmartOS to respect Zone boot order, specifically allowing Zones to specify dependency zones that need to be booted up before they can boot.

Also, a special thanks to the Lights and Shapes blog for writing about this significantly earlier than I have.