ngrokd v0.3.0
Forward Proxy Daemon for Private End-to-End Connectivity
A standalone daemon that enables local and network applications to connect to private endpoints in ngrok's cloud via mTLS, without requiring a Kubernetes cluster.
What is ngrokd?
ngrokd is a background daemon that:
Auto-Discovery
Automatically discovers Kubernetes bound endpoints from ngrok API and reconciles dynamically every 30 seconds.
Virtual Network Interfaces
Creates unique IPs per endpoint with automatic IP allocation and persistence across restarts.
Automatic DNS
Manages /etc/hosts automatically - no manual DNS configuration needed.
Secure mTLS
Forwards traffic securely via mTLS to ngrok cloud with automatic certificate provisioning.
Auto Certificate Renewal
Automatically renews mTLS certificates before expiry. Manual renewal available via CLI.
Dynamic Reconciliation
Endpoints added or removed on-the-fly with automatic listener lifecycle management.
Prerequisites
Before installing ngrokd, you'll need:
1. Create an ngrok API Key
- Go to ngrok API dashboard
- Click "New API Key"
- Give it a description (e.g., "ngrokd daemon")
- Copy the API key - you'll use it with
ngrokctl set-api-key
The API key allows ngrokd to discover your bound endpoints and provision certificates.
2. Create Private Cloud Endpoints
- Go to ngrok Endpoints dashboard
- Click "New Endpoint" → "Cloud"
- Choose "Kubernetes Operator" as the binding type
- Configure your endpoint:
- URL:
identifier.subdomain(e.g.,api.myapp) - Traffic Policy: Optional routing rules, auth, rate limiting
- URL:
- Click "Create Endpoint"
ngrokd automatically discovers these endpoints and creates local access points for them.
Platform Support
| Platform | IP Range | Interface |
|---|---|---|
| Linux | 10.107.0.0/16 | dummy (ngrokd0) |
| macOS | 127.0.0.0/8 | lo0 aliases |
| Windows | 127.0.0.0/8 | Loopback Adapter |
| Docker | Configurable | virtual/0.0.0.0 |
All platforms support both virtual mode (unique IPs) and network mode (port-based access).
Installation
Quick Install (Recommended)
curl -fsSL https://raw.githubusercontent.com/ishanj12/ngrokd/main/install.sh | sudo bash
Download Pre-built Binaries
# Apple Silicon
curl -LO https://github.com/ishanj12/ngrokd/releases/download/v0.3.0/ngrokd-v0.3.0-darwin-arm64.tar.gz
tar xzf ngrokd-v0.3.0-darwin-arm64.tar.gz
cd ngrokd-v0.3.0-darwin-arm64
sudo ./install.sh
# Intel Mac
curl -LO https://github.com/ishanj12/ngrokd/releases/download/v0.3.0/ngrokd-v0.3.0-darwin-amd64.tar.gz
tar xzf ngrokd-v0.3.0-darwin-amd64.tar.gz
cd ngrokd-v0.3.0-darwin-amd64
sudo ./install.sh
Start Daemon
# Background mode (recommended)
sudo nohup ngrokd --config=/etc/ngrokd/config.yml > ~/ngrokd.log 2>&1 &
# Foreground mode (for debugging)
sudo ngrokd --config=/etc/ngrokd/config.yml
Set API Key
ngrokctl set-api-key YOUR_NGROK_API_KEY
Verify
ngrokctl status
ngrokctl list
curl http://your-endpoint/
Quick Install (Recommended)
curl -fsSL https://raw.githubusercontent.com/ishanj12/ngrokd/main/install.sh | sudo bash
Download Pre-built Binaries
# AMD64
curl -LO https://github.com/ishanj12/ngrokd/releases/download/v0.3.0/ngrokd-v0.3.0-linux-amd64.tar.gz
tar xzf ngrokd-v0.3.0-linux-amd64.tar.gz
cd ngrokd-v0.3.0-linux-amd64
sudo ./install.sh
# ARM64
curl -LO https://github.com/ishanj12/ngrokd/releases/download/v0.3.0/ngrokd-v0.3.0-linux-arm64.tar.gz
tar xzf ngrokd-v0.3.0-linux-arm64.tar.gz
cd ngrokd-v0.3.0-linux-arm64
sudo ./install.sh
Start Daemon
# Background mode (recommended)
sudo nohup ngrokd --config=/etc/ngrokd/config.yml > ~/ngrokd.log 2>&1 &
# Or use systemd
sudo tee /etc/systemd/system/ngrokd.service << 'EOF'
[Unit]
Description=ngrokd Forward Proxy Daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/ngrokd --config=/etc/ngrokd/config.yml
Restart=always
User=root
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable ngrokd
sudo systemctl start ngrokd
Set API Key
ngrokctl set-api-key YOUR_NGROK_API_KEY
Verify
ngrokctl status
ngrokctl list
curl http://your-endpoint/
Installation Steps
Run PowerShell as Administrator
1. Download and extract:
iwr https://github.com/ishanj12/ngrokd/releases/download/v0.3.0/ngrokd-v0.3.0-windows-amd64.tar.gz -OutFile ngrokd.tar.gz
tar -xzf ngrokd.tar.gz
cd ngrokd-v0.3.0-windows-amd64
2. Install binaries:
# Copy to permanent location
mkdir "C:\Program Files\ngrokd" -Force
Copy-Item .\ngrokd.exe "C:\Program Files\ngrokd\"
Copy-Item .\ngrokctl.exe "C:\Program Files\ngrokd\"
# Add to PATH
$oldPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
$newPath = "$oldPath;C:\Program Files\ngrokd"
[Environment]::SetEnvironmentVariable("Path", $newPath, "Machine")
$env:Path += ";C:\Program Files\ngrokd"
3. Create config:
mkdir C:\ProgramData\ngrokd -Force
notepad C:\ProgramData\ngrokd\config.yml
Paste this config and save:
api:
url: https://api.ngrok.com
key: ""
ingressEndpoint: kubernetes-binding-ingress.ngrok.io:443
server:
log_level: info
bound_endpoints:
poll_interval: 30
selectors: ['true']
net:
subnet: 127.0.0.0/8
listen_interface: virtual
start_port: 9080
Start Daemon
Restart PowerShell as Administrator (for PATH to take effect), then:
# Foreground mode
ngrokd --config="C:\ProgramData\ngrokd\config.yml"
# Background mode
Start-Process ngrokd -ArgumentList '--config=C:\ProgramData\ngrokd\config.yml' -WindowStyle Hidden
Set API Key
ngrokctl set-api-key YOUR_NGROK_API_KEY
Verify
ngrokctl status
ngrokctl list
curl.exe http://your-endpoint/
Note: ngrokd requires Administrator privileges to manage the hosts file and network adapters. See the Windows Guide for details.
Pull Image
docker pull ishanjain8108/ngrokd:latest
Run Container
docker run -d \
--name ngrokd \
--cap-add=NET_ADMIN \
-e NGROK_API_KEY=your_api_key \
-p 9080-9100:9080-9100 \
-v ngrokd-data:/etc/ngrokd \
ishanjain8108/ngrokd:latest
Note: NET_ADMIN is only required for virtual mode (unique IPs). Network mode (listen_interface: "0.0.0.0") does NOT require NET_ADMIN — simply remove --cap-add=NET_ADMIN and set listen_interface: "0.0.0.0" in your config.
Verify
# Check status
docker exec ngrokd ngrokctl status
# List endpoints (wait ~30s first)
docker exec ngrokd ngrokctl list
# Test endpoint
curl http://localhost:9080/
Using Docker Compose
services:
ngrokd:
image: ishanjain8108/ngrokd:latest
container_name: ngrokd
cap_add:
- NET_ADMIN
environment:
- NGROK_API_KEY=${NGROK_API_KEY}
ports:
- "9080-9100:9080-9100"
volumes:
- ngrokd-data:/etc/ngrokd
restart: unless-stopped
volumes:
ngrokd-data:
Start with:
NGROK_API_KEY=your_key docker compose up -d
Edit Config in Container
docker exec -it ngrokd ngrokctl config edit
Configuration
Listen Interface Modes
Virtual Mode (Default)
Each endpoint gets a unique IP address. Best for same-machine access.
net:
listen_interface: "virtual"
Example:
- Endpoint 1: 127.0.0.2:80 (macOS) or 10.107.0.2:80 (Linux)
- Endpoint 2: 127.0.0.3:80
- Endpoint 3: 127.0.0.4:80
Network Mode - All Interfaces
Binds to 0.0.0.0 with sequential ports. Best for network accessibility.
net:
listen_interface: "0.0.0.0"
start_port: 9080
Example:
- Endpoint 1: 0.0.0.0:9080
- Endpoint 2: 0.0.0.0:9081
- Endpoint 3: 0.0.0.0:9082
Network Mode - Named Interface
Bind to a specific network interface. Great for multi-homed servers.
net:
listen_interface: "eth0" # or "en0", "enp0s1", etc.
start_port: 9080
Example:
- Automatically resolves eth0 to its IP (e.g., 192.168.1.100)
- Endpoints bind to 192.168.1.100:9080, 9081, 9082...
Network Mode - Specific IP
Bind to a specific IP address.
net:
listen_interface: "192.168.1.100"
start_port: 9080
Per-Endpoint Overrides
Customize listen interface for specific endpoints:
net:
listen_interface: "virtual" # Default
start_port: 9080
overrides:
my-endpoint: "0.0.0.0" # This endpoint uses network mode
other-endpoint: "eth0" # This one uses eth0
Configuration File
Default location: /etc/ngrokd/config.yml
Complete Configuration Example
api:
url: https://api.ngrok.com
key: "" # Set via: ngrokctl set-api-key
ingressEndpoint: "kubernetes-binding-ingress.ngrok.io:443"
server:
log_level: info
socket_path: /var/run/ngrokd.sock
client_cert: /etc/ngrokd/tls.crt
client_key: /etc/ngrokd/tls.key
bound_endpoints:
poll_interval: 30 # Seconds between API polls
net:
interface_name: ngrokd0
subnet: 10.107.0.0/16
listen_interface: "virtual"
start_port: 9080
overrides: {} # Per-endpoint overrides
Configuration Options
| Option | Description | Default |
|---|---|---|
api.url |
ngrok API URL | https://api.ngrok.com |
api.key |
ngrok API key (set via ngrokctl) | "" |
ingressEndpoint |
mTLS ingress endpoint | kubernetes-binding-ingress.ngrok.io:443 |
server.log_level |
Log level (info, debug, error) | info |
bound_endpoints.poll_interval |
Seconds between endpoint discovery | 30 |
net.listen_interface |
virtual, 0.0.0.0, IP, or interface name | virtual |
net.start_port |
Starting port for network mode | 9080 |
net.overrides |
Per-endpoint listen_interface overrides | {} |
Hot Reload
The daemon watches the config file and automatically reloads changes:
# Edit config
sudo nano /etc/ngrokd/config.yml
# Or in Docker
docker exec -it ngrokd ngrokctl config edit
# Changes apply automatically in ~5 seconds
# Endpoints are rebound if listen_interface changes
CLI Reference
ngrokctl Commands
| Command | Description |
|---|---|
ngrokctl status |
Check daemon registration status |
ngrokctl list |
List discovered endpoints |
ngrokctl health |
Detailed health information |
ngrokctl set-api-key KEY |
Set ngrok API key |
ngrokctl config edit |
Edit configuration file |
ngrokctl refresh-cert |
Check and renew certificate if expiring |
ngrokctl refresh-cert --force |
Force immediate certificate renewal |
Technical Details
How It Works
1. Virtual Network Interface
Creates an interface with subnet for unique IP allocation:
- Linux: dummy interface
ngrokd0with 10.107.0.0/16 subnet - macOS: loopback aliases on
lo0with 127.0.0.0/8 subnet
2. IP Allocation
Each discovered endpoint gets a unique IP:
10.107.0.2 → api.example.com
10.107.0.3 → web.example.com
10.107.0.4 → db.example.com
IP mappings persist across restarts in /etc/ngrokd/ip_mappings.json
3. DNS Management
Automatically updates /etc/hosts:
# BEGIN ngrokd managed section
10.107.0.2 api.example.com
10.107.0.3 web.example.com
# END ngrokd managed section
4. Traffic Forwarding
Each endpoint has a listener that forwards via mTLS:
Local app → Listener (unique IP:port) → mTLS → ngrok cloud → Backend
Network Modes
Virtual Mode
Pros:
- Multiple endpoints can use the same port (e.g., all on port 80)
- DNS works via /etc/hosts
- Clean separation per endpoint
Cons:
- Only accessible from same machine
- Requires root for /etc/hosts and interface management
Network Mode
Pros:
- Accessible from network (other machines, containers)
- Easy to configure firewalls (single port range)
- Works well with Docker port mappings
Cons:
- Uses sequential ports (can't have multiple endpoints on port 80)
- Need to check ngrokctl list to see port assignments
Automatic Features
- Port Conflict Resolution: Automatically finds next available port if conflict detected
- Certificate Auto-Renewal: Threshold-based checking renews certificates when remaining lifetime is less than 1/3 of total lifetime
- Hot Certificate Reload: Zero-downtime certificate rotation — new certificates are loaded without restarting listeners
- Operator Recovery: Automatic re-registration if the operator binding is deleted (404 recovery)
- Hot Reload: Config changes detected and applied automatically
- State Persistence: IP and port mappings survive restarts
Troubleshooting
No Endpoints Discovered
Symptoms: ngrokctl list shows 0 endpoints
Solutions:
- Wait 30 seconds for first poll cycle
- Verify API key is set:
ngrokctl status - Check bound endpoints exist in ngrok dashboard
- Check daemon logs:
tail ~/ngrokd.log
Connection Refused
Symptoms: curl: (7) Failed to connect
Solutions:
- Verify daemon is running:
ps aux | grep ngrokd - Check endpoint is listed:
ngrokctl list - Verify DNS entry in /etc/hosts (virtual mode)
- Check IP allocation:
cat /etc/ngrokd/ip_mappings.json
Port Already in Use
Symptoms: bind: address already in use
What happens: Daemon automatically retries with next port (up to 20 attempts)
Check logs:
tail ~/ngrokd.log | grep "Port in use, trying next port"
If you see this, the conflict was automatically resolved!
Docker: Can't Access Endpoints from Host
Issue: Using virtual mode, virtual IPs only exist inside container
Solution: Use listen_interface: "0.0.0.0" for Docker
# Edit config
docker exec -it ngrokd ngrokctl config edit
# Change to:
listen_interface: "0.0.0.0"
# Wait ~5s for reload
docker exec ngrokd ngrokctl list
# Access from host
curl http://localhost:9080/
Network Interface Errors
Symptoms: Failed to create virtual network interface
Solutions:
- Ensure running as root/sudo
- On Linux: Check kernel modules for dummy interface
- On macOS: Verify permissions for ifconfig
- Check logs for specific error details
Certificate Errors
Symptoms: Connection failures after long uptime, TLS handshake errors
Solutions:
- Force certificate renewal:
ngrokctl refresh-cert --force - Check certificate status in logs:
tail ~/ngrokd.log | grep cert - Verify certificate files exist:
ls -la /etc/ngrokd/tls.*
Invalid Interface Name
Symptoms: Config validation failed with interface name
Solution: Check available interfaces:
# Linux
ip addr show
# macOS
ifconfig
# Docker
docker exec ngrokd ip addr show
Use the correct interface name from the list (e.g., enp0s1 instead of eth0)
Requirements
- ngrok API Key: Get from https://dashboard.ngrok.com/api
- Bound Endpoints: Create Kubernetes bound endpoints in ngrok dashboard
- Root/sudo: Required for network interface and /etc/hosts management
- Platform: Linux, macOS, or Docker (Windows planned)