What Is Tunnelto and How to Self-Host It on Ubuntu with Nginx & HTTPS
Modern development lives in an awkward space: your app runs locally, but the outside world needs access. Webhooks, mobile testing, third-party callbacks, and client demos all demand a public URL. This is exactly where Tunnelto shines.
What Is Tunnelto?
Tunnelto is an open-source tunneling tool that exposes your local development server to the internet through a secure tunnel. Think of it as a developer-friendly alternative to services like ngrok, but with one crucial difference: you can self-host it.
At a high level, Tunnelto works like this:
- You run a Tunnelto server on a public machine (VPS or cloud server).
- You run a Tunnelto client on your local machine.
- The client opens a secure tunnel to the server.
- External requests hit the server and are forwarded to your local app.
This makes Tunnelto ideal for:
- Webhook testing (Stripe, PayPal, GitHub, etc.)
- Local WordPress, Laravel, or Node.js development
- Client previews without deploying code
- Teams that want control over infrastructure
Why Self-Host Tunnelto?
Self-hosting is not about distrust; it is about control.
- No usage limits
- No vendor lock-in
- Custom domains and subdomains
- Your data never leaves your infrastructure
If you already manage servers, self-hosting Tunnelto is surprisingly lightweight.
Prerequisites
Before starting, make sure you have:
- An Ubuntu 20.04+ VPS
- A domain name (example:
example.com) - A subdomain pointing to your server (example:
tunnel.example.com) - Root or sudo access
- Nginx installed
Step 1: Install Tunnelto Server on Ubuntu
Tunnelto is distributed as a single binary.
# Download Tunnelto server
curl -L https://github.com/agrinman/tunnelto/releases/latest/download/tunnelto-linux-amd64 \
-o tunnelto
# Make it executable
chmod +x tunnelto
# Move to a global path
sudo mv tunnelto /usr/local/bin/tunnelto
Verify installation:
tunnelto --help
Step 2: Run Tunnelto Server
Start the server on a private port (example: 3000):
tunnelto server --port 3000
For production, you should run this via systemd or a process manager like pm2.
Step 3: Configure Nginx as a Reverse Proxy
Create a new Nginx config file:
sudo nano /etc/nginx/sites-available/tunnelto
Add the following configuration:
server {
listen 80;
server_name tunnel.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/tunnelto /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 4: Enable HTTPS with Let’s Encrypt
Secure tunnels deserve secure transport.
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d tunnel.example.com
Once complete, your Tunnelto server will be available at:
https://tunnel.example.com
Step 5: Install Tunnelto Client (Local Machine)
Linux
curl -L https://github.com/agrinman/tunnelto/releases/latest/download/tunnelto-linux-amd64 \
-o tunnelto
chmod +x tunnelto
sudo mv tunnelto /usr/local/bin/tunnelto
macOS
brew install tunnelto
Windows (PowerShell)
winget install tunnelto
Step 6: Expose Your Local App
Assume your local app runs on port 8000.
tunnelto --host https://tunnel.example.com http://localhost:8000
Tunnelto will output a public URL pointing to your local server. Any request to that URL is forwarded directly to your machine.
Security Notes
- Use HTTPS only in production
- Restrict access using firewall rules if possible
- Rotate domains or tokens for sensitive testing
A tunnel is a doorway. Treat it like one.
Final Thoughts
Tunnelto sits at a sweet intersection of simplicity and power. It does one thing—expose local services—and does it cleanly. By self-hosting, you gain reliability, privacy, and freedom without adding much complexity.
For developers working with APIs, webhooks, WordPress plugins, Laravel apps, or SaaS integrations, a self-hosted Tunnelto setup becomes a quiet but indispensable tool in the workflow.
Adding Authentication to Tunnelto
A public tunnel without authentication is like leaving your dev server unlocked. Tunnelto supports authentication tokens that allow only trusted clients to connect. This is especially important when your Tunnelto server is exposed via a real domain and HTTPS.
Generate an Authentication Token
On your Tunnelto server, generate a strong token:
openssl rand -hex 32
Example output:
9c4f1c2d3b0a7f91e8d2c5a4c93a8f1cbbd3e7f6e9c1b8a5d2e4f7a1b9d
Run Tunnelto Server with Authentication
Restart the Tunnelto server with the token enabled:
tunnelto server \
--port 3000 \
--auth-token 9c4f1c2d3b0a7f91e8d2c5a4c93a8f1cbbd3e7f6e9c1b8a5d2e4f7a1b9d
Only clients that present this token will be allowed to establish tunnels.
Connect from Client with Authentication
On the developer machine:
tunnelto \
--host https://tunnel.example.com \
--auth-token 9c4f1c2d3b0a7f91e8d2c5a4c93a8f1cbbd3e7f6e9c1b8a5d2e4f7a1b9d \
http://localhost:8000
If the token is missing or invalid, the connection is rejected immediately. This keeps random scanners and curious bots out of your tunnels.
Custom Subdomains per Developer
One of the quiet superpowers of self-hosted Tunnelto is predictable URLs. Instead of random subdomains, each developer can have their own permanent subdomain.
Example structure:
sabuj.tunnel.example.comalice.tunnel.example.combob.tunnel.example.com
This is extremely useful for:
- Team-based development
- Persistent webhook URLs
- Client demos that should not break
DNS Configuration
Create a wildcard DNS record:
Type: A
Host: *.tunnel
Value: YOUR_SERVER_IP
This allows any subdomain under tunnel.example.com to resolve to your server.
Nginx Wildcard Server Configuration
Update your Nginx server block:
server {
listen 80;
server_name *.tunnel.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
Reissue HTTPS certificates with wildcard support (requires DNS validation):
sudo certbot certonly \
--dns-cloudflare \
-d "*.tunnel.example.com"
Developer-Specific Tunnel
Each developer connects using their assigned subdomain:
tunnelto \
--host https://sabuj.tunnel.example.com \
--auth-token DEVELOPER_TOKEN \
http://localhost:8000
Tunnelto automatically maps incoming requests for that subdomain to the correct tunnel.
No collisions. No confusion.
Operational Best Practices
- Use a unique auth token per developer
- Rotate tokens periodically
- Restrict Tunnelto server ports using a firewall
- Run Tunnelto server as a systemd service
At this point, Tunnelto stops being a simple tunnel and starts behaving like infrastructure.
Lightweight, quiet infrastructure—but infrastructure nonetheless.
Need to build a Website or Application?
Since 2011, Codeboxr has been transforming client visions into powerful, user-friendly web experiences. We specialize in building bespoke web applications that drive growth and engagement.
Our deep expertise in modern technologies like Laravel and Flutter allows us to create robust, scalable solutions from the ground up. As WordPress veterans, we also excel at crafting high-performance websites and developing advanced custom plugins that extend functionality perfectly to your needs.
Let’s build the advanced web solution your business demands.