New stack tailscale-proxy: a LAN HTTP proxy (192.168.0.35:3128) that egresses
through the dedicated Hetzner Tailscale exit node fedora-technohim, so proxied
LAN clients appear on the internet as 89.167.72.79 without routing the docker
host itself through the tunnel.
- ts-proxy: Tailscale in userspace mode (--exit-node=100.121.234.85), exposing a
SOCKS5 proxy on :1055. Userspace mode makes no host route/firewall changes.
- tinyproxy: LAN-facing forward proxy on :3128 (Allow 192.168.0.0/22), upstream
socks5 to ts-proxy. Access log shipped to Alloy/Loki via the syslog
log-driver (tag tailscale-proxy).
The Tailscale auth key stays outside Git in /mnt/containers/tailscale-proxy/
ts.env (0600); node state persists in the state/ dir. tinyproxy.conf is tracked
here as source of truth. See tailscale-proxy/README.md.
Also adds logging/grafana-dashboards/tailscale-proxy.json: a Loki dashboard
(request rate by method, top destinations, top clients, totals, live access
log) built from the tinyproxy syslog stream, matching the existing dashboards'
schemaVersion 39 + ${datasource} template-var pattern.
Full build record in ops-knowledge
diagnostics/2026-07-23-02-tekhnohim-docker-tailscale-exit-proxy-stack.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tailscale exit-node LAN proxy
LAN HTTP proxy that egresses through the dedicated Hetzner Tailscale exit node,
so proxied clients appear on the internet as 89.167.72.79 without routing the
docker host itself through the tunnel.
- client endpoint:
192.168.0.35:3128/tcp(HTTP/HTTPS forward proxy); tinyproxyaccepts LAN clients (Allow 192.168.0.0/22) and forwards upstream over SOCKS5 to the Tailscale container;ts-proxyruns Tailscale in userspace mode and sends outbound traffic through the exit nodefedora-technohim(100.121.234.85);- userspace mode adds no host route/firewall changes — the docker host's own services (NPM, KMS, IPsec) keep egressing via the normal gateway;
- tinyproxy's access log ships to the logging stack (Alloy → Loki) via the
syslog log-driver (
tag: tailscale-proxy); the Grafana dashboardlogging/grafana-dashboards/tailscale-proxy.jsonvisualises it.
The stack deliberately keeps credential-bearing / stateful files outside Git:
| Host path | Purpose | Required mode |
|---|---|---|
/mnt/containers/tailscale-proxy/ts.env |
TS_AUTHKEY=<tailscale auth key> |
0600, owner root:root |
/mnt/containers/tailscale-proxy/state/ |
Tailscale node state (persists identity across restarts) | dir, owner root:root |
/mnt/containers/tailscale-proxy/tinyproxy.conf |
tinyproxy config (also tracked in this directory as the source of truth) | 0644 |
Before deploying: create ts.env on the host with a Tailscale auth key, and in
the Tailscale admin console approve the exit node and allow this node to use it.
Deploy this directory as a Portainer Git stack named tailscale-proxy, or run it
with Docker Compose using project name tailscale-proxy. The absolute
configuration files must already exist on the host before deployment.
Full build record and rationale (userspace vs TUN dead-ends, SOCKS5-vs-HTTP,
monitoring) is in ops-knowledge:
diagnostics/2026-07-23-02-tekhnohim-docker-tailscale-exit-proxy-stack.md.