Plex Media Server on Ubuntu

From WikiMLT

In­stall Plex Me­dia Serv­er

Add the Plex repos­i­to­ry.

sudo apt update
curl https://downloads.plex.tv/plex-keys/PlexSign.key | sudo apt-key add -
echo deb https://downloads.plex.tv/repo/deb public main | sudo tee /etc/apt/sources.list.d/plexmediaserver.list

In­stall the Plex serv­er.

sudo apt update
sudo apt install plexmediaserver
  • On the ques­tion: Do you want to con­tin­ue? Type: Y.
  • On the ques­tion about the source list, type: N.

Here is the es­sen­tial part of the out­put of the above com­mand:

PlexMediaServer install: PlexMediaServer-1.25.3.5409-f11334058 - Installation starting.
PlexMediaServer install:
PlexMediaServer install: Now installing based on:
PlexMediaServer install:   Installation Type:   New
PlexMediaServer install:   Process Control:     systemd
PlexMediaServer install:   Plex User:           plex
PlexMediaServer install:   Plex Group:          plex
PlexMediaServer install:   Video Group:         video
PlexMediaServer install:   Metadata Dir:        /var/lib/plexmediaserver/Library/Application Support
PlexMediaServer install:   Temp Directory:      /tmp
PlexMediaServer install:   Lang Encoding:       en_US.UTF-8
PlexMediaServer install:   Intel i915 Hardware: Not found
PlexMediaServer install:   Nvidia GPU card:     Not Found
PlexMediaServer install:
PlexMediaServer install: Completing final configuration.

Con­trol the Plex serv­er

By de­fault, the Plex Me­dia ser­vice should be au­to­mat­i­cal­ly start­ed. To ver­i­fy this, use the fol­low­ing sys­tem­ctl sta­tus com­mand.

sudo systemctl status  plexmediaserver
sudo systemctl start   plexmediaserver   # sudo systemctl stop plexmediaserver
sudo systemctl enable  plexmediaserver   # sudo systemctl disable plexmediaserver
sudo systemctl restart plexmediaserver

Set­up the Plex serv­er via We­bUI

Now that Plex is in­stalled on your sys­tem, you need to con­fig­ure and fin­ish the set­up through the We­bUI. To ac­cess this, open your pre­ferred In­ter­net Brows­er and nav­i­gate to http://127.0.0.1:32400/web or http://localhost:32400/web.

File stem per­mis­sions

In my case I will use Plex Me­dia Serv­er to op­er­ate with NextClud, so its user will be added to the www-da­ta group, as it is de­scribed in the sec­ond link above.

sudo usermod -aG www-data plex
sudo systemctl restart plexmediaserver.service

Ipt­a­bles Rules (LAN)

sudo iptables -N PLEX_MEDIA_SERVER
sudo iptables -I INPUT 6 -j PLEX_MEDIA_SERVER
sudo iptables -A PLEX_MEDIA_SERVER -p tcp -m tcp --dport 32400 -j ACCEPT
sudo iptables -A PLEX_MEDIA_SERVER -p tcp -m tcp --dport 3005 -j ACCEPT
sudo iptables -A PLEX_MEDIA_SERVER -p tcp -m tcp --dport 8324 -j ACCEPT
sudo iptables -A PLEX_MEDIA_SERVER -p tcp -m tcp --dport 32469 -j ACCEPT
sudo iptables -A PLEX_MEDIA_SERVER -p udp -m udp --dport 1900 -j ACCEPT
sudo iptables -A PLEX_MEDIA_SERVER -p udp -m udp --dport 5353 -j ACCEPT
sudo iptables -A PLEX_MEDIA_SERVER -p udp -m udp --dport 32410 -j ACCEPT
sudo iptables -A PLEX_MEDIA_SERVER -p udp -m udp --dport 32412 -j ACCEPT
sudo iptables -A PLEX_MEDIA_SERVER -p udp -m udp --dport 32413 -j ACCEPT
sudo iptables -A PLEX_MEDIA_SERVER -p udp -m udp --dport 32414 -j ACCEPT

Apache2 re­verse proxy VHost con­fig­u­ra­tion for Plex

This cur­rent con­fig­u­ra­tion is based of at least Serv­er Ver­sion 1.16.5.1488 and Web Ver­sion: 3.108.2. This up­dat­ed con­fig file al­lows the play­ing of trail­ers and TV Show theme mu­sic where as the pre­vi­ous one did not. Re­quire­ments:

  1. Apache ver­sion > 2.4.
  2. A bunch of mod's en­abled (proxy, ssl, proxy_​​​wstunnel, http, dir, env, head­ers, proxy_​​​balancer, proxy_​​​http, rewrite).
  3. Pro­to­cols h2 and http/1.1 needs apachectl ‑V 2.4.17 and high­er.
<VirtualHost *:80>
	ServerName plex.szs.space
	ServerAdmin admin@szs.space
    # Redirect Requests to HTTPS
	Redirect permanent "/" "https://plex.szs.space/"

    ErrorLog ${APACHE_LOG_DIR}/plex.szs.space.error.log
    CustomLog ${APACHE_LOG_DIR}/plex.szs.space.access.log combined
</VirtualHost>

<IfModule mod_ssl.c>
<VirtualHost _default_:443>
	ServerName plex.szs.space
	ServerAdmin admin@szs.space

    <IfModule http2_module>
		# https://httpd.apache.org/docs/2.4/mod/mod_http2.html
		# https://httpd.apache.org/docs/2.4/howto/http2.html

	    Protocols h2 h2c http/1.1
	    #ProtocolsHonorOrder Off
	    #H2Direct on
		#H2Upgrade on

		# From apache2/mods-available/http2.conf
		# Since mod_http2 doesn't support the mod_logio module (which provide the %O format),
		# you may want to change your LogFormat directive as follow:
		LogFormat "%v:%p %h %l %u %t \"%r\" %>s %B \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
        LogFormat "%h %l %u %t \"%r\" %>s %B \"%{Referer}i\" \"%{User-Agent}i\"" combined
        LogFormat "%h %l %u %t \"%r\" %>s %B" common
	</IfModule>


    ErrorLog ${APACHE_LOG_DIR}/plex.szs.space.error.log
    CustomLog ${APACHE_LOG_DIR}/plex.szs.space.access.log combined

	SSLEngine on
	SSLCertificateFile /etc/letsencrypt/live/szs.space/cert.pem
	SSLCertificateKeyFile /etc/letsencrypt/live/szs.space/privkey.pem
	SSLCertificateChainFile /etc/letsencrypt/live/szs.space/chain.pem

    Alias "/welcome.php" "/var/www/wiki.szs.space/resources_metalevel/src/welcome.php"
    Alias "/issues.php" "/var/www/wiki.szs.space/resources_metalevel/src/issues.php"

    <LocationMatch ^/(issues.php|welcome.php|abuseipdb-verification.html|favicon.ico|robots.txt)$>
		Require all granted
    </LocationMatch>

    <ifModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond "%{REMOTE_ADDR}" "!^(172\.16\.[0-9]{1}\.[0-9]{1,3}|127\.0\.0\.[0-9]{1,3}|185\.218\.64\.95)$"
        RewriteCond "%{REQUEST_URI}" "!^/(issues\.php|welcome\.php|abuseipdb-verification\.html|favicon\.ico|rdp-vnc-ssh-portal.*)$"
        RewriteRule "^.*$" "/welcome.php" [R]
        #RewriteRule "^.*$" "https://szs.space/welcome.php" [R]
    </ifModule>

    <IfModule pagespeed_module>
        ModPagespeed off
    </IfModule>

    Define szsPlexDocRoot "/var/www/szs.space.plex"

    DocumentRoot "${szsPlexDocRoot}"
    <Directory "${szsPlexDocRoot}">
		DirectoryIndex index.php
		Require all granted

		Options None FollowSymLinks
		#Options None FollowSymLinks MultiViews

        #AllowOverride None
        AllowOverride All

		<IfModule security2_module>
			SecRuleEngine Off
		</IfModule>
	</Directory>

	# BEGIN Plex Media Server -----
	#  - https://wiki.szs.space/wiki/Plex_Media_Server_on_Ubuntu_20.04
    #  - https://gist.github.com/HazCod/3ef10a15f52c171a7839
    #  - https://stackoverflow.com/questions/40291126/using-apache-as-reverse-proxy-to-access-plex-under-subdomain

    DEFINE plex_url 127.0.0.1
    DEFINE plex_port 32400
    DEFINE serv_name plex.szs.space

    #Options -Includes -ExecCGI
    #LimitRequestBody 512000
    #FileETag None
    #TraceEnable off
    #Timeout 360
    #ProxyTimeout 600
    ProxyRequests Off
    ProxyPreserveHost On
    #ProxyReceiveBufferSize 4096
    SSLProxyEngine On
    RequestHeader set Front-End-Https "On"
    ServerSignature Off
    SSLCompression Off
    SSLUseStapling On
    SSLStaplingResponderTimeout 20
    SSLStaplingReturnResponderErrors Off
    SSLSessionTickets Off
    RequestHeader set X-Forwarded-Proto 'https' env=HTTPS
    Header always set Strict-Transport-Security "max-age=15552000; preload"
    Header always set X-Content-Type-Options nosniff
    Header always set X-Robots-Tag none
    Header always set X-XSS-Protection "1; mode=block"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "same-origin"
    Header always set Permissions-Policy "geolocation=(self), midi=(self), sync-xhr=(self), microphone=(self), camera=(self), magnetometer=(self), gyroscope=(self), fullscreen=(self), payment=(self)"
	#Header always set Permissions-Policy: 'accelerometer=(), autoplay=*, camera=(), cross-origin-isolated=*, document-domain=(), encrypted-media=*, fullscreen=*, geolocation=*, gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=*, publickey-credentials-get=*, screen-wake-lock=(), sync-xhr=*, usb=(), web-share=*, xr-spatial-tracking=*, clipboard-read=(self), clipboard-write=(self), hid=(self), serial=(self)'
    #Header always set Feature-Policy "geolocation 'self'; midi 'self'; sync-xhr 'self'; microphone 'self'; camera 'self'; magnetometer 'self'; gyroscope 'self'; speaker 'self'; fullscreen 'self'; payment 'self'"

    #Header always set Referrer-Policy "strict-origin-when-cross-origin"
    #Header always set Content-Security-Policy "default-src 'self' https:; font-src 'self' data: ${plex_url} ${serv_name}; media-src 'self' blob: data: https: ${plex_url} ${serv_name} *.plex.direct *.plex.tv plex.tv; script-src 'self' 'unsafe-inline' 'unsafe-eval' ${plex_url} ${serv_name} plex.tv *.plex.tv gstatic.com *.gstatic.com *.plex.direct; style-src 'self' ${plex_url} ${serv_name} *.plex.direct 'unsafe-inline'; img-src 'self' data: blob: ${plex_url} ${serv_name} plex.tv *.plex.tv *.plex.direct; worker-src *; frame-src 'none'; connect-src 'self' wss: https: ${plex_url} ${serv_name} plex.tv *.plex.direct *.plex.tv;"
    #Header always set Content-Security-Policy "default-src 'none'; base-uri 'self' ${serv_name}; font-src 'self' data: ${serv_name}; media-src 'self' data: blob: ${serv_name} https://*.plex.direct:32400 https://video.internetvideoarchive.net https://*.cloudfront.net; script-src 'self' 'unsafe-inline' 'unsafe-eval' domain.com ${serv_name}; style-src 'self' 'unsafe-inline' ${serv_name}; img-src 'self' data: blob: https: ${serv_name}; worker-src * blob:; frame-src 'self'; connect-src 'self' https: domain.com ${serv_name} wss://*.plex.direct:32400 wss://pubsub.plex.tv; object-src 'self' ${serv_name}; frame-ancestors 'self' domain.com ${serv_name}; form-action 'self' ${serv_name}; manifest-src 'self' ${serv_name}; script-src-elem 'self' 'unsafe-inline' domain.com ${serv_name} www.gstatic.com"
    Header always set Content-Security-Policy "default-src 'self'; base-uri 'self' ${serv_name}; font-src 'self' data: ${serv_name}; media-src 'self' data: blob: ${serv_name} https://*.plex.direct:32400 https://video.internetvideoarchive.net https://*.cloudfront.net; script-src 'self' 'unsafe-inline' 'unsafe-eval' domain.com ${serv_name}; style-src 'self' 'unsafe-inline' ${serv_name}; img-src 'self' data: blob: https: ${serv_name}; worker-src * blob:; frame-src 'self'; connect-src 'self' https: domain.com ${serv_name} wss://*.plex.direct:32400 wss://pubsub.plex.tv; object-src 'self' ${serv_name}; frame-ancestors 'self' domain.com ${serv_name}; form-action 'self' ${serv_name}; manifest-src 'self' ${serv_name}; script-src-elem 'self' 'unsafe-inline' domain.com ${serv_name} www.gstatic.com"
    
    
    # Don't prox Let's encrypt requests
	ProxyPassMatch ^/.well-known !
    ProxyPassMatch ^/welcome.php !
    ProxyPassMatch ^/issues.php !

    ### Plex Specific Section ###
    ## Plex has A LOT of javascript, xml and html. This helps a lot, but if it causes playback issues with devices, disable this section
    <IfModule mod_deflate.c>
        AddOutputFilterByType DEFLATE text/html
        AddOutputFilterByType DEFLATE text/plain
        AddOutputFilterByType DEFLATE text/css
        AddOutputFilterByType DEFLATE application/javascript
        AddOutputFilterByType DEFLATE text/javascript
        AddOutputFilterByType DEFLATE application/x-javascript
        AddOutputFilterByType DEFLATE image/svg+xml
        AddOutputFilterByType DEFLATE image/x-icon
        AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
        AddOutputFilterByType DEFLATE application/x-font
        AddOutputFilterByType DEFLATE application/x-font-opentype
        AddOutputFilterByType DEFLATE application/x-font-otf
        AddOutputFilterByType DEFLATE application/x-font-truetype
        AddOutputFilterByType DEFLATE application/x-font-ttf
        AddOutputFilterByType DEFLATE font/opentype
        AddOutputFilterByType DEFLATE font/otf
        AddOutputFilterByType DEFLATE font/ttf
        AddOutputFilterByType DEFLATE application/rss+xml
        AddOutputFilterByType DEFLATE application/xhtml+xml
        AddOutputFilterByType DEFLATE application/xml
        AddOutputFilterByType DEFLATE text/xml
        BrowserMatch ^Mozilla/4 gzip-only-text/html
        BrowserMatch ^Mozilla/4\.0[678] no-gzip
        BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
        Header append Vary User-Agent
    </IfModule>

    ## Proxy all web traffic here ## 
    <Location />
        ProxyPass http://${plex_url}:${plex_port}/
        ProxyPassReverse http://${plex_url}:${plex_port}/
    </Location>

    ## Proxy all websocket requests here ## 
    <Location /:/>
        ProxyPass wss://${plex_url}:${plex_port}/:/
        ProxyPassReverse wss://${plex_url}:${plex_port}/:/

        ProxyPass ws://${plex_url}:${plex_port}/:/
        ProxyPassReverse ws://${plex_url}:${plex_port}/:/
    </Location>

    # ProxyPass /:/ ws://${plex_url}:${plex_port}/:/
    # ProxyPassReverse /:/ ws://${plex_url}:${plex_port}/:/

    # ProxyPass /:/ wss://${plex_url}:${plex_port}/:/
    # ProxyPassReverse /:/ wss://${plex_url}:${plex_port}/:/

	<Location /:/websockets/notifications>
		ProxyPass wss://${plex_url}:${plex_port}/:/websockets/notifications
		ProxyPassReverse wss://${plex_url}:${plex_port}/:/websockets/notifications
	</Location>

    <Proxy *>
        Require all granted
    </Proxy>

	# LimitRequestBody 512000
	# FileETag None
	# TraceEnable off
	# #Header edit Set-Cookie ^(.*)$ ;HttpOnly;Secure
    # Header edit Set-Cookie ^(.*)$ "$1; HttpOnly; Secure"

    ## ???
	RewriteEngine on
	RewriteCond %{REQUEST_URI} !^/web
	RewriteCond %{HTTP:X-Plex-Device} ^$
	RewriteCond %{REQUEST_METHOD} !^(OPTIONS)$
	RewriteCond %{QUERY_STRING} (^|&)X-Plex-Device=(&|$) [OR]
	RewriteCond %{QUERY_STRING} !(^|&)X-Plex-Device=
	RewriteRule ^/$ /web/$1 [R,L]
	# END Plex Media Server -----

</VirtualHost>
</IfModule>

Up­date the Plex serv­er

sudo apt update
sudo apt upgrade plexmediaserver -y

Re­move the Plex serv­er

sudo apt autoremove plexmediaserver --purge -y
sudo rm /etc/apt/sources.list.d/plexmediaserver.list
sudo rm -Rf /var/lib/plexmediaserver/
sudo a2dissite ...

Ref­er­ences