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

  1. Go to ngrok API dashboard
  2. Click "New API Key"
  3. Give it a description (e.g., "ngrokd daemon")
  4. 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

  1. Go to ngrok Endpoints dashboard
  2. Click "New Endpoint" → "Cloud"
  3. Choose "Kubernetes Operator" as the binding type
  4. Configure your endpoint:
    • URL: identifier.subdomain (e.g., api.myapp)
    • Traffic Policy: Optional routing rules, auth, rate limiting
  5. 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 ngrokd0 with 10.107.0.0/16 subnet
  • macOS: loopback aliases on lo0 with 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

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