Issue with Setting Up Remote Access When Plex Doesn’t Detect the Correct Public IP (e.g., Using VPN)

Hi,

I’ve been struggling to set up remote access on my Plex server, particularly in cases where Plex fails to detect the correct public IP address (e.g., when the server is running behind a VPN).

I managed to temporarily bypass this issue by manually entering remote URL in settings using https://123-123-123-123.c45678097867564656658f.plex.direct:32400 format. While this works, it comes with significant drawbacks:
• My public IP is not static, so I need to update the configuration every time the IP changes.
• It doesn’t seem possible to use a custom domain like https://XXX.duckdns.org.c45678097867564656658f.plex.direct:32400/web, which would solve the issue.

I’m surprised that Plex doesn’t allow us to specify a custom domain where the server is accessible. If I could simply set something like XXX.duckdns.org, Plex could resolve the actual IP dynamically, and redirects from https://app.plex.tv would work seamlessly.

This limitation makes it challenging to use Plex in dynamic environments where the public IP changes frequently and can not be detected correctly by the plex server. It feels like a simple solution could be implemented – just like we can manually set the port, we could also set a custom IP or URL.

Is something like this being considered for future updates? Or is there another way to handle this problem?

Thanks for any tips or suggestions!

@ChuckPa I think you might bring some light here

Go to ‘Network’ in settings. There’s an option to set your own custom domain.

Hi @Krazeh, I believe you’re referring to Custom server access URLs, correct?

The issue I’m encountering is that when I specify a custom URL, it doesn’t function as expected because it requires an HTTPS connection with valid custom certificates. If the certificate is invalid, the URL isn’t utilized.

What I was hoping for is that the URL would be used to detect the public IP. I tried using my XXX.duckdns.org address, but this caused an issue where app.ples.tv didn’t allow streaming in higher quality—it wasn’t using the direct connection.

If your real public IP is, for example, 203.0.113.100 and the port you’re forwarding externally is the Plex default of 32400, enter the following for your custom server access URL:
http://203.0.113.100:32400

Plex will automatically convert this into one their secure *.plex.direct connection URLs. So, for example, the above will become:
https://203-0-113-100.certificateuuid.plex.direct:32400

You can tell what’s been published to Plex’s server’s by navigating here (if you use Safari, you’ll need to turn on developer options):
https://plex.tv/api/resources?includeIPv6=1&includeHttps=1&X-Plex-Token=<your_plex_token>

You can find your Plex token using the information here:
https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/

Note, there are situations in which this does not work, for example if PMS starts detecting your real public IP for some reason (VPN disconnection). This URL will return what PMS thinks its public IP is:
https://v4.plex.tv/pms/:/ip

Hi @pshanew, this is almost what I would expect, but instead of http://203.0.113.100:32400 I would like to put there http://XYZ.duckdns.org:32400 as my public IP changes constantly (it is not static). When I did this, I was not able to select bitrates above 2mbps and the server was complaining that it is not directly connected. When I went directly to http://XYZ.duckdns.org:32400 in web browser I was able to select what ever quality I wanted, I am unfortunately not able to do that in iOS or tvOS apps. So it looks like it does not work properly with custom domains.

I was able to get it working by putting there https://203-0-113-100.certificateuuid.plex.direct:32400 directly, but this does not solve the issue for me as my IP will change and I would need to constantly modify this Custom server access URLs

If you go that route, as you noted, you’ll have to set up a custom server certificate as well.

Another option, if your VPN provider supports it (and on Linux) would be to use split tunneling, whereby traffic from Plex Media Server would bypass the VPN.

Yeah, and that’s the issue — it’s not easy to do (at least not for me :smile:). I would expect this to work out of the box since it’s as simple as detecting the public IP from the provided URL and then making it work.

In my opinion, it shouldn’t be necessary to create a custom certificate. I would expect it to automatically detect the public IP and convert it to something like https://203-0-113-100.certificateuuid.plex.direct:32400.

I believe Plex uses the servers listed in this link to check the remote access status and identify the public IP address of your PMS. The IP list is publishsed by Plex and updated when they change.

If you have the ability to selectively route traffic to/from Plex at your router via the specific connection that you need then that could help your issue.

If you want Plex to use a custom domain such as XXX.duckdns.org and not have PMS publish your public IP address up to Plex’s servers then it is possible but the set up is slightly counter intuitive.

You need to first disable remote access. If remote access is enabled then Plex will publish what it thinks your public IP is. If you need to set an external port map other than 32400 then you need to enable remote access first, enable Manually specify public port, add the correct port number, save the settings, then disable remote access. Plex will retain the changed port.

To enable secure connections to your PMS via a custom domain you will need to create a custom SSL certificate for the XXX.duckdns.org domain. This could be done using Let’s Encrypt, but you may need to run a HTTP challenge as I don’t believe Duck DNS supports DNS challenges (this is based on a quick search so I could be wrong). This would require you to run an accessible web server which will reply to the Let’s Encrypt challenges. There are other DDNS providers that may let you use DNS challenges which are simpler. You’ll need to configure automatic renewal of the certificate and restarting of Plex via a deployment handle in acme.sh.

Save the custom certificate as a PKCS12 (i.e. PFX file) and add the following under Network settings:

  • Custom certificate location - Enter the full path to you PFX file
  • Custom certificate encryption key - The encryption key/password for your PFX file (Plex displays this in plain text which is pretty poor security practice on their part).
  • Custom certificate domain - You custom domain name (e.g. XXX.duckdns.org)
    You do not need to specify a custom server access URL as Plex automatically adds the domain specified under the custom certificate domain setting.

If you try to connect to your PMS using the IP address then it will present the plex.direct server certificate. It will only present the custom domain certificate when you connect using the custom domain URL.

When you check the published server addresses you should see both your local IP and the custom domain, and not your public IP.

Plex will show the red remote access not working indicator, but ignore that as it should work via the custom dns provided you have the correct port forward enabled on your router.

I’m aware of both custom certificates and split tunneling, but both are too complicated for me to set up. I’ve spent countless hours on this over the past two years and never got it working. Today, I managed to bypass it by using the public IP directly, but that’s not a long-term solution. I’ve seen many users facing the same issue, and in my eyes, there could be a simple fix for it.

Just allow me to specify the custom domain here, similar to how I can configure the port…

Thanks, @kesawi. I’m aware of all this, but unfortunately, I haven’t been able to set it up. I’ve never found an easy (at least for me) tutorial on how to generate a certificate for my custom domain.

P.S. I still think that ideally this shouldn’t be necessary. Plex should implement a change that allows me to specify a URL to detect the public IP, and they could handle the certificate setup in the background as they are doing right now when I specify the http://123.123.123.123:32400.

It’s a previously requested feature, but not too many votes.

To get custom certificates issued, I suggest switching to DynDNU which is a free dynamic DNS service that works better with Let’s Encrypt.

Install https://github.com/acmesh-official/acme.sh by following the Wiki. You’ll probably want to install it as the root user so that it will have permission to write the certificate to the Plex directory.

You’ll need to create an API key in DynDNU for acme.sh to be able to do the necessary challenges.

Once you’ve created the API key, you then need to use acme.sh to issue the certificate using the instructions in the wiki. You’ll need to modify the command so that it exports to a PKCS12 file. For example if you just want a certifcate for the specific domain and don’t want a wildcard certificate then the command, and the encryption key for the PFX file is ` then:

export Dynu_ClientId="<client id>"
export Dynu_Secret="<secret>"
./acme.sh --issue --keylength 2048 --dns dns_dynu -d your.dynamicdns.com --to-pkcs12  --password <password>

That should then request the certificate and also create the PFX file.

To get it to deploy then you need to create a custom deployhook script calledplex.sh and place it in the deploy folder where ever acme.sh was installed.

#!/usr/bin/bash

#returns 0 means success, otherwise error.

# Variables
# You can either leave the export lines uncommented, or comment them out and enter them from the cmd line prior to using the deployment script for the first time.

# Your PFX file password/key
export DEPLOY_PLEX_P12PASS='<password>'

#The location that you want to deploy the certificate to that your Plex Media Server can access
export DEPLOY_PLEX_CERT_LIBRARY='/var/lib/plexmediasever'

#The command to restart your Plex Media Server
export DEPLOY_PLEX_RELOAD='systemctl restart plexmediaserver'


#### Do not edit below this line ####

plex_deploy() {
	_cdomain="$1"
	_ckey="$2"
	_ccert="$3"
	_cca="$4"
	_cfullchain="$5"

	_debug _cdomain "$_cdomain"
	_debug _ckey "$_ckey"
	_debug _ccert "$_ccert"
	_debug _cca "$_cca"
	_debug _cfullchain "$_cfullchain"

	_getdeployconf DEPLOY_PLEX_P12PASS
	_getdeployconf DEPLOY_PLEX_CERT_LIBRARY
	_getdeployconf DEPLOY_PLEX_RELOAD

	_debug2 DEPLOY_PLEX_P12PASS "$DEPLOY_PLEX_P12PASS"
	_debug2 DEPLOY_PLEX_CERT_LIBRARY "$DEPLOY_PLEX_CERT_LIBRARY"
	_debug2 DEPLOY_PLEX_RELOAD "$DEPLOY_PLEX_RELOAD"

	_reload_cmd=$DEPLOY_PLEX_RELOAD

	# Check if deployment path exists
	if [ ! -d $DEPLOY_PLEX_CERT_LIBRARY ]; then
		_err "Certificate deployment path doesn't exist"
		return 1
	fi

	# Flag to track errors
	ERROR_FLAG=false

	DEPLOY_P12_FILE="$DEPLOY_PLEX_CERT_LIBRARY/$_cdomain.pfx"

	
	cp $_import_pkcs12 $DEPLOY_P12_FILE || { _err "Error copying pkcs12 file"; ERROR_FLAG=true; }

	# Restart Plex Media Server if no errors encountered
	if [ "$ERROR_FLAG" = false ]; then
		_info "Reload services (this may take some time): $_reload_cmd"
		if eval "$_reload_cmd"; then
			_info "Reload success!"
		else
			ERROR_FLAG=true
			_err "Reload error"
			return 1
		fi
	fi

	# Check if any errors occurred during processing
	if [ "$ERROR_FLAG" = true ]; then
		_err "Plex Media Server deploy script failed with errors."
		return 1
	fi

	# Successful, so save all (non-default) config:
	_savedeployconf DEPLOY_PLEX_P12PASS "$DEPLOY_PLEX_P12PASS"
	_savedeployconf DEPLOY_PLEX_CERT_LIBRARY "$DEPLOY_PLEX_CERT_LIBRARY"
	_savedeployconf DEPLOY_PLEX_RELOAD "$DEPLOY_PLEX_RELOAD"

	return 0

}

This script grabs the PFX file, copies it to the directory /var/lib/plexmediasever, and restarts PMS. The PFX file will be called your.dynamicdns.com.pfx. I’ve modified an existing custom deployment script I have for another application to work with Plex and haven’t tested it. Hopefully it shouldn’t produce any errors, but if you decide to use it I’m happy to help troubleshoot.

To get acme.sh to use the deployment hook script and deploy you need to use the command:

./acme.sh --deploy --deploy-hook plex -d your.dynamicdns.com

You’ll need to make sure the user and access persmissions of the PFX key in the destination are set so that Plex can read it.

acme.sh should automatically create a cron job to renew and deploy the certificate. If it hasn’t then you can use the command ./acme.sh --install-cronjob.

EDIT: Changed acme.sh command so that it issues a RSA rather than ECC key.

1 Like

Thanks a lot for the guide! I’m currently not at home, so I can’t SSH into my server, but once I’m back (in two weeks), I’ll definitely give it a try! Just one question: I’m using Plex in a Docker container (CasaOS), so I’ll need to set this up inside the container, right?

I’m not familiar with CasaOS but I assume you can still install acme.sh and the deploy script outside of the docker container, but would just need to modify the variables at the start of the deployment script to reflect the locations of your PMS config directory and restart command for the PMS container.

Assuming you’ve mapped /var/lib/plexmediasever to /config in your docker container then the DEPLOY_PLEX_CERT_LIBRARY='/var/lib/plexmediasever' variable wouldn’t need to change.

If it’s using a different directory such as /home/plex mapped to /config in docker then you would use DEPLOY_PLEX_CERT_LIBRARY='/home/plex'.

If it’s kept in a docker volume then script won’t work and I have a different one that modifies the docker volume rather than the mapped directory. Looking at the default approach CasaOS appears to take I think it maps volumes.

For the DEPLOY_PLEX_RELOAD variable you’ll need to change it to match the command used to restart the container in docker. I can’t seem to find any documentation on how CasaOS does it, but assuming it uses docker and the container name is plex, then you could just use DEPLOY_PLEX_RELOAD='docker restart plex'

If you’re interested, here’s a small’ish Python script which will automate some of what you’ve been doing manually. It’ll check what your DDNS domain resolves to, check to see if it is different than what is currently set in Plex, and change it if so. Optionally, it will restart the PMS service (or container) if a change has been made.

If you run it without any arguments it will tell you what you should need. In your case, you’d do something like:
./customurl.py -f plex.example.com -p path/to/Preferences.xml -c <container_name> -r

What this says is check the IP address of plex.example.com; read the contents of the Preferences.xml file at the designated path; if the custom server URL has changed, restart the container named ‘container_name’.

There are some limitations of this script:

  • It does not back up your current Preferences.xml; do so yourself before running, if you decide to.
  • It only supports one custom server URL. If you need to use multiple for some reason, it won’t work for that. (It would be relatively easy to add that, but doesn’t seem to be a common use case.)
  • To be able to restart the Docker container (or systemd unit, if that’s how it’s run) you need root privileges; i.e. you need to sudo it.
  • It currently calls the docker or systemctl executable by hard-coded paths. They should be correct for most Debian-/Ubuntu-derived systems. Run which docker or which systemctl to see where they really exist on your system. Adjust the last 4 lines lines as appropriate.
  • If changes are made, a PMS service restart is required for them to take effect.
  • This is a script which does in ~60 lines what could likely be done in half that with some more thought and optimization.

All that being said, it’s generally well-behaved; that bold warning in the first bullet is just a reminder to back Preferences.xml up first.

So, if you like, run it from the host (not the container) and for the -p/--path argument give the full path to the Preferences.xml file. At whatever path you mounted to /config in the container it will be ‘base_mount_path/Library/Application Support/Plex Media Server/Preferences.xml’

If you’re interested, save the following as ‘customurl.py’ on your system, make it executable, and give it a whirl.

#!/usr/bin/env python3

import os
import socket
import xml.etree.cElementTree as ET
import argparse

def getaddr(fqdn):
    try:
        addr = socket.gethostbyname(fqdn)
        return addr
    except Exception as e:
        return None
    
def setcustomurl(prefs_path, ip, secure, port):
    try:
        tree = ET.parse(prefs_path)
        root = tree.getroot()
        current_url = root.get('customConnections')
    except Exception as e:
        return False
    schema = "https" if secure else "http"
    url = f"{schema}://{ip}:{port}"
    if current_url == url:
        return False
    try:
        root.set('customConnections', url)
    except Exception as e:
        return False
    try:
        tree.write(prefs_path)
    except Exception as e:
        return False
    return True

ap = argparse.ArgumentParser()
ap.add_argument("-p", "--path", help="Path to Preferences.xml", required=True)
ap.add_argument("-f", "--fqdn", help="Fully-Qualified Domain Name of Server", required=True)
ap.add_argument("-t", "--tcpport", help="Public port", default="32400")
ap.add_argument("-s", "--secure", help="Use HTTPS", action='store_true')
ap.add_argument("-c", "--container", help="Docker container name")
ap.add_argument("-r", "--restart", help="Restart Plex Media Server (requires root)", action='store_true')
args = ap.parse_args()

addr = getaddr(args.fqdn)

if addr is not None:
    changed = setcustomurl(args.path, addr, args.secure, args.tcpport)
else:
    print("Error setting custom URL, exiting...")
    exit(1)

if changed and args.restart:
    if args.container is not None:
        arglist = ["docker", "restart", args.container]
        os.execv("/usr/bin/docker", arglist)
    else:
        arglist = ["systemctl", "restart", "plexmediaserver"]
        os.execv("/usr/bin/systemctl", arglist)

P.S. Ignore this whole thing and do the right way with @kesawi’s instructions above. Using a secure connection protected via a modern TLS certificate is better.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.