tech · privacy · tutorial3 min
How I replaced ngrok with a free, self-hosted alternative
A guide to setting up frp as a self-hosted ngrok replacement with custom subdomains, parallel tunnels, and zero monthly fees.
If you've ever needed to expose a local server to the internet—maybe for testing webhooks, sharing a demo with a client, or debugging a mobile app—you've probably used ngrok. It's great. It just works.
But here's the thing: ngrok's free tier is limited. You get random URLs that change every session, and if you want custom subdomains or multiple tunnels at once, you're looking at a paid plan. For a tool I use occasionally, that felt like overkill.
So I went looking for a self-hosted alternative. Enter frp.
#What is frp?
frp is an open-source reverse proxy that lets you expose local servers behind a NAT or firewall to the internet. It supports TCP, UDP, HTTP, and HTTPS, with features like custom domains, SSL termination, and authentication.
The setup is straightforward: you run frps (the server) on a VPS, and frpc (the client) on your local machine. The client connects to the server, and traffic flows through.
#Setting up the server
I wanted something I could deploy easily on my VPS using Coolify, so I wrapped the frp Docker image with some custom configuration. The repo is here: joaopcm/frp-docker-deployment.
The server needs three environment variables: FRP_TOKEN, DASHBOARD_USER, and DASHBOARD_PWD.
In Coolify, you configure the domains field to route traffic through Traefik. Only port 7000 needs direct exposure for client connections. Everything else goes through Traefik with automatic SSL.
#The client: a simple shell command
I didn't want to manage config files every time I needed a tunnel. So I added a custom tunnel command to my shell that:
- Generates a temporary frp config file
- Pulls the auth token from 1Password (so it's never hardcoded)
- Starts the tunnel
Now I can just run:
tunnel 3000 app1And my localhost:3000 is instantly available at https://app1.frp.jopcmelo.dev with SSL and a custom subdomain. I can run app1, app2, and app3 in parallel without paying a cent.
#Why this setup works for me
- Free: No monthly fees, just VPS costs I'm already paying
- Custom subdomains: Predictable URLs I can share without regenerating
- Parallel tunnels: Multiple services exposed at once
- Secure: Token auth via 1Password, SSL via Cloudflare/Traefik
- Fast: frp is lightweight and performant
Is it more setup than just running ngrok http 3000? Yes. But it's a one-time investment that pays off if you tunnel regularly.
#Wrapping up
If you're tired of ngrok's limitations and already have a VPS, frp is worth a look. The Docker deployment makes it easy to get started, and once you have a shell alias, the day-to-day experience is just as smooth.
Happy tunneling!
Plot twist: Right after I finished writing this, Rehan casually mentioned he uses Tailscale Funnel—which has automatic SSL/HTTPS built-in. I always thought Tailscale was just a VPN. Turns out you can skip most of this setup and just use Tailscale with much higher limits than ngrok's free tier. So... you probably don't need any of this. But hey, at least I learned something.