LocalXpose Docker Image - Run Tunnels in Containers
Deploy LocalXpose instantly with Docker - no installation required. Perfect for containerized environments, CI/CD pipelines, and orchestrated deployments. Expose localhost to the internet from Docker containers.
Why Use LocalXpose with Docker?
- Zero Installation - Run immediately with
docker run
- Isolated Environment - No system dependencies or conflicts
- Docker Compose Ready - Integrate with your existing services
- CI/CD Compatible - Works in GitHub Actions, GitLab CI, Jenkins
- Persistent Configuration - Mount volumes for certificates and config
Quick Start
# First, export your access token
export LX_ACCESS_TOKEN=your_token_here # Get from https://localxpose.io/dashboard/access
# Then run LocalXpose, pointing to your service
docker run -it -e LX_ACCESS_TOKEN \
localxpose/localxpose:latest tunnel http --to YOUR_SERVICE:PORT
Replace YOUR_SERVICE:PORT
based on where your service runs:
- Container in same network: Use service name (e.g.,
webapp:80
) - Host machine (Docker Desktop): Use
host.docker.internal:8080
- Host machine (Linux): Use host IP or configure host.docker.internal
- External service: Use full URL (e.g.,
example.com:80
)
Complete Working Example
Note: in these examples we're using the Traefik whoami
image listening on port 80
as a demo application. Traefik isn't required for running LocalXpose in Docker.
version: '3.8'
services:
# Your application
web-app:
image: traefik/whoami
ports:
- '8080:80'
# LocalXpose tunnel
tunnel:
image: localxpose/localxpose:latest
restart: unless-stopped
environment:
- LX_ACCESS_TOKEN
volumes:
- ./lx-data:/home/nonroot/.localxpose # Persist certificates
command: tunnel http --to web-app:80
ports:
- '54538:54538' # Only if using Let's Encrypt
Run with: export LX_ACCESS_TOKEN=your_token && docker compose up
Installation & Versions
Docker Hub
docker pull localxpose/localxpose:latest
Version Compatibility
Version | Binary Path | User | Working Dir | Notes |
---|---|---|---|---|
23.11.1 | /app/loclx | root (0) | /app | Legacy version, works with older configs |
24.1.1+ | /ko-app/loclx | nonroot (65532) | none | Hardened security, different paths |
latest | /ko-app/loclx | nonroot (65532) | none | Recommended for new deployments |
Breaking Change: Commands like ./loclx
that worked in 23.11.1 will fail
in 24.1.1 or later. The binary location and user context have changed.
Connecting to Your Service
LocalXpose is a proxy - it needs to connect to your service. The networking configuration depends on where your service is running:
Service Location Guide
Your Service Location | Docker Network Mode | Use --to Parameter |
---|---|---|
Another container (Docker Compose) | Bridge (default) | Service name: app:80 |
Host machine (Docker Desktop) | Bridge (default) | host.docker.internal:8080 |
Host machine (Linux) | Bridge with extra_hosts | Host IP or configured hostname |
Host machine (Linux) | Host network | localhost:8080 |
External URL | Any | example.com:80 |
Examples by Service Location
Container-to-Container (Most Common)
services:
# Your application
webapp:
image: traefik/whoami # some app listening on port 80
# LocalXpose tunnel - connects to webapp service
tunnel:
image: localxpose/localxpose:latest
environment:
- LX_ACCESS_TOKEN
command: tunnel http --to webapp:80 # Use service name
Host Service - Docker Desktop (Mac/Windows)
# Docker Desktop provides host.docker.internal
docker run -e LX_ACCESS_TOKEN \
localxpose/localxpose:latest \
tunnel http --to host.docker.internal:8080
Host Service - Linux
services:
tunnel:
image: localxpose/localxpose:latest
environment:
- LX_ACCESS_TOKEN
# Option 1: Add host entry for Linux
extra_hosts:
- 'host.docker.internal:host-gateway' # or "host.docker.internal:172.17.0.1"
command: tunnel http --to host.docker.internal:8080
# Option 2: Use host network mode (simpler but less isolated)
# network_mode: host
# command: tunnel http --to localhost:8080
Configuration Reference
Environment Variables
Variable | Description | Required |
---|---|---|
LX_ACCESS_TOKEN | Your LocalXpose access token | Yes |
HTTP_LISTEN_ADDRESS | GUI bind address (0.0.0.0:54537 for Docker) | GUI only |
Port Requirements
Port | Purpose | When to Expose |
---|---|---|
54537 | Web GUI | If using GUI |
54538 | Let's Encrypt HTTP-01 | If generating certificates |
Command Options
Tip: If you notice TTY issues on your OS when running in Docker, try using --raw-mode
(-r
):
command: tunnel -r http --to webapp:80
This ensures proper output handling in non-interactive containers, though it's not always required.
Volume Mounts
docker run -v $(pwd)/lx-data:/home/nonroot/.localxpose \
localxpose/localxpose:latest
Important: Always mount a volume at /home/nonroot/.localxpose
(for 24.1.1 or later) or /app/.localxpose
(for 23.11.1) to persist:
- Let's Encrypt certificates (avoid rate limits!)
- Access tokens
- Configuration files
- Domain reservations
Note: The local directory name (lx-data
in examples) is arbitrary - choose any name that makes sense for your project.
Security Note: This directory contains private keys and access tokens. Keep it secure and never commit to version control:
# Add to .gitignore
lx-data/
.localxpose/
GUI Configuration (Optional)
The GUI is optional - tunnels can be managed via CLI commands or config.yaml. If you need the GUI:
When Proxying to Host Services
services:
tunnel:
image: localxpose/localxpose:latest
# For Linux with host network (simplest for GUI + host services)
network_mode: host
environment:
- LX_ACCESS_TOKEN
command: gui # Access at localhost:54537
When Proxying to Container Services
services:
tunnel:
image: localxpose/localxpose:latest
environment:
- LX_ACCESS_TOKEN
- HTTP_LISTEN_ADDRESS=0.0.0.0:54537 # Required for bridge network
ports:
- '54537:54537'
command: gui # Access at localhost:54537
Platform Note: Host network mode only works on Linux. Docker Desktop
(Mac/Windows) accepts --network host
but doesn't provide true host
networking due to the VM layer.
Let's Encrypt Certificates
Rate Limiting: Let's Encrypt allows only 5 certificates per unique domain
per 7 days. Without volume persistence, each container restart requests a new
certificate and will hit this limit after just 5 restarts! Each subsequent
certificate requires a 34-hour wait. Test with a different subdomain (e.g.,
test.yourdomain.com
) to avoid rate limiting your production domain.
Persistent Certificate Setup
services:
localxpose:
image: localxpose/localxpose:latest
volumes:
# CRITICAL: Persist certificates to avoid rate limits
- ./lx-data:/home/nonroot/.localxpose
ports:
- '54538:54538' # Required for Let's Encrypt HTTP-01
environment:
- LX_ACCESS_TOKEN
command: >
tunnel http
--reserved-domain yourdomain.loclx.net
--to web-app:80
Certificate Generation
# Generate certificate (persisted via volume)
docker run -v $(pwd)/lx-data:/home/nonroot/.localxpose \
-p 54538:54538 \
-e LX_ACCESS_TOKEN \
localxpose/localxpose:latest \
domain letsencrypt --domain yourdomain.loclx.net
Rate Limit Recovery
If you hit Let's Encrypt rate limits:
- Per-domain limit (5 certificates/7 days): Wait 34 hours between each new certificate request
- Testing strategy: Use a test subdomain (e.g.,
test.yourdomain.com
) for development to preserve your production domain's quota - Prevention: Always use persistent volumes to avoid regenerating certificates
- Full reset: Rate limits reset 7 days after the first certificate was issued
Complete Examples
Production Setup with Traefik
version: '3.8'
services:
traefik:
image: traefik:v2.10
command:
- '--providers.docker=true'
- '--entrypoints.web.address=:80'
volumes:
- '/var/run/docker.sock:/var/run/docker.sock:ro'
localxpose:
image: localxpose/localxpose:latest
environment:
- LX_ACCESS_TOKEN
volumes:
- ./lx-data:/home/nonroot/.localxpose # Persist certificates
ports:
- '54538:54538' # For Let's Encrypt
command: tunnel http --to traefik:80 --reserved-domain "*.yourdomain.com"
app1:
image: traefik/whoami
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.app1.rule=Host(`app1.yourdomain.com`)'
app2:
image: traefik/whoami
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.app2.rule=Host(`app2.yourdomain.com`)'
Development with Host Service
version: '3.8'
services:
# LocalXpose tunneling to host service on port 3000
tunnel:
image: localxpose/localxpose:latest
environment:
- LX_ACCESS_TOKEN
- HTTP_LISTEN_ADDRESS=0.0.0.0:54537 # For GUI
ports:
- '54537:54537' # GUI port
- '54538:54538' # Let's Encrypt port
volumes:
- ./lx-data:/home/nonroot/.localxpose
# Docker Desktop (Mac/Windows)
command: tunnel http --to host.docker.internal:3000
# Linux - add this:
# extra_hosts:
# - "host.docker.internal:host-gateway"
Using Docker GUIs (Synology, QNAP, Unraid)
Many NAS devices and home servers provide Docker through a web GUI. To run LocalXpose on these platforms:
- Image: Search for and download
localxpose/localxpose:latest
- Environment Variables: Add
LX_ACCESS_TOKEN
with your token value - Port Mappings: Map container port 54538 to host 54538 (for Let's Encrypt)
- Volume Mounts: Create a folder and map it to
/home/nonroot/.localxpose
(for persistence) - Command/Entrypoint: Override with your tunnel command, e.g.,
tunnel http --to host.docker.internal:8080
Platform-Specific Guides:
- Synology: Use Container Manager → Advanced Settings for environment, ports, and volumes
- QNAP: Use Container Station → Advanced Settings → Environment and Network
- Unraid: Use Docker tab → Add Container with Advanced View enabled
If you're using a platform not listed let us know at hello@localxpose.io!
Using Configuration Files
tunnels:
web:
type: http
to: webapp:3000
subdomain: myapp
api:
type: http
to: api:8080
subdomain: api-myapp
# Mount and use config file
docker run -v $(pwd)/config.yaml:/config.yaml \
-e LX_ACCESS_TOKEN \
localxpose/localxpose:latest \
tunnel config -f /config.yaml
Troubleshooting
Common Issues and Solutions
Problem | Cause | Solution |
---|---|---|
"GUI not accessible" | Missing HTTP_LISTEN_ADDRESS | Set HTTP_LISTEN_ADDRESS=0.0.0.0:54537 |
"HTTPS stopped working" | Let's Encrypt rate limit | Use volume mounts to persist certificates |
"loclx: command not found" | Using wrong path for version | Check version compatibility table |
"OCI runtime error" | Version incompatibility | Use specific version tag instead of latest |
"Cannot connect to localhost:8080" | Docker network isolation | Use host.docker.internal or container names |
"Permission denied" | Volume ownership issues | Check user ID (65532 for v24.1.1 or later) |
"Certificate not found" | No volume persistence | Mount volume at /home/nonroot/.localxpose path |
Debug Commands
# Check container logs
docker logs container_name
# Verify environment variables
docker exec container_name env | grep -E '(LX_ACCESS_TOKEN|HTTP_LISTEN)'
# Test connectivity from container
docker exec container_name wget -O- http://target:port
# Check certificate persistence
docker exec container_name ls -la /home/nonroot/.localxpose/
Getting Help
If you encounter issues not covered here:
- Check container logs:
docker logs container_name
- Verify network connectivity:
docker exec container_name ping target
- Ensure ports are mapped correctly:
docker ps
- Check our troubleshooting guide
- Contact support with your Docker version and complete error logs
Advanced Topics
Multi-Stage Builds
# Build stage
FROM golang:1.20 as builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# Runtime with LocalXpose
FROM localxpose/localxpose:latest
COPY --from=builder /app/myapp /app/myapp
CMD ["/app/myapp"]
CI/CD Integration
Easily implement preview app deployments, test webhooks, and use automated HTTPS endpoints from within your CI build and test pipelines, without deploying to a staging environment!
Using GitHub Actions?
Try our 🆕 LocalXpose Tunnel GitHub Action (opens in a new tab).
Not using GitHub Actions? Request support for your preferred CI/CD tunneling solution here (opens in a new tab).
name: Tunnel for Testing
on: [push]
jobs:
test:
runs-on: ubuntu-latest
services:
localxpose:
image: localxpose/localxpose:latest
env:
LX_ACCESS_TOKEN: ${{ secrets.LX_ACCESS_TOKEN }}
options: --network host
steps:
- uses: actions/checkout@v3
- run: npm test
Security Best Practices
- Never commit tokens - Use environment variables or secrets
- Use specific versions - Avoid
latest
in production - Limit exposed ports - Only map required ports
- Use read-only mounts - Add
:ro
to Docker socket mounts - Run as non-root - Use version 24.1.1 or later for better security
- Rotate tokens regularly - Update LX_ACCESS_TOKEN periodically