Getting Started

This guide will walk you through installing LocalUp, setting up relay servers, and creating your first tunnel in minutes.


Installation

Choose one of the following installation methods:

Option 1: Homebrew (macOS/Linux)

brew tap localup-dev/tap
brew install localup

# Verify installation
localup --version
localup --help

Option 2: Quick Install (One-Liner)

curl -fsSL https://raw.githubusercontent.com/localup-dev/localup/main/scripts/install.sh | bash

Verify Installation

After installation, verify that LocalUp is working:

localup --version
localup relay --help        # Shows available relay subcommands
localup relay tcp --help    # Shows TCP relay options
localup relay tls --help    # Shows TLS/SNI relay options
localup relay http --help   # Shows HTTP/HTTPS relay options
localup relay all --help    # Shows all protocol options
localup generate-token --help

Upgrading

Keep LocalUp up to date to get the latest features and security fixes.

Option 1: Homebrew (macOS/Linux)

If you installed via Homebrew:

brew update
brew upgrade localup

# Verify the new version
localup --version

Option 2: Quick Install Script

Re-run the install script to upgrade to the latest version:

curl -fsSL https://raw.githubusercontent.com/localup-dev/localup/main/scripts/install.sh | bash

# Verify the new version
localup --version

Option 3: Manual Download

Download the latest release directly from GitHub:

# Check current version
localup --version

# Download latest release (replace with your OS/arch)
# Visit: https://github.com/localup-dev/localup/releases/latest

# macOS (Apple Silicon)
curl -L https://github.com/localup-dev/localup/releases/latest/download/localup-aarch64-apple-darwin.tar.gz | tar xz
sudo mv localup /usr/local/bin/

# macOS (Intel)
curl -L https://github.com/localup-dev/localup/releases/latest/download/localup-x86_64-apple-darwin.tar.gz | tar xz
sudo mv localup /usr/local/bin/

# Linux (x86_64)
curl -L https://github.com/localup-dev/localup/releases/latest/download/localup-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv localup /usr/local/bin/

# Verify upgrade
localup --version

Check for Updates

You can check if a newer version is available:

# Check your current version
localup --version

# Check latest release on GitHub
curl -s https://api.github.com/repos/localup-dev/localup/releases/latest | grep tag_name

Client Setup

The LocalUp client connects your local service to a relay server. Here's how to use it:

Basic Client Options

localup [OPTIONS]

--port <PORT>                 Local port to expose
--address <HOST:PORT>         Local address to expose (alternative to --port)
--protocol <PROTOCOL>         Protocol: http, https, tcp, tls
--relay <ADDR>                Relay server address (host:port)
--subdomain <NAME>            Subdomain for HTTP/HTTPS
--remote-port <PORT>          Specific port for TCP tunnels
--token <TOKEN>               JWT authentication token

Generate JWT Token

Before creating a tunnel, generate a JWT token:

export TOKEN=$(localup generate-token --secret "your-secret-key" --sub "myapp" --token-only)

Note: The JWT secret must match the relay server's --jwt-secret configuration.


Self-Hosting Relays

LocalUp supports three types of relay servers, each with different protocol capabilities. You can also run all protocols on a single relay.

Common Relay Options

All relay types share these common options:

--localup-addr <ADDR>         Control plane address [default: 0.0.0.0:4443]
--jwt-secret <SECRET>         JWT secret for authenticating clients (REQUIRED)
--domain <DOMAIN>             Public domain name [default: localhost]
--log-level <LEVEL>           Log level (trace, debug, info, warn, error)
--database-url <URL>          Database URL (postgres:// or sqlite://)

Understanding the --domain flag:

The --domain flag determines how subdomains are constructed for your tunnels:

  • --domain localhost → tunnels accessible at {subdomain}.localhost:PORT
  • --domain relay.example.com → tunnels accessible at {subdomain}.relay.example.com

For production deployments with a real domain, you'll need:

  1. DNS wildcard record: *.relay.example.com → your server IP
  2. Let's Encrypt certificates for your domain
  3. The --domain flag set to your domain name

TCP Relay

Exposes local TCP services (databases, SSH, custom TCP services) with port-based routing and automatic port allocation.

localup relay tcp \
  --localup-addr "0.0.0.0:14443" \
  --tcp-port-range "10000-20000" \
  --jwt-secret "my-jwt-secret"

TCP-specific options:

  • --tcp-port-range <START-END>: Port range for TCP tunnels [default: 10000-20000]

Learn more: TCP Relay Guide

TLS/SNI Relay

Provides TLS passthrough with SNI-based routing for end-to-end encrypted connections (no certificates needed on relay).

localup relay tls \
  --localup-addr "0.0.0.0:14443" \
  --tls-addr "0.0.0.0:18443" \
  --jwt-secret "my-jwt-secret"

TLS-specific options:

  • --tls-addr <ADDR>: TLS/SNI server address [default: 0.0.0.0:4443]

Learn more: TLS/SNI Relay Guide

HTTP/HTTPS Relay

Hosts web applications, APIs, and webhooks with host-based routing and TLS termination.

# Generate self-signed certificates (one-time)
openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout key.pem -out cert.pem -days 365 \
  -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"

# Start HTTP/HTTPS relay
localup relay http \
  --localup-addr "0.0.0.0:14443" \
  --http-addr "0.0.0.0:18080" \
  --https-addr "0.0.0.0:18443" \
  --tls-cert=cert.pem --tls-key=key.pem \
  --jwt-secret "my-jwt-secret"

HTTP-specific options:

  • --http-addr <ADDR>: HTTP server address [default: 0.0.0.0:8080]
  • --https-addr <ADDR>: HTTPS server address (optional)
  • --tls-cert <PATH>: TLS certificate file (PEM format, required if --https-addr used)
  • --tls-key <PATH>: TLS private key file (PEM format, required if --https-addr used)

Learn more: HTTP/HTTPS Relay Guide

All Protocols (Combined Relay)

Run all protocols (TCP, TLS, HTTP, HTTPS) on a single relay server:

# Generate certificates (one-time)
openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout key.pem -out cert.pem -days 365 \
  -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost"

# Start combined relay
localup relay all \
  --localup-addr "0.0.0.0:14443" \
  --tcp-port-range "10000-20000" \
  --tls-addr "0.0.0.0:18443" \
  --http-addr "0.0.0.0:18080" \
  --https-addr "0.0.0.0:18443" \
  --tls-cert=cert.pem --tls-key=key.pem \
  --jwt-secret "my-jwt-secret"

Quick Start

Here's a complete example to get you up and running in 2 minutes:

# 1. Generate self-signed certificate (one-time)
openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout key.pem -out cert.pem -days 365 \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost"

# 2. Start relay server (Terminal 1)
localup relay http \
  --localup-addr=0.0.0.0:14443 \
  --http-addr=0.0.0.0:18080 \
  --https-addr=0.0.0.0:18443 \
  --tls-cert=cert.pem --tls-key=key.pem \
  --jwt-secret="my-jwt-secret"

# 3. Start a local HTTP server (Terminal 2)
python3 -m http.server 13000

# 4. Generate token and create tunnel (Terminal 3)
export TOKEN=$(localup generate-token --secret "my-jwt-secret" --sub "myapp" --token-only)
localup --port 13000 --relay localhost:14443 --subdomain myapp --token=$TOKEN

# 5. Access your service (Terminal 4)
curl -k https://myapp.localhost:18443
curl http://myapp.localhost:18080

What's happening:

  1. We generate self-signed TLS certificates for HTTPS support
  2. The relay server starts with HTTP/HTTPS support on ports 18080 and 18443
  3. A local HTTP server runs on port 13000
  4. The LocalUp client creates a tunnel from the local server to the relay
  5. Your local server is now accessible via myapp.localhost on both HTTP and HTTPS

Reverse Tunnels

Unlike traditional tunnels that expose local services to the public internet, reverse tunnels allow you to securely access private services behind NAT/firewall without opening any inbound ports. Perfect for accessing databases, internal APIs, and IoT devices on private networks from anywhere.

What's Different?

Traditional Tunnel (Public Exposure):

Local Service → Client → Relay → Internet (Anyone can access)

Reverse Tunnel (Private Access):

Private Service ← Agent ← Relay ← Client (Authenticated only)

Quick Start Example

Here's a complete example accessing a private PostgreSQL database:

# Terminal 1: Start relay server
localup relay tcp \
  --localup-addr "0.0.0.0:14443" \
  --tcp-port-range "10000-20000" \
  --jwt-secret "my-jwt-secret"

# Terminal 2: Run agent on private network
export AGENT_TOKEN=$(localup generate-token --secret "my-jwt-secret" --sub "private-db" --token-only)
localup agent \
  --relay localhost:14443 \
  --agent-id "private-db" \
  --target-address "localhost:5432" \
  --token "$AGENT_TOKEN"

# Terminal 3: Connect as client from anywhere
export CLIENT_TOKEN=$(localup generate-token --secret "my-jwt-secret" --sub "client" --token-only)
localup connect \
  --relay localhost:14443 \
  --agent-id "private-db" \
  --local-address "localhost:19432" \
  --remote-address "localhost:5432" \
  --token "$CLIENT_TOKEN"

# Terminal 4: Access the private service
psql -h localhost -p 19432 -U postgres

Security Features

Reverse tunnels provide strong security guarantees:

  • Zero Inbound Ports: No firewall rules needed on private network
  • End-to-End Encryption: All traffic encrypted with QUIC (TLS 1.3)
  • Mutual Authentication: Both agent and client must present valid JWT tokens
  • Localhost-Only: Agent only accesses localhost, preventing lateral movement
  • No Public Exposure: Private service never listens on public internet

Learn more: Complete Reverse Tunnel Guide with Security Architecture →


Production Domain Setup

For production deployments with a real domain (e.g., relay.example.com):

# 1. Set up DNS wildcard record
# In your DNS provider, create: *.relay.example.com → your-server-ip

# 2. Get Let's Encrypt certificates (one-time setup)
certbot certonly --standalone -d relay.example.com -d "*.relay.example.com"

# 3. Start relay with your domain
localup relay http \
  --localup-addr "0.0.0.0:4443" \
  --http-addr "0.0.0.0:80" \
  --https-addr "0.0.0.0:443" \
  --domain "relay.example.com" \
  --tls-cert "/etc/letsencrypt/live/relay.example.com/fullchain.pem" \
  --tls-key "/etc/letsencrypt/live/relay.example.com/privkey.pem" \
  --jwt-secret "your-production-secret"

# 4. Create tunnel from client
export TOKEN=$(localup generate-token --secret "your-production-secret" --sub "api" --token-only)
localup --port 8000 --relay relay.example.com:4443 --subdomain api --token "$TOKEN"

# 5. Access your service
# HTTP:  http://api.relay.example.com
# HTTPS: https://api.relay.example.com

Key points:

  • The --domain flag determines subdomain construction
  • --domain localhost{subdomain}.localhost:PORT (for local development)
  • --domain relay.example.com{subdomain}.relay.example.com (for production)

Next Steps:

Was this page helpful?