Documentation / Multi-Port Tunneling

Multi-Port Tunneling

Expose multiple services on a single hostname with HTTP and TCP on different ports

Overview

Multi-port tunneling allows you to expose multiple services (HTTP, TCP, databases, APIs) under a single tunnel hostname. This is perfect for full-stack applications where you need to expose both your web frontend and backend services like databases.

How It Works: Unlimited HTTP Services = 1 Tunnel

Your Machine
:3000 React
:4000 Express API
:5000 Admin Panel
:8080 Docs Site
1 gRPC Stream
Counts as 1 Tunnel
Public URLs
/ React
/api Express
/admin Admin
/docs Docs
myapp.stunl.io

All HTTP services share one tunnel. Path-based routing happens server-side.

Key Benefits:

  • One tunnel ID for multiple services
  • HTTP on standard ports (80/443) + TCP on custom ports
  • Perfect for full-stack development
  • All traffic under one hostname
How Tunnels Are Counted
Protocol Tunnel Counting
HTTP All ports share 1 parent tunnel
WebSocket Shares the HTTP parent tunnel
TCP Each port = 1 child tunnel
UDP Each port = 1 child tunnel

Example: 1 multiport tunnel with 20 HTTP services (1 tunnel) + 3 TCP ports (3 tunnels) = 4 tunnels toward your limit.

Quick Start

Basic Multi-Port Tunnel (HTTP + PostgreSQL)
# Start your services locally
$ npm run dev              # HTTP server on port 3000
$ docker run -p 5432:5432 postgres  # PostgreSQL on port 5432

# Create multi-port tunnel
$ stunl -id myapp -ports "web:3000:http,db:5432:tcp"

  ● SIMPLETUNNEL

  ╭── ◎ ── HTTP web
  │   HTTPS    https://myapp.stunl.io
  │   HTTP     http://myapp.stunl.io
  │   Local    localhost:3000

  ╭── ⇄ ── TCP db
  │   Connect  telnet myapp.stunl.io 15432
  │   Local    localhost:5432

Multiple TCP Ports

Add multiple services by extending the -ports comma-separated list:

HTTP + PostgreSQL + Redis
# Start your services
$ npm run dev              # Port 3000
$ docker run -p 5432:5432 postgres
$ docker run -p 6379:6379 redis

# Create tunnel with multiple TCP ports
$ stunl -id myapp -ports "web:3000:http,db:5432:tcp,cache:6379:tcp"

  ● SIMPLETUNNEL

  ╭── ◎ ── HTTP web
  │   HTTPS    https://myapp.stunl.io
  │   HTTP     http://myapp.stunl.io
  │   Local    localhost:3000

  ╭── ⇄ ── TCP db
  │   Connect  telnet myapp.stunl.io 15432
  │   Local    localhost:5432

  ╭── ⇄ ── TCP cache
  │   Connect  telnet myapp.stunl.io 16379
  │   Local    localhost:6379

Real-World Example: Full-Stack App

Next.js + PostgreSQL + Redis
# 1. Start all services locally
$ npm run dev                          # Next.js on 3000
$ docker compose up postgres redis     # DB on 5432, Redis on 6379

# 2. Create multi-port tunnel with custom hostname
$ stunl -id myapp -ports "app:3000:http,db:5432:tcp,cache:6379:tcp"

  ● SIMPLETUNNEL

  ╭── ◎ ── HTTP app
  │   HTTPS    https://myapp.stunl.io
  │   HTTP     http://myapp.stunl.io
  │   Local    localhost:3000

  ╭── ⇄ ── TCP db
  │   Connect  telnet myapp.stunl.io 15432
  │   Local    localhost:5432

  ╭── ⇄ ── TCP cache
  │   Connect  telnet myapp.stunl.io 16379
  │   Local    localhost:6379

# 3. Update your .env to use public endpoints
DATABASE_URL="postgresql://user:pass@myapp.stunl.io:15432/mydb"
REDIS_URL="redis://myapp.stunl.io:16379"

# 4. Share with your team
Frontend: https://myapp.stunl.io
Database: myapp.stunl.io:15432
Cache:    myapp.stunl.io:16379

Reserved Ports: Keep Your Port Numbers

Notice those port numbers (15432, 16379)? By default, TCP/UDP ports are randomly assigned from range 10001-19999 each time you connect. With reserved ports, you lock in specific port numbers that persist across reconnects.

# Use @port syntax to request a reserved port

$ stunl -ports "web:3000:http,db:5432:tcp@15432,cache:6379:tcp@16379"

Why reserve ports?

  • Stable connection strings - no need to update configs when you reconnect
  • Team collaboration - share once, works forever
  • Exclusive access - your reserved port is yours even when offline

Pro includes 2 reserved ports free (max 10). Reserve ports at portal.stunl.com first, then use them with @port syntax.

Connection Examples

Once your tunnel is running, connect to your TCP services using the assigned public port:

PostgreSQL

# Using psql
$ psql -h myapp.stunl.io -p 15432 -U myuser -d mydb

# Connection string
postgresql://myuser:mypass@myapp.stunl.io:15432/mydb

# In your app (Node.js)
const { Pool } = require('pg');
const pool = new Pool({
  host: 'myapp.stunl.io',
  port: 15432,
  user: 'myuser',
  password: 'mypass',
  database: 'mydb'
});

MySQL

# Using mysql client
$ mysql -h myapp.stunl.io -P 13306 -u root -p

# Connection string
mysql://root:password@myapp.stunl.io:13306/mydb

Redis

# Using redis-cli
$ redis-cli -h myapp.stunl.io -p 16379

# Connection string
redis://myapp.stunl.io:16379

MongoDB

# Using mongosh
$ mongosh "mongodb://myapp.stunl.io:17017/mydb"

# Connection string
mongodb://myapp.stunl.io:17017/mydb

Use Cases

Full-Stack Development

Share your entire application stack (frontend + backend + database) with one URL.

Database Access for Remote Teams

Let remote developers connect to your local database for debugging without VPN.

Microservices Testing

Expose multiple microservices on different TCP ports for integration testing.

Demo Applications

Show clients a complete working application with real data and database connections.

Pro Tip: HTTP Password Protection

Add password protection to your HTTP services:

$ stunl -ports "web:3000:http" -password mySecret123

Note: Password protection applies to HTTP/WebSocket only. TCP/UDP ports rely on application-level authentication (e.g., database credentials).

Tier Features & Limits

Feature Pro
Total tunnels 10
HTTP services per multiport tunnel Unlimited
Custom hostname (-id)
Monthly bandwidth 25 GB
Reserved TCP ports 2 included

How Tunnel Counting Works

  • Multiport parent: 1 multiport tunnel = 1 toward your limit (with unlimited HTTP services inside)
  • TCP/UDP ports: Each TCP or UDP port = 1 additional tunnel (child of the parent)
  • Example: 1 multiport with 20 HTTP services + 5 TCP ports = 6 tunnels total

Next Steps