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:
- DNS wildcard record:
*.relay.example.com→ your server IP - Let's Encrypt certificates for your domain
- The
--domainflag 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:
- We generate self-signed TLS certificates for HTTPS support
- The relay server starts with HTTP/HTTPS support on ports 18080 and 18443
- A local HTTP server runs on port 13000
- The LocalUp client creates a tunnel from the local server to the relay
- Your local server is now accessible via
myapp.localhoston 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
--domainflag determines subdomain construction --domain localhost→{subdomain}.localhost:PORT(for local development)--domain relay.example.com→{subdomain}.relay.example.com(for production)
Next Steps: