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


self-hosting

I wrote before about securely exposing Plex for external access, 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. It works like a charm!

Sections

  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 on Cloudflare
  8. Set up reverse proxy
  9. Add ingress rules on OCI
  10. Configure Plex
  11. References

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 homelabbers may find themselves unable to expose their services.

Although there are other solutions to get across CGNAT, this one can be set up with fairly minimal effort and does not run afoul of any service provider’s rules.

What we’ll be setting up is this:

  • We will install Tailscale on the same server as Plex or, alternately, on another machine in the home network that will act as subnet router. (See this section Tailscale docs — for this guide, we’ll install Tailscale on the same server running Plex, so subnet routing isn’t necessary.)

  • We will create a free tier compute instance on Oracle Cloud Insfrastructure and install Tailscale on it, so it’s on the same tailnet as the Plex server. We’ll expose ports 80 and 443 to the internet on the VM, but only allowing access from specific IPs, and run a reverse proxy to forward web traffic to Plex.

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, although you can use your preferred distribution.)

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.1.Micro VM 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 free VMs totally free.

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.

Information

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. Scroll down to Image and shape, and click Edit.

  3. The default Shape (VM.Standard.A1.Flex) is nearly impossible to get, it’s always “out of capacity.” That’s fine, it’s overkill for this anyway. Click on Change shape.

  4. Make sure Instance type is Virtual machines.

  5. Under Shape series, choose Specialty and previous generation which falls under the always-free tier.

Choosing an Image and Shape while creating a compute instance in OCI.

  1. Under Shape name, check the box for (the only option) VM.Standard.E2.1.Micro. (Notice the “always-free eligible** tag.) Click the Select shape button at the bottom.

  2. The default image is Oracle Linux 8, but you can click Change image and choose one of the other always-free eligible images — Ubuntu (my suggestion) or CentOS.

Information

The rest of this guide assumes you chose Canonical Ubuntu 22.04 Minimal as your image.

SSH key settings when creating a compute instance in OCI.

  1. Scroll down to Add SSH keys. You can upload your own public key, or you can let it generate a key pair for you. If you choose the latter, make sure you save the private and private key so you can SSH into the VM!

  2. You can leave the rest as default, or change stuff around if you like. I’ll let you make those choices yourself. When ready, click the Create button at the bottom.

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. Use the following command:

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

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

Host oracle
    HostName <Instance-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 has a hostname and unique Tailscale IP. 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. Instead we’ll use 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.

Configure DNS on Cloudflare

Now we’ll go to Cloudflare to set up the external DNS, so they internet can go to your-domain.com and end up at the Oracle instance. Create a free Cloudflare account if you haven’t ready.

We’ll assume you need to add your domain to Cloudflare, but if you already did that you can skip ahead.

From the Cloudflare dashboard, do the following:

  1. On the sidebar, go to Websites and click the Add a site button.

Adding a site to Cloudflare.

  1. Enter your domain and click Add site, then click on the Free plan at the bottom and click Continue.

Cloudflare free plan.

  1. After waiting a few moments for the DNS quick scan, you should see your domain’s DNS records appear. Click on Continue.

  2. Cloudflare will now present you with the URLs to two nameservers, should be something like adam.ns.cloudflare.com. Leave this page open, we’ll come back to it.

  3. Login to the registrar that owns your domain, go into your domain’s DNS settings, and change the nameservers to both of the URLs provided by Cloudflare. (This is different for each domain registrar, you’ll need to figure out how to do that on your own.)

  4. Back in Cloudflare, click Done, check nameservers. It could take up to 24 hours for the change to propagate, but usually it will take less than an hour, and often less than 20 minutes. In the meantime, follow the Quick Start Guide.

  5. Leave Automatic HTTPS Rewrites checked as-is, and enable the checkbox for Always Use HTTPS.

  6. Leave Brotli on. On the summary, click Finished.

  7. You’ll be back at your site’s Overview. If you still see Complete your nameserver setup, you can try using the Check nameservers button. In my experience that makes the DNS changes propagate within a few minutes.

Once your DNS changes have taken effect, the Overview page will say: “Great news! Cloudflare is now protecting your site!”