Documentation
Everything you need to run secure tunnels with clientproxy.io.
Tutorials
Follow a walkthrough for the service you want to publish.
Publish a NAS web app →
Install the Synology package or Docker client and give a NAS web app a public HTTPS URL.
Share a local development server →
Share a page running on your computer, with a simple demo and Vite configuration.
Publish an ESP32 control UI →
Serve a React dashboard from the proxy and send live API requests to an ESP32 on your LAN.
Getting started
clientproxy.io lets you expose a local service — a web app, an API, or an IoT device — to the public internet through a secure tunnel. The tunnel-client runs on your machine and connects to one of our proxy servers, which terminates TLS and routes traffic to your tunnel domain.
To get started you need three things: an account, a Tunnel ID, and your subscription API key. All three are available in the admin console — see the table below.
| Value | Where to find it |
|---|---|
| API key | Admin console → Dashboard → API Key (click the eye icon to reveal, copy icon to copy) |
| Tunnel ID | Admin console → Client Tunnels → open a tunnel → Tunnel ID field |
| Proxy ID | Admin console → Proxy Servers → open a server → Proxy ID field (needed for self-hosted proxy setup only) |
Installation
tunnel-client is a single static binary with no runtime dependencies. Packages are published on each release to GitHub Releases.
Linux — Debian / Ubuntu (amd64)
VER=$(curl -s https://api.github.com/repos/clientproxy-io/tunnel-client/releases/latest \
| grep '"tag_name"' | cut -d'"' -f4 | sed 's/v//')
curl -LO "https://github.com/clientproxy-io/tunnel-client/releases/download/v${VER}/tunnel-client_${VER}_amd64.deb"
sudo dpkg -i "tunnel-client_${VER}_amd64.deb"For arm64 (Raspberry Pi, etc.) replace amd64 with arm64.
Linux — RHEL / Rocky / Amazon Linux (amd64)
VER=$(curl -s https://api.github.com/repos/clientproxy-io/tunnel-client/releases/latest \
| grep '"tag_name"' | cut -d'"' -f4 | sed 's/v//')
curl -LO "https://github.com/clientproxy-io/tunnel-client/releases/download/v${VER}/tunnel-client-${VER}-1.amd64.rpm"
sudo rpm -i "tunnel-client-${VER}-1.amd64.rpm"Windows (amd64)
Download the zip from the GitHub Releases page. It contains the standalone client and an optional Windows service installer.
# PowerShell — download and extract latest release
$VER = (Invoke-RestMethod https://api.github.com/repos/clientproxy-io/tunnel-client/releases/latest).tag_name.TrimStart('v')
Invoke-WebRequest `
"https://github.com/clientproxy-io/tunnel-client/releases/download/v$VER/tunnel-client-windows-$VER.zip" `
-OutFile tunnel-client.zip
Expand-Archive tunnel-client.zip -DestinationPath .
# Run
.\tunnel-client.exe `
--api-url https://api-us.clientproxy.io/api `
--tunnel-id <YOUR_TUNNEL_ID> `
--api-key <YOUR_API_KEY>The executable can run directly without installation. To run it as a Windows service, open an elevated PowerShell window in the extracted folder and run powershell -ExecutionPolicy Bypass -File install.ps1. The installer preserves existing credentials and updates old default regional API URLs.
Other Linux — static binary
For any other distribution, or a 32-bit Raspberry Pi OS, download the static binary. It is available for amd64, arm64 and armv7.
curl -Lo tunnel-client \
https://github.com/clientproxy-io/tunnel-client/releases/latest/download/tunnel-client-linux-armv7
chmod +x tunnel-client
./tunnel-client --versionConfigure & start (systemd)
After installing the package, set your credentials in /etc/tunnel-client/env:
TUNNEL_API_URL=https://api-us.clientproxy.io/api
TUNNEL_ID=<YOUR_TUNNEL_ID>
TUNNEL_API_KEY=<YOUR_API_KEY>sudo systemctl enable --now tunnel-client
sudo systemctl status tunnel-clientBuild from source
Requires Rust 1.80+.
git clone https://github.com/clientproxy-io/tunnel-client.git
cd tunnel-client
cargo build --bin tunnel-client --release
./target/release/tunnel-client --helpNAS & Docker
Run the tunnel client on your NAS to put apps like DSM, Jellyfin, Home Assistant or Nextcloud on a public HTTPS address. You don't need port forwarding or a public IP.
In the dashboard, map your tunnel domains to the services on the NAS, for example localhost:5000 for Synology DSM or localhost:8096 for Jellyfin.
Synology DiskStation (DSM 7)
A native package that works on every DSM 7 model: Intel/AMD, ARMv8 and ARMv7. Installing from Package Center means you also get updates there.
- Open Package Center → Settings → General and set Trust Level to Any publisher.
- Under Package Sources, click Add. Name:
clientproxy.io, Location:https://api-us.clientproxy.io/api/synology. - Open the Community tab, choose clientproxy.io Tunnel and click Install.
- In the wizard, choose your region and paste your Tunnel ID and API key. The tunnel starts right away, and again on every boot.
Manual install: download tunnel-client-<version>-synology-dsm7.spk from GitHub Releases and use Package Center → Manual Install.
Map DSM to your domain: in the dashboard, set the domain's Local IP:port to localhost:5000. Always use DSM's plain HTTP port 5000, never the HTTPS port 5001. The tunnel already provides HTTPS, and a 5001 mapping fails with 400 The plain HTTP request was sent to HTTPS port. Also keep Control Panel → Login Portal → Automatically redirect HTTP connection to HTTPS turned off, or visitors get a redirect loop. For other DSM apps and containers, map their HTTP port the same way; the in-app User Manual has a full port table.
Change settings: each package update opens a wizard where you can switch region, Tunnel ID or API key. Fields left blank keep their current values. Logs: Package Center → clientproxy.io Tunnel → View log.
Docker
The multi-arch image ghcr.io/clientproxy-io/tunnel-client runs on amd64, arm64 and arm/v7. It is configured entirely through environment variables.
docker run -d --name tunnel-client --restart unless-stopped --network host \
-e TUNNEL_API_URL=https://api-us.clientproxy.io/api \
-e TUNNEL_ID=<YOUR_TUNNEL_ID> \
-e TUNNEL_API_KEY=<YOUR_API_KEY> \
ghcr.io/clientproxy-io/tunnel-client:latestThe same setup as a Compose file, which the NAS instructions below reuse:
services:
tunnel-client:
image: ghcr.io/clientproxy-io/tunnel-client:latest
container_name: tunnel-client
restart: unless-stopped
network_mode: host
environment:
TUNNEL_API_URL: https://api-us.clientproxy.io/api
TUNNEL_ID: <YOUR_TUNNEL_ID>
TUNNEL_API_KEY: <YOUR_API_KEY>Why host networking? It lets a dashboard backend like localhost:8080 reach services running on the host. With bridge networking, localhost means the container itself. In that case, use the host's LAN IP (e.g. 192.168.1.10:8080) or another container's name on a shared network.
View logs with docker logs -f tunnel-client.
QNAP (Container Station)
Open Container Station → Applications → Create, paste the Compose file above with your values, and click Create.
TrueNAS SCALE
Open Apps → Discover Apps → Custom App and fill in:
- Image repository
ghcr.io/clientproxy-io/tunnel-client, taglatest. - Environment variables
TUNNEL_API_URL,TUNNEL_IDandTUNNEL_API_KEY. - Host Network enabled.
On versions that offer Install via YAML, you can paste the Compose file instead.
Unraid
Open Docker → Add Container and fill in:
- Repository
ghcr.io/clientproxy-io/tunnel-client:latest. - Network Type Host.
- Three variables (Add another Path, Port, Variable… → Variable):
TUNNEL_API_URL,TUNNEL_IDandTUNNEL_API_KEY.
OpenMediaVault, CasaOS / ZimaOS, Portainer
Paste the Compose file above into the app's compose editor:
- OpenMediaVault: Services → Compose → Files → Add, then click Up.
- CasaOS / ZimaOS: App Store → Custom Install → Import.
- Portainer: Stacks → Add stack.
You can also use Docker on a Synology model that has Container Manager: Project → Create. The native package above is simpler, though.
Quick start
Start a TLS tunnel to a local service on port 8080:
./tunnel-client \
--api-url https://api-us.clientproxy.io/api \
--tunnel-id <YOUR_TUNNEL_ID> \
--api-key <YOUR_API_KEY>Once connected, your local service is reachable at the tunnel domain shown in the dashboard (and in the client output). Test it:
curl -s -o /dev/null -w "%{http_code}" https://your-tunnel.clientproxy.io/
# → 200API keys
Your API key authorises the client against your subscription. It has the format <subscriptionId>_<salt> and is shown in Admin console → Dashboard → API Key. Click the eye icon to reveal it and the copy icon to copy it to the clipboard.
Keep it secret — anyone with the key can open tunnels on your subscription. If it leaks, click Regenerate in the dashboard; the old key stops working immediately and you update your client config with the new one.
CLI reference
| Flag | Environment variable | Description |
|---|---|---|
--api-url | TUNNEL_API_URL | Proxy-admin API base URL (regional endpoint) |
--tunnel-id | TUNNEL_ID | Your tunnel ID from the dashboard |
--api-key | TUNNEL_API_KEY | Subscription API key (<subscriptionId>_<salt>) |
--no-tls | TUNNEL_NO_TLS | Plain TCP mode for ESP32 / IoT devices |
--reconnect-interval | TUNNEL_RECONNECT_INTERVAL | Seconds between reconnect attempts (default 1) |
--tls-server-name | TUNNEL_TLS_SERVER_NAME | Override the TLS SNI hostname |
--tls-ca-cert-path | TUNNEL_TLS_CA_CERT_PATH | Custom CA certificate for the tunnel TLS connection |
--verbose | TUNNEL_VERBOSE | Enable debug logging |
Regions
The coordination service — account, tunnel and certificate management — runs in three regions. Choose the endpoint closest to you and set --api-url accordingly:
# United States
--api-url https://api-us.clientproxy.io/api
# Europe
--api-url https://api-eu.clientproxy.io/api
# Asia
--api-url https://api-asia.clientproxy.io/apiProxy servers — the hosts that terminate TLS and carry your traffic — are a separate layer. Shared proxies are listed in the dashboard; additional ones, including private proxies in a specific region, are deployed on request (info@clientproxy.io).
Private proxy sharing
A private proxy remains owned by its subscription, but its owner can grant controlled access to another subscription. This is useful for an agency, customer environment, or a separate team that needs to run its own tunnel through dedicated infrastructure.
For the proxy owner
Open Dashboard → Proxy Servers, choose a Private proxy, then select Sharing Keys. Create a key with a clear name and optional comment, set an expiry or leave it permanent, and choose the maximum number of active client tunnels it can support. Copy the generated key when it is shown and send it securely. You can revoke the key at any time.
For the recipient
In Dashboard → Client Tunnels, create or edit a tunnel, paste the key into Private Proxy Sharing Key, select Add shared proxy, then save the tunnel. Assign your own active Private client plan before connecting.
Important: the key delegates access only. It does not share the owner's plan, API key, proxy credentials, or capacity beyond the key's active-tunnel limit. The proxy's normal capacity limits still apply to all tunnels using it.
Plain TCP / ESP32
For microcontrollers and other resource-constrained devices, run the client in plain-TCP mode with --no-tls. The framing protocol is identical — TLS is just a socket wrapper — so the same tunnel works for both.
./tunnel-client \
--api-url https://api-us.clientproxy.io/api \
--tunnel-id <YOUR_TUNNEL_ID> \
--api-key <YOUR_API_KEY> \
--no-tlsThe protocol uses a compact 10-byte frame header, making it practical to implement directly on an ESP32 with a fixed 4 KB buffer.
Custom domains
Every tunnel gets a free subdomain automatically. To use your own domain, add a domain mapping in Dashboard → Client Tunnels → Domains and point a DNS record at your proxy server. Certificates are provisioned automatically via Let's Encrypt.
For domains that change IP, configure a DDNS provider (Cloudflare, RFC 2136 TSIG, and others are supported) so DNS stays in sync with your proxy.
Proxy server setup
You can run your own tunnel-proxy instance on any Linux server — ideal for dedicated infrastructure, fixed IPs, or a region you want to host yourself. The server handles TLS termination, ACME certificate provisioning, and connection multiplexing automatically.
During beta, private proxy servers are provisioned for free on request. Email info@clientproxy.io if you'd prefer us to set one up for you.
Prerequisites
Open firewall ports — the proxy needs four ports reachable from the internet:
| Port | Purpose |
|---|---|
80 | HTTP — ACME challenge + redirect |
443 | HTTPS — public tunnel traffic |
8778 | TLS tunnel client connections |
8779 | Plain-TCP tunnel client connections (IoT/ESP32) |
Ubuntu / Debian with ufw:
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp
sudo ufw allow 8778/tcp && sudo ufw allow 8779/tcp
sudo ufw enableRHEL / Rocky / Amazon Linux (firewalld):
sudo firewall-cmd --permanent --add-port={80,443,8778,8779}/tcp
sudo firewall-cmd --reloadOracle Cloud / plain iptables (no ufw):
Oracle Cloud Ubuntu images use raw iptables with a catch-all REJECT rule at the end of the INPUT chain. Rules added with -A (append) land after the REJECT and are silently ignored — the port appears open on the server but external connections are refused. Always use -I (insert) to place rules before the REJECT:
# Find the REJECT rule line number
REJECT=$(sudo iptables -L INPUT --line-numbers -n | awk '/REJECT/ {print $1; exit}')
# Insert ACCEPT rules just above it (reverse order so 80 ends up first)
sudo iptables -I INPUT $REJECT -p tcp -m state --state NEW -m tcp --dport 8779 -j ACCEPT
sudo iptables -I INPUT $REJECT -p tcp -m state --state NEW -m tcp --dport 8778 -j ACCEPT
sudo iptables -I INPUT $REJECT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
sudo iptables -I INPUT $REJECT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
# Persist across reboots
sudo apt install -y iptables-persistent
sudo iptables-save | sudo tee /etc/iptables/rules.v4To verify the order is correct: sudo iptables -L INPUT --line-numbers -n — the four ACCEPT rules must appear above the REJECT line.
Cloud network firewall: even with the OS firewall configured, also open the same four ports in the provider's network security group or security list (Oracle VCN security lists, AWS security groups, Hetzner firewall) — they are a separate layer.
DNS — point your proxy domain (e.g. myproxy.clientproxy.io) A record to the server IP before starting the service. ACME certificate provisioning runs on first start and requires DNS to resolve.
Credentials — you need two values from the admin console:
- API key — Admin console → Dashboard → API Key (eye icon to reveal, copy icon to copy)
- Proxy ID — Admin console → Proxy Servers → open the server entry → Proxy ID field
Install on Debian / Ubuntu
# Add GPG key and repository
curl -fsSL https://us-central1-apt.pkg.dev/doc/repo-signing-key.gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/clientproxy-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/clientproxy-archive-keyring.gpg] \
https://us-central1-apt.pkg.dev/projects/proxyconfig-71f70 tunnel-proxy-apt main" \
| sudo tee /etc/apt/sources.list.d/clientproxy.list
sudo apt update && sudo apt install tunnel-proxyInstall on RHEL / Rocky / Amazon Linux
cat <<EOF | sudo tee /etc/yum.repos.d/clientproxy.repo
[clientproxy]
name=clientproxy.io Tunnel Proxy
baseurl=https://us-central1-yum.pkg.dev/projects/proxyconfig-71f70/tunnel-proxy-yum
enabled=1
gpgcheck=0
EOF
sudo dnf install tunnel-proxyConfigure
Create /etc/tunnel-proxy/config.toml:
listen_port = 80
listen_port_tls = 443
tunnel_port_tls = 8778
tunnel_port = 8779
tunnel_api_base_url = "https://api-us.clientproxy.io/api" # or api-eu / api-asia
tunnel_api_key = "<YOUR_API_KEY>"
tunnel_proxy_id = "<YOUR_PROXY_ID>"
tunnel_cache_dir = "/etc/tunnel-proxy/app-file-cache"sudo mkdir -p /etc/tunnel-proxy/app-file-cache
sudo systemctl start tunnel-proxy
sudo systemctl status tunnel-proxyLogs
journalctl -u tunnel-proxy -f # follow live
journalctl -u tunnel-proxy -n 100 # last 100 linesRecommended server sizing
Each TLS connection uses roughly 100 KB of RAM (TLS state + I/O buffers). For 2,000 concurrent clients, 4 GB RAM is sufficient with comfortable headroom. A 1 Gbps network port supports the traffic load easily. We recommend Hetzner CAX11 (ARM, €4.15/mo, 4 GB RAM, 20 TB/month) for EU and Asia deployments.
FAQ
Is the service really free right now?
Yes — clientproxy.io is currently in open beta and all services are free. Every account gets a free tunnel product and a demo plan to try additional features. No credit card is required at any point during beta.
Can I get a dedicated private proxy?
Yes — and it's free during beta. There are two ways:
- We host it for you — email info@clientproxy.io or post in GitHub Discussions and we'll provision a private proxy on your subscription at no cost.
- Self-hosted — run
tunnel-proxyon your own server. See the Proxy server setup section for step-by-step instructions.
Either way, you need a Private client plan assigned to your tunnel. A demo private client plan is available in the dashboard at no charge during beta.
Can I share my private proxy with another subscription?
Yes. Open the private proxy's Sharing Keys action, create a named key, and set an optional expiry and active-tunnel limit. The recipient adds the key while creating or editing a tunnel and must assign their own Private client plan. You can revoke a key immediately without changing the proxy's ownership or exposing its credentials.
What's the difference between public, Pro, and private proxies?
Public — shared infrastructure, available to all accounts on free and paid plans. Good for development and low-traffic use. Pro — higher-capacity shared servers, available on paid plans (post-beta). Private — a dedicated server exclusively for your subscription: no shared neighbours, fixed IP, configurable quotas. All three types are free to use during beta.
How will billing work after beta?
Once beta ends, paid plans will activate. Free and demo plans will remain available. Paid plans renew monthly; VAT/tax is handled automatically. You'll be notified before any charges begin — nothing will switch to paid without your action.
What happens when a demo plan expires?
The associated resource is suspended (your configuration stays intact). During beta you can request a fresh demo plan at any time via email or GitHub Discussions.
Something is missing or not working — who do I tell?
Open a thread on GitHub Discussions. Beta feedback is the most valuable thing you can give right now — missing features, confusing UX, capacity limits that feel too low. We read everything and act on it.
Can I request more capacity or a custom setup?
Yes. Email info@clientproxy.io or post in GitHub Discussions describing what you need. During beta we set things up manually and use the feedback to design the self-serve flow.
Support
GitHub Discussions is the primary support channel. Search existing threads before posting — your question is often already answered. All threads are indexed by Google, so the answers benefit the whole community.
github.com/clientproxy-io/tunnel-client/discussions →
For custom plans, private proxy provisioning, or anything that doesn't fit a public thread, email us at info@clientproxy.io.
Source code
The tunnel client is open source: github.com/clientproxy-io/tunnel-client. Bug reports and pull requests are welcome.
Ready to try it?
Create free account →