How to securely expose Plex from behind CGNAT for library sharing using Tailscale and a free Oracle VM


updated

I wrote before about securely exposing Plex for sharing your library, but my previous solution relied on Cloudflare Tunnel and it was technically against their TOS. So I switched to using a Oracle VM on their free-tier, connecting it to my home network with Tailscale, and exposing Plex via reverse proxy on the VM. It works like a charm!

Table of Contents

  1. What and Why
  2. Pre-Requisites
  3. Create OCI account
  4. Create a compute instance
  5. SSH into instance
  6. Set up Tailscale
  7. Configure DNS in Tailscale
  8. Add and configure domain in Cloudflare
  9. Install reverse proxy on Oracle instance
  10. Add ingress rules on OCI
  11. Add proxy host in Nginx Proxy Manager
  12. Configure the Plex server
  13. Using the Plex server as subnet router and exit node
  14. References

Please note that effective April 29, 2025 an active Plex Pass subscription is required to remotely access Plex — the below still works as is to get through CGNAT with Plex Pass, however if you are trying to share your library without Plex Pass (or without the user you’re sharing with have a Remote Watch Pass) then additional configuration is required. I have added a section at the end to setup the Plex server as a subnet router and exit node, which seems to be a workaround to get this to work. Please let me know if this no longer works, since I cannot test it myself as a lifetime Plex Pass owner!

What and Why

Plex is a self-hosted media server that lets you stream your owned (or downloaded, or otherwise acquired) media from other devices on the same network, through a web-based GUI (access via browser) or dedicated app. (Say, on a smart TV or Roku device.) Plex has a built-in feature to share your media library externally, but that requires opening a port on your router and forwarding it to the Plex server. Setting aside that port forwarding can be dangerous if you don’t know what you’re doing, it won’t work anyway if your home network is behind Carrier-Grade Network Address Translation, or CGNAT. Many ISPs use this, and so many self-hosters may find themselves unable to expose their services.

Although I previously wrote about how to expose Plex through CGNAT with Cloudflare Tunnel, it’s against their terms of service, so I don’t use that method anymore and suggest you don’t either. The method I explain in this post has a few extra steps, but it does not run afoul of any service provider’s rules.

Note: The following is ONLY for exposing Plex to other users you’ve shared libraries with, and is not required if you’re trying to access your own Plex server. For that, a VPS is not required, just install Tailscale on the Plex server and on whatever external device you want to access Plex from. An external VPS (free or otherwise) is only necessary to expose Plex to other users without them needing to run Tailscale themselves!

What we’ll be setting up is this:

Pre-Requisites

First of all, you should be comfortable using the terminal, because we’ll be doing quite a bit through command line. (Ubuntu specifically, since that’s the distribution I used for OCI from the options available.)

The method I explain here requires you to own a domain — it may be possible to instead use something like DuckDNS or NoIP, but I have not tried it. I’ll also be using Cloudflare for DNS, but that’s just my personal preference — feel free to use another DNS provider.

Finally, you’ll need a Plex server already set up. (And I’ll assume it’s running in Linux or as a Docker container.) I won’t go into how to do that here, see this post for instructions on running Plex as a Docker container.

Create OCI account

We’ll be using a free-tier VM from Oracle Cloud Infrastructure (OCI) — specifically, an E2 Micro instance which runs on a single-core AMD OCPU, has 1 GB of memory and a 0.48 Gbps connection, more than enough for streaming even 4K content through Plex. You can run TWO of these VMs totally free.

In addition to two AMD E2 Micro instances, the OCI free tier includes one Ampere A1 Flex instance with up to 4 Arm OCPUs, up to 24 GB of memory, and a connection of 1 Gbps per OCPU. (These are the total limits, you can also split it with two A1 Flex instances with 2 OCPUs and 12 GB of memory, or 4 OCPUs with 6 GB of memory each, etc.)

It may be preferable to use the A1 Flex instance for this instead of the E2 Micro if you want faster connection for more concurrent remote streams, if sharing your library with more than 1 or 2 users. However, you will most likely encounter an “out of capacity” error when trying to provision an A1 Flex instance of any shape and configuration, but if you upgrade your free tier account to Pay As You Go, the errors will disappear. I have confirmed this myself by upgrading my account. Just be sure to stay within the free limits noted above if you do this!

First, go to Oracle Cloud’s website and click Start for free to create your account. You will need a credit card, but only for verification purposes! As long as you stick to free tier and don’t upgrade, you won’t be charged.

Once your account is set up you’ll receive an email with the Cloud Account Name (which is your “tenant”) and Username. (The email you used to sign up.) You’ll need the Count Account Name to sign-in to OCI, after which you’ll be asked for the email address and password.

You’ll be asked if you want to Enable Secure Verification (MFA) which I strongly suggest you do. You’ll need a USB security key or to download and use the Oracle Authenticator app. It’s annoying to have to use another Authenticator app, but it’s worth the peace of mind.

Create a compute instance

Once you’re signed in to OCI, you’ll be at the Get Started page. Click on Instances under the Service Links.

On the next page, look for Compartment on the sidebar, and choose your Cloud Account Name from the dropdown menu.

Click the Create instance button and do the following:

  1. Name your instance to whatever you want. (Or leave the default generated name if you prefer.)

  2. Under the Placement section, just leave it default. Note that Ampere A1 instances can use any availability domain, while AMD E2 instances can only be created on the AD 3 availability domain. It should automatically default to the right one depending on your Shape choice, so you can ignore this.

  3. Scroll down to Image and shape. The default image is Oracle Linux 9 and the default shape is VM.Standard.A1 Flex — as explained above, this shape is nearly impossible to get and is almost always “out of capacity.” You can upgrade your Oracle account to Pay As You Go to make it accessible, but we’ll just go with E2.1.Micro instead which is also free-tier and is always available.

  4. First, if you don’t want to use Oracle Linux, click on Change image and choose a different one. I suggest Ubuntu, click on it and then choose the image name Canonical Ubuntu 24.04 Minimal, this is the latest version of Ubuntu and minimal means no extra bloat. (If using the Ampere A1 Flex shape, make sure you choose an image that says aarch64, since Ampere instances uses Arm CPUs — for E2.1.Micro which uses an AMD CPU, you need to choose an image that doesn’t say that.)

Choosing an Image while creating a compute instance in OCI.

  1. By choosing the Ubuntu 24.04 Minimal image, it should have automatically changed the shape to VM.Standard.E2.1.Micro — if it did not, click on Change shape and choose it. Also, make sure Instance type is Virtual machine and NOT bare metal. Click on the Select shape button to confirm your choice.

Choosing a Shape while creating a compute instance in OCI.

If provisioning an Ampere A1 instance, click on the arrow to the left of the Shape name to change the number of OCPUs and amount of memory — remember, the Ampere A1 free-tier includes 4 OCPUs and 24 GB of memory — when you increase the OCPU, it will also increase the memory automatically. Make sure you don’t go over these free limits!

  1. Leave the rest of the options as-is and click on the Next button at the bottom-right.

  2. Under the Security section, read what it says and make your choice. Personally I always leave the toggle for Shielded instance at OFF. The toggle for Confidential computing will always be grayed out because free-tier shapes don’t support it. Click on the Next button at the bottom-right.

  3. Under the Networking section, you can customize your VNIC name, subnet, IP addresses, etc. Or just leave it all to be automatically assigned as is the default, I usually do.

  4. Scroll down to Add SSH keys. You can upload your own public key, or you can let it generate a key pair for you, which is the default. If you choose the default option, make sure you download the public and private keys, you’ll need them to SSH into the VM! When done, click on the Next button.

SSH key settings when creating a compute instance in OCI.

  1. Under the Storage section, I just leave everything as default. A 46.6 GB boot volume is automatically included with the instance, but if you want it to be a larger size, turn on the toggle and choose a larger size. Note that the free-tier limit is 200 GB, which is shared between ALL instances, so be sure not to go over this! I usually just leave this as default. (I also never mess with the Boot Volume Performance and suggest you don’t either, unless you know what you’re doing.)

  2. If you’ve made a Block volume separately (this also falls under the combined 200 GB of space for all instances) you can attach it here. I don’t use block volumes, and it’s not necessary for our purposes here, so just leave it alone. Click on Next.

  3. Under the Review section, you’ll see all the details of your choices made thus far. If everything looks good, click on the Create button at the bottom-right.

Once the instance is fully provisioned and shows Running, you’re good to go. Click on it and look for Public IPv4 address, take note of this!

SSH into instance

We’ll assume you generated a key pair and downloaded the private key to your Downloads folder. In your Linux terminal, or if using Windows in Powershell or Windows Terminal, use the following command: (Obviously, use the correct filename of your SSH key, it’ll have the date you created it on in the name. You can rename this key file if you want.)

ssh -i ~/Downloads/ssh-key-2024-01-30.key ubuntu@<Instance Public IP>

For the future, you should create or edit the ~/.ssh/config file, and add in something like the following:

Host oracle
    HostName <Instance Public IP>
    IdentityFile ~/Downloads/ssh-key-2024-01-30.key
    User ubuntu

Once you’re in, let’s make sure everything is up-to-date.

sudo apt update && sudo apt upgrade -y

We’re done in the Oracle instance for now, but we’ll be back soon.

Set up Tailscale

Go to the Tailscale website and create an account. This will create your Tailnet (private mesh network for all your Tailscale-connected devices) with your newly created account as the Owner and which you’ll manage through the web-based admin console.

Once you’ve got the account ready, use the following command in both the server where you’re running Plex and the Oracle instance:

curl -fsSL https://tailscale.com/install.sh | sh

Once it’s finished installing, use the command sudo tailscale up on both your server and the Oracle instance, go to the provided URLs and login to add both machines to your tailnet. Now go to the Tailscale admin console and you should see them both there.

Note that each machine running Tailscale has a unique Tailscale IP and hostname. We’ll need these later.

Configure DNS in Tailscale

On the admin console go to the DNS tab.

First, notice the Tailnet name is something auto-generated like tailfe8c.ts.net. You can keep this if you want, but instead we’ll change it to a “fun name” that is more human-readable and easier to remember. You can’t just type one in, you choose from ones generated by Tailscale.

Click the Rename tailnet… button and follow the prompts. You can keep reloading until you find a fun name you like. For future examples, we’ll assume your tailnet name is cyber-sloth.

Scroll down to the end of the page and click the Enable HTTPS button. Now we can provision TLS certificates for machines in your tailnet, so that you can reach them at https://<name>.cyber-sloth.ts.net.

In the terminals for each machine — the Plex server and the Oracle instance — use this command to generate the certificates:

sudo tailscale cert <name>.cyber-sloth.ts.net

From here on out we’ll assume the Plex sever is plex.cyber-sloth.ts.net and the Oracle instance is oracle.cyber-sloth.ts.net.

Add and configure domain in Cloudflare

Create your free Cloudflare account if you haven’t already. If you bought a domain on Cloudflare, you can skip to the next section since it is auto-configured already. If your domain is from another registrar, we’ll need to add it to Cloudflare:

  1. On the Cloudflare dashboard Account Home, click the + Add a domain button.

  2. Enter your domain, leave Quick scan for DNS records selected, and click Continue.

Adding a domain to Cloudflare.

  1. Click on the Free plan at the bottom and click Continue.

Cloudflare free plan.

  1. You’ll see your DNS records, if there are any. Don’t worry about this right now and click on the Continue to activation button.

DNS management page.

  1. You’ll see a pop-up window saying you should set your DNS records now, click on Confirm.

Add DNS records pop-up.

  1. Now you’ll be provided some instructions to update the nameservers on your domain’s registrar, open a new tab and follow those instructions. Once you’ve added the Cloudflare nameservers at your registrar, go back to Cloudflare and click on Continue.

  2. Now you’ll have to wait a few minutes for the changes to propagate, then click on Check nameservers and reload the page. If it’s still shows Pending next to the domain at the top, just keep waiting and reload again after a few more minutes.