How to securely expose Plex from behind CGNAT using Tailscale and a free Oracle VM
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
- What and Why
- Pre-Requisites
- Create OCI account
- Create a compute instance
- SSH into instance
- Set up Tailscale
- Configure DNS on Cloudflare
- Set up reverse proxy
- Add ingress rules on OCI
- Configure Plex
- 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.
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:
-
Name your instance to whatever you want. (Or leave the default generated name if you prefer.)
-
Scroll down to Image and shape, and click Edit.
-
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.
-
Make sure Instance type is Virtual machines.
-
Under Shape series, choose Specialty and previous generation which falls under the always-free tier.
-
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.
-
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.
The rest of this guide assumes you chose Canonical Ubuntu 22.04 Minimal as your image.
-
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!
-
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:
- On the sidebar, go to Websites and click the Add a site button.
- Enter your domain and click Add site, then click on the Free plan at the bottom and click Continue.
-
After waiting a few moments for the DNS quick scan, you should see your domain’s DNS records appear. Click on Continue.
-
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. -
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.)
-
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.
-
Leave Automatic HTTPS Rewrites checked as-is, and enable the checkbox for Always Use HTTPS.
-
Leave Brotli on. On the summary, click Finished.
-
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!”
With the domain set up in Cloudflare, we just need to add a DNS record:
-
On the sidebar go to Websites, choose your domain, then go to DNS -> Records.
-
Click on Add record.
-
For Type choose A from the dropdown menu.
-
For Name type in
your-domain.com
. -
For IPv4 address type in the Oracle instance public IP.
-
Under Proxy status toggle it off to DNS only.
Make sure NOT to leave it proxied. If you do, all traffic will go through Cloudflare’s CDN which we do not want. We’re only using Cloudflare to resolve our domain to the IP of the Oracle instance, nothing more!
- Leave TTL at Auto and click Save.
Next, we need to create an API token to edit the DNS config from third-party apps, we is necessary to get a HTTPS certificate in the reverse proxy later.
-
On the Cloudflare dashboard, click on Websites and choose your domain.
-
Under the DNS column on the right side of the page, scroll down to API and click on Get your API token.
-
Click the Create Token button.
-
Choose the Edit zone DNS template.
-
Under Zone Resources, it should already be set to Include and Specific zone — choose your domain from the dropdown menu and click Continue to summary.
-
On the next page, click Create Token.
-
Important! Copy your API token and save it somewhere, you won’t be shown it again.
Set up reverse proxy
Back in the Oracle compute instance, we’ll be setting up Docker to run Nginx Proxy Manager. If you know what you’re doing, feel free to use whatever reverse proxy you like, and run it however you like.
SSH into the instance and install Docker with the following command:
curl -fsSL https://get.docker.com | sh
We’ll use Docker Compose to run the reverse proxy container.
-
Create the data directory for the reverse proxy and change into it with
mkdir ~/nginxproxy && cd ~/nginxproxy
(This assumes you’re using the defaultubuntu
user.) -
Create the compose file with
touch compose.yml
and edit it withnano compose.yml
, copy & paste these contents into it:
services:
nginxproxy:
image: docker.io/jc21/nginx-proxy-manager:latest
container_name: nginxproxy
volumes:
- /home/ubuntu/nginxproxy:/data
- /home/ubuntu/nginxproxy/letsencrypt:/etc/letsencrypt
ports:
- 80:80
- 81:81
- 443:443
restart: always
- Once the compose file is ready, run it with
docker compose up -d
Once it’s up and running, we need to access the Nginx Proxy Manager GUI. I strongly suggest NOT opening port 81 on your instance. (I’ll talk about opening ports 80 and 443 in the next section.)
Instead, it’s safer to install Tailscale on your PC or tablet, connect to the Tailnet, then on a browser go to https://oracle.cyber-sloth.ts.net:81
. When you’re done just disconnect the Tailscale client, and only connect when you need to access the GUI. (You may want to disable the Tailscale client from starting up at boot.)
Once in the Nginx Proxy Manager GUI, login with the default [email protected]
and changeme
as the password. You’ll want to change that before anything else.
Click on Users on the top nav bar, then to the right of the Administrator entry click the three dots. Choose Edit Details to change the email and Change password to change password. Log out and back in with the new credentials.
Now to add the configuration for our custom domain:
-
On the Dashboard, click Proxy hosts and then Add proxy host.
-
Type in
your-domain.com
under Domain Name. -
Leave the Scheme as http.
-
Type in
plex.cyber-sloth.ts.net
under Forward Hostname/IP. -
Type in
80
under Forward Port. -
Toggle on Websockets Support, but leave the other two off.
-
Go to SSL tab and choose Request a new SSL Certificate from the dropdown menu.
-
Enable the toggle for Use a DNS Challenge.
-
Choose Cloudflare as DNS Provider from the dropdown menu.
-
In the credentials file content, delete the numbers after
dns_cloudflare_api_token=
and add in your API token instead. -
Enable the toggle to agree to the Let’s Encrypt TOS and click Save.
Give it a minute or two for Let’s Encrypt to provision the TLS certificate, and the proxy host will then be created.
Add ingress rules on OCI
Now to actually allow internet connections to the Oracle instance, we need to add ingress rules in the OCI dashboard. You really have two options here:
-
Allow access from the entire internet and rely on authentication to block anyone that shouldn’t have access.
-
Allow access only from specific IPs, including yours, and block everyone else. (If they go to your domain they will get a 403 error.) I strongly suggest this.
Under Instances, click on your instance, and under Instance details click on the link for Virtual Cloud Network, it should be something like vcn-20221216-2035
.
In Subnets click on the only choice, something like subnet-20221216-2035
. Finally, click on the Default Security List.
-
Click Add Ingress Rules
-
Leave the source type as CIDR.
-
Under Source CIDR type an IP you want to allow in this format
123.45.678.90/32
. -
Leave the IP Protocol as TCP.
-
Leave the Source Port Range as Optional.
-
Set the Destination Port Range as
80
. -
Click on + Another Ingress Rule, do the same but use
443
as Destination Port Range.
Repeat the above steps for each IP address you want to allow access. You want to have ingress rules that allow each IP to access both ports 80
and 443
.
Now if every step up till now has been done correctly, you should reach Plex when you go to https://your-domain.com
. (You added ingress rules for your own IP, right?)
Configure Plex
One last thing! Although the allowed IPs can now reach Plex and stream your library by logging in to the Plex web UI at https://your-domain.com
, using Plex apps will not work until you do the following:
-
On the Plex web UI, go to Settings by clicking on the wrench icon at the top-left.
-
On the sidebar, scroll down to Settings and click Network.
-
Next to Secure connections, choose Preferred from the downdown menu.
-
(Optional) Scroll down and enable the checkbox for Treat WAN IP as LAN Bandwitdh.
-
Make sure to leave disabled the checkbox for Enable Relay.
-
Under Custom server access URLs type in
https://your-domain.com
. (Make sure to include the HTTPS!) -
At the bottom of the page, click the Save changes button.
Now your external users can access your library through their Plex apps too.
Related Articles
How to securely expose Plex from behind CGNAT using Tailscale and a free Oracle VM
How to remotely access your home server from anywhere using Tailscale