Skip to content

Instantly share code, notes, and snippets.

@br0kenpixel
Last active October 18, 2025 08:02
Show Gist options
  • Select an option

  • Save br0kenpixel/a309780669350f9a2207ecca8cacd513 to your computer and use it in GitHub Desktop.

Select an option

Save br0kenpixel/a309780669350f9a2207ecca8cacd513 to your computer and use it in GitHub Desktop.
Bypass CGNAT for Plex remote access using Wireguard and a VPS

Introduction

The goal of this guide is to show you how to bypass CGNAT in order to allow remote access for your Plex server.

Why not a reverse proxy?

Unlike other self-hostable apps, such as Jellyfin, qBittorrent, Vaulwarden, etc., Plex needs to be tied to a public IP, and it expects port-forwarding to "just work."

Requirements

  • A Plex server running inside Docker, using an image like lscr.io/linuxserver/plex, configured using a Compose file.
  • A VPS with a public static IPv4 address.
    • IPv6 should theoretically work too, but I haven't tried.

Your VPS server should ideally not be from Hetzer. Plex started banning their IPs after finding out that people host Plex servers with pirated content on Hetzner. Also, there are several reports on Reddit claiming that Hetzner is randomly deleting peoples accounts without notice.

As for Oracle Cloud, while they offer lifetime free VPS', I could not get this to work for some reason. It appears that their Firewall restrictions are too strict and buggy.

For this guide, I will be using a cheap VPS. The server is running Debian 13. There is no firewall configuration.

How?

We'll start by setting up a WireGuard VPN server on the VPS, and then use Gluetun inside a Docker container to connect the Plex container to the internet. This way, Plex will see the VPS' public IP, instead of your own.

Afterwards, using some firewall (iptables) roules, we'll make sure that Plex can route traffic to/from the VPS into/from the container. The final step is to configure Plex to recognize local devices connecting from your local subnet.

flowchart LR
    subgraph Home network
        subgraph Plex stack
            Plex<-->Gluetun
        end

        Plex<-->|Direct|LDEV[Local device]
    end

    subgraph VPS network
        Gluetun<-->WGS[WireGuard server]
    end

    WGS<-->INTERNET[Public internet]
Loading

VPS setup

I'll assume you already have a VPS running and are SSH'd into it. Before we begin, you should update the system.

apt update && apt upgrade --yes

Now, install WireGuard and create a client using this script. This process is mostly automated.

wget https://git.io/wireguard -O wireguard-install.sh && sudo bash wireguard-install.sh

I'll assume you left all parameters as default (so port 51820, dns 1.1.1.1). When the script finishes, you'll see a QR code in your terminal, and most importantly, a client configuration file will be generated for you.

Finished!

The client configuration is available in: /root/nas-plex.conf
New clients can be added by running this script again.

At this point, WireGuard is installed and running. Now, open your WireGuard server's configuration file using your favorite text editor, I'll use nano. Note that if you're not root, you'll likely need sudo for this.

nano /etc/wireguard/wg0.conf

This file should look like this:

# Do not alter the commented lines
# They are used by wireguard-install
# ENDPOINT [REDACTED]

[Interface]
Address = 10.7.0.1/24
PrivateKey = [REDACTED]
ListenPort = 51820

# BEGIN_PEER [REDACTED]
[Peer]
PublicKey = [REDACTED]
PresharedKey = [REDACTED]
AllowedIPs = 10.7.0.2/32
# END_PEER [REDACTED]

We need to configure some firewall rules, specifically, to route traffic between the client 10.7.0.2 and the VPS's public IP. To do this, let's add the following lines to the [Interface] configuration (right under ListenPort.)

PostUp = iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 32400 -j DNAT --to-destination 10.7.0.2; iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 1.2.3.4

PostDown = iptables -t nat -D PREROUTING -p tcp -i eth0 --dport 32400 -j DNAT --to-destination 10.7.0.2; iptables -t nat -D POSTROUTING -o eth0 -j SNAT --to-source 1.2.3.4

Source: https://github.com/mochman/Bypass_CGNAT/wiki/Digital-Ocean-(Manual-Installation)#b-forward-specific-ports

Make sure to:

  1. Change the port 32400 to whatever port you want to run Plex on. I'll leave the default port.
  2. Change all references of 10.7.0.2 to your WireGuard client's IP. This is the IP address specified in AllowedIPs under [Peer] in your server configuration. (See above)
  3. Change all references of 1.2.3.4 to your VPS's public IP.
  4. Save and close the file.

After this, you may restart the WireGuard server and optionally your VPS too.

systemctl restart [email protected]

Client setup

I'll assume you're already SSH'd into your home server and are in the same working directory as your docker-compose.yml for Plex.

Now, you have to add a Gluetun service to your Compose file like so:

gluetun:
  image: qmcgaw/gluetun
  container_name: gluetun
  cap_add:
    - NET_ADMIN
  devices:
    - /dev/net/tun:/dev/net/tun
  ports:
    - 32400:32400
    - 1900:1900/udp
    - 5353:5353/udp
    - 8324:8324
    - 32410:32410/udp
    - 32412:32412/udp
    - 32413:32413/udp
    - 32414:32414/udp
    - 32469:32469
  environment:
    - VPN_SERVICE_PROVIDER=custom
    - VPN_TYPE=wireguard
    - WIREGUARD_ADDRESSES=10.7.0.2
    - WIREGUARD_PRIVATE_KEY=[REDACTED]
    - WIREGUARD_PUBLIC_KEY=[REDACTED]
    - WIREGUARD_PRESHARED_KEY=[REDACTED]
    - WIREGUARD_ENDPOINT_IP=[REDACTED]
    - WIREGUARD_ENDPOINT_PORT=51820
    - FIREWALL_VPN_INPUT_PORTS=32400
    - FIREWALL_OUTBOUND_SUBNETS=192.168.0.0/24
  volumes:
    - ./gluetun:/gluetun
  restart: unless-stopped

You'll have to open the WireGuard client configuration file we generated before, using the WireGuard install script. Fill out WIREGUARD_PRIVATE_KEY, WIREGUARD_PUBLIC_KEY, WIREGUARD_PRESHARED_KEY, WIREGUARD_ENDPOINT_IP and FIREWALL_OUTBOUND_SUBNETS.

WIREGUARD_ENDPOINT_IP is your VPS's public IP. FIREWALL_OUTBOUND_SUBNETS is your local subnet. Note you'll have to also change WIREGUARD_ADDRESSES if you have a different address assigned to your client, WIREGUARD_ENDPOINT_PORT if your VPN server is running on a different port, FIREWALL_VPN_INPUT_PORTS if you chose a different port for Plex.

Lastly, add network_mode: "service:gluetun" to your Plex service configuration. You can now save and close the file. You may start your Plex stack now.

docker compose up -d

Check the logs using docker compose logs gluetun, and if everything is OK, you should see a You are running on the bleeding edge of latest! message at the end of the log. I'll assume it's working for you.

Now, open your Plex web UI using your server's local IP, so for me, that would be http://192.168.0.178:32400. Once you're in, go to the settings, and make sure your server is selected, then go to Remote Access. If you don't see your server here, you might have to remove it from your account and re-claim it. You may manually set the port and enable remote access. You should see your VPS's public IP and the Docker container's local IP.

I'll assume that remote access works now.

Ensuring proper local access

In your Plex server's settings go to Network and under LAN Networks, specify your local subnet, WireGuard client's IP subnet, and the Docker container's internal IP. You have to separate these subnets with a column. So for example, 192.168.5.1/24,10.7.0.2/32,172.20.0.1/24.

Next, under Custom server access URLs add the Plex server's local IP, the WireGuard client's IP, the container's internal IP, and the VPS's public IP. So for example, http://192.168.1.222:32400,http://1.2.3.4:32400,http://10.7.0.2:32400,http://172.20.0.2:32400.

Save the changes and try it out.

(Optional) Securing access

Under your Plex server's Network settings, set Secure connections to Required. Note that even though the public-facing web UI only runs on HTTP, as long as you're accessing the server over app.plex.tv instead, (which is basically always unless you're manually opening http://1.2.3.4:32400 in your browser) then Plex will handle encryption on it's own. Note that changing this setting to Required will result in blocking local connections to your server's web UI! So you may want to keep this on Preferred instead.

Notes

This works, but not always

I have noticed that with certain VPS hosting services, this method does not work for whatever reason. The most stable seems to be MassiveGrid, and Serverio. As for Oracle Cloud, I couldn't get it to work at all, but their firewall is quite restrictive and a pain to configure.

Traditional VPNs might not be sufficient

Traditional VPN providers like NordVPN, Mullvad, ProtonVPN, etc. will not work or be reliable. If you want to buy a VPN for this purpose, you need to make sure that it allows port forwarding with a static port. Most of these services are designed to generate a new port every time you connect.

Improving connection times and/or latency

You want to make sure that your VPN server is hosted as close to your location as possible to improve latency. This method will introduce noticable delays when performing certain operations, especially if the VPN connection is slow or creates high latency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment