frp (Fast Reverse Proxy) instead of ngrok

Sunday, 18 August 2024

Intro

(feel free too skip it if you’re here just for the config)

In recent weeks, I’ve been spending a lot of time building on the backend (and like it a lot!). After the initial spike where I was running stuff on my laptop, time came to actually expose the server to the outside world, so that my friend’s frontend (hosted on Vercel) will be able to reach it.

Ah, the age old question, once again. How to expose my thing to the world?

A minute or two to get the container up and running on Google Cloud Run seems to not be that long, but compared to ~5 seconds it takes to rebuild my server image locally, it’s a night and day difference.

So, I reached for the old, good ngrok – or is it? Well, last time I used it a few years ago, it was this cute little tool. Now it seems to have grown into something much bigger, that I don’t care enough about to understand, and gives me low-key enshittification vibes. Also, I ain’t paying $10/month for stable IP address just to tunnel public traffic to my laptop.1

Meet frp

I went on the lookout for a free, self-hosted alternatives, and quickly found the frp project (Fast Reverse Proxy). It was exactly what I needed.

To get started, download the latest frp release for your machine from here. Once you unzip, you should find two binaries in there: - frps - Fast Reverse Proxy server - frpc - Fast Reverse Proxy client

It was mildly annoying to me is that neither frps nor frpc are available on apt and Homebrew. I’ll try to get frp on Homebrew when I’ll be sufficiently bored and free.

Config

Armed with knowledge from frp’s README and some two blogposts, I started configuring it on my VPS with stable IP.

On the public server

Do this setup on the server that has a stable IP address. For simplicity, I decided to keep things in my user’s $HOME:

~
├── frp/
│   ├── frps
│   └── frps.toml
│   └── frps.service

frps is the binary, and frps.toml is literally a single line:

bindPort = 7000

This is frps.service:

[Unit]
Description=Fast Reverse Proxy server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=%h/frp/frps -c %h/frp/frps.toml
Restart=on-failure
RestartSec=10s

[Install]
WantedBy=default.target

Heads up: to be able to use %h in systemd unit file – which expands to /home/charlie (assuming your user is charlie) – this service has to run as a user service, not a system one. Also, you have to enable lingering ($ loginctl enable-linger) to avoid our service being killed on user logout.

With that out of the way, let’s register the service in systemd:

cp ~/.frp/frps.service ~/.config/systemd/user

Enable it to always run on startup:

systemctl --user enable frps.service

And start it right now:

systemctl --user start frps.service

Even though I’m not a fan of systemd, I have a slight preference for it over containers when it comes to self hosting, so this config is systemd-based. The simpler, the better.

On the local server

Do this setup on the server that cannot be reached from the public internet (e.g. because of being behind NAT or firewalls) – such as your laptop.

frpc.toml

I put this file in ~/.config/frp.

serverAddr = "106.248.27.132"
serverPort = 7000 # connect to frp server running on this port

[[proxies]]
name = "my-tcp-proxy"
type = "tcp"
localIP = "127.0.0.1" # my server runs on localhost
localPort = 2137 # my server runs on this port on localhost
remotePort = 8080 # this port will be exposed on public server

Then run the frp client – frpc – and specify the config file:

frpc -c ~/.config/frp/frpc.toml

Now open a new terminal tab and run your server, listening on the port you specified in fprc.toml (2137 in our case). If you don’t have any backend at hand, you can use Eli Bendersky’s static fileserver:

go run github.com/eliben/static-server@latest --addr localhost:2137

Test it

Assuming IP of your server is 106.248.27.132, open http://106.248.27.132:8080 in the browser.

You should be able to see contents of the directory you run static-server in.

Wrapping up

The whole thing is hosted on the cheapest $5/month VPS from DigitalOcean, and it works perfectly. Now, whenever I start developing my backend locally, but want to also have it exposed to the grand World Wide Web, I use frpc.

I didn’t bother to set up TLS, so it’s only http for now, but that’s fine for my use case. I might get to it in the future.


2024-08-18 – frp (Fast Reverse Proxy) instead of ngrok
2024-08-16 – Cirrus CI is the best CI system out there
2024-08-14 – Going to Berlin for Droidcon/Fluttercon
2024-06-25 – I was awarded Google Open Source Peer Bonus
2024-06-04 – My journey to Google I/O ’24
2024-05-11 – GitHub Actions beg for a supply chain attack
2024-03-19 – Writing a custom Dart VM service extension (part 1)
2024-02-08 – On using smartphone for things that make sense
2023-11-30 – Semantics in Flutter - under the hood
2023-11-25 – Flutter Engine notes
2023-09-17 – Creating and managing Android Virtual Devices using the terminal
2023-05-27 – Suckless Android SDK setup
2023-05-26 – Let’s start over
2023-05-21 – Short thought on “The Zen of Unix”
2023-05-15 – Notes about “flutter assemble”
2019-01-07 – Google Code-in 2018


  1. If you want ngrok-like experience and don’t want to self-host, check out Cloudflare Tunnels. Thanks Luis Duarte for pointing this out.↩︎