A Docker container that exposes local services to your Tailscale network. Combines Tailscale VPN, Caddy reverse proxy, socat TCP relays, and a Web UI for browser-based management.
- Web UI - Browser-based management on port 8021
- Automatic TLS - Tailscale HTTPS certificates via Caddy
- HTTP/HTTPS Proxies - Configure reverse proxies through the UI
- TCP Relays - Forward non-HTTP protocols with socat
- Backup & Restore - Save and restore configurations
- Dual Authentication - Token or Tailscale network authentication
- Multi-Platform - Docker images for amd64 and arm64
- Features
- Why tailrelay?
- Technology Stack
- Quick Start
- Web UI
- Getting Started
- Development
- Troubleshooting
- Contributing
tailrelay provides secure remote access to self-hosted services:
- Secure Access: Tailscale's VPN eliminates port forwarding requirements
- Easy Configuration: Web UI handles setup without manual config files
- Automatic TLS: Caddy obtains and renews certificates via Tailscale HTTPS
- Protocol Support: HTTP/HTTPS proxies and TCP relays for any service
- Backup & Restore: Save and restore configurations
Useful for accessing Start9 services like BTCPayServer, LND, electrs, and Mempool without Tor.
| Component | Purpose | Documentation |
|---|---|---|
| Tailscale | VPN, MagicDNS, device authentication | Tailscale docs |
| Caddy | HTTP/2 reverse proxy, automatic HTTPS | Caddy docs |
| socat | TCP relay for non-HTTP services | socat manual |
| Web UI | Browser-based management (Go, HTML/CSS/JS) | See Web UI section |
# Pull the image
docker pull sudocarlos/tailrelay:latest
# Run the container
docker run -d --name tailrelay \
-v /path/to/data:/var/lib/tailscale \
-e TS_HOSTNAME=myserver \
-p 8021:8021 \
--net bridge \
sudocarlos/tailrelay:latest
# Access the Web UI and follow the Tailscale login link
open http://localhost:8021The Web UI provides browser-based management on port 8021.
- Dashboard - Real-time Tailscale connection status and system health
- Tailscale Management - Connect/disconnect and view network peers
- Caddy Proxy Management - Add, edit, delete, and toggle HTTP/HTTPS reverse proxies
- Socat Relay Management - Start, stop, and restart TCP relay processes
- Backup & Restore - Create and restore compressed tar.gz backups
The Web UI uses two authentication methods:
- Tailscale Network Authentication: Devices on your Tailscale network are automatically authenticated. If the container is not connected, the Web UI shows a Tailscale login link and polls until the device is connected.
- Token Authentication: A token is generated on first startup at
/var/lib/tailscale/.webui_tokenfor scripted access or legacy flows.
The Web UI runs on port 8021:
# Via Tailscale hostname (if HTTPS is enabled)
https://your-hostname.your-tailnet.ts.net:8021
# Or via local IP
http://localhost:8021- A Tailscale account with an active Tailnet (tailscale.com)
- HTTPS certificates enabled in Tailscale Admin console
- Docker or Podman installed
- Log into Tailscale Admin console and click DNS to enable MagicDNS.
- Tailnets created on or after October 20, 2022 have MagicDNS enabled by default.
- Review MagicDNS to understand how it works.
- Verify or set your Tailnet name
- Scroll down and enable HTTPS under HTTPS Certificates
-
SSH into your Start9 server:
ssh start9@SERVER-HOSTNAME
-
Create a directory for persistent data:
mkdir -p /home/start9/tailscale
-
Run the container:
sudo podman run --name start9.tailscale \ -v /home/start9/tailscale/:/var/lib/tailscale \ -e TS_HOSTNAME=start9 \ -p 8021:8021 \ --net start9 \ docker.io/sudocarlos/tailrelay:latest
-
Access the Web UI at
http://localhost:8021and follow the login link
Environment Variables:
TS_HOSTNAME- Tailnet machine nameRELAY_LIST- (Optional, deprecated) Comma-separated relay definitions. Use Web UI instead.MAX_LOG_BODY_SIZE- Max bytes for Caddy API request/response body logging (0 = full body)
Volume Mounts:
/var/lib/tailscale- Tailscale state, Web UI configs, backups
Network:
--net start9- Required to access Start9 services
See Tailscale Docker docs for more options.
For rapid iteration without rebuilding the full Docker image:
make frontend-build
make dev-buildThis compiles ./data/tailrelay-webui with build metadata (version, commit, date) and embeds the SPA assets.
Dev asset override:
Set WEBUI_DEV_DIR to a directory that contains templates/ and static/ (for example, webui/cmd/webui/web) to serve assets from disk instead of the embedded files.
Manual build:
cd webui
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo \
-ldflags="-w -s" \
-o ../data/tailrelay-webui ./cmd/webuiOption A: Mount Binary (Recommended)
Mount the local binary for instant updates:
# compose-test.yml
services:
tailrelay:
volumes:
- ./data/tailrelay-webui:/usr/bin/tailrelay-webui:ro
- ./tailscale/:/var/lib/tailscaleThen restart:
docker compose -f compose-test.yml restart tailrelayIteration workflow:
- Edit code in
webui/orwebui/frontend/ - Run
make frontend-build(if frontend changed) - Run
make dev-build - Restart container
- Test changes
Option B: Build Development Image
make dev-docker-buildThis builds a Docker image using the local binary.
# Development build with local binary
make frontend-build
make dev-build
make dev-docker-build
# Production build (multi-stage)
docker buildx build -t sudocarlos/tailrelay:latest .
# Show available targets
make help# Python test suite
python docker-compose-test.py
# Bash test suite
./docker-compose-test.shSetup:
- Copy
.env.exampleto.env - Edit variables (
TAILRELAY_HOST,TAILNET_DOMAIN) - Run tests
The dev-build target injects build information:
var (
version = "dev" // Git describe output
commit = "none" // Short commit hash
date = "unknown" // Build timestamp (UTC)
branch = "unknown" // Git branch
builtBy = "local" // System username
)Access these in webui/cmd/webui/main.go.
Check container status:
docker ps | grep tailrelayVerify port mapping:
docker port tailrelayCheck logs:
docker logs tailrelay | grep -i webuiVerify listening port:
docker exec tailrelay netstat -tulnp | grep 8021Retrieve token:
docker exec tailrelay cat /var/lib/tailscale/.webui_tokenEnsure you're accessing from Tailscale network or clear browser cache.
Validate configuration:
docker exec tailrelay caddy validate --config /etc/caddy/CaddyfileCheck Caddy logs:
docker logs tailrelay | grep -i caddyCheck relay status:
docker exec tailrelay ps aux | grep socatVerify listening ports:
docker exec tailrelay netstat -tulnp | grep socatTest target connectivity:
docker exec tailrelay nc -zv target-host target-portContributions welcome:
- Issues: GitHub Issues
- Pull Requests: GitHub PRs
- Documentation: Help improve docs or add examples
# Clone repository
git clone https://github.com/sudocarlos/tailscale-socaddy-proxy.git
cd tailscale-socaddy-proxy
# Build locally
docker build -t tailrelay:dev .
# Run tests
docker-compose -f compose-test.yml upSee Development section for WebUI development workflow.
- v0.3.0 - Logging, custom CA certs, and proxy management improvements
- v0.2.1 - Caddy API integration
- v0.2.0 - Web UI release
Open source project. See repository for license details.