Troubleshooting¶
Checking Status¶
CLI¶
Shows the current mode, relay info, and server/client state. If a daemon is running, it connects via gRPC to get live status.
Dashboard¶
The main page shows real-time status for all components with color-coded indicators:
- Green (up) — component is healthy
- Red (down/error) — component has failed
- Yellow — transitional state (starting/stopping)
Testing the Relay¶
Runs a 3-step diagnostic:
- DNS resolution — verifies the domain resolves correctly
- HTTPS/Caddy — confirms TLS certificate is valid and Caddy responds
- Xray + SSH — establishes a full tunnel and opens an SSH session
Log Levels¶
Increase verbosity for debugging:
CLI¶
Dashboard¶
Go to Config → Log Level → select debug → Save. Restart/reconnect to apply.
The log level is persisted to config.yaml. When set via the CLI --log-level flag, it also updates the config for dashboard consistency.
Console Logs¶
The dashboard shows real-time logs at the bottom of the main page. Click Clear to reset the log view.
Common Issues¶
DNS Not Resolving¶
After provisioning a relay, you need to create a DNS A record pointing your domain to the relay's IP address. The provisioning wizard will wait and retry until DNS resolves.
Fix: Create the A record with your DNS provider. Allow up to 5 minutes for propagation.
TLS Certificate Not Ready¶
Caddy automatically provisions a TLS certificate via Let's Encrypt. This requires:
- DNS must resolve to the relay IP
- Port 80 must be accessible (for ACME challenge)
Fix: Ensure the DNS record is correct and the relay firewall allows port 80.
Tunnel Drops and Reconnects¶
The client and server automatically reconnect with exponential backoff (2s → 4s → 8s → 16s → 30s max). Frequent reconnects may indicate:
- Unstable network connection
- Proxy or firewall dropping idle connections
- Relay VM resource constraints
Fix: Check the debug logs for specific error messages. Ensure keepalive traffic can pass through any intermediate proxies.
"local port … is already in use"¶
tw client connect checks every tunnel's local port before starting and refuses to connect if one is taken:
local port 8080 (→ server port 15432) is already in use — override it with
'tw client set-port 15432 <port>' or 'tw client connect --map <port>:15432'
Either stop whatever is using the port, or remap the tunnel to a free local port — persistently with tw client set-port 15432 4000, or for one run with tw client connect --map 4000:15432. See Local Port Conflicts & Overrides.
tw join Couldn't Store the Context¶
If a join succeeds (the issuer approved the SAS) but the granted context can't be stored — typically an unwritable config dir — the invite code is already burned. tw join saves the received profile as tw_rescue_<name>.twctx in the current directory (or the system temp dir) so nothing is lost.
Fix: resolve the storage problem (permissions, disk space), then run tw config import tw_rescue_<name>.twctx --activate. No new invite needed.
If instead you see config directory ... is not writable before the SAS prompt, the code is still unused — that preflight runs before any relay contact. Rerun as sudo tw join ... (Windows: an Administrator shell), or point TW_CONFIG_DIR at a directory you own.
Server-id Mismatch After Moving a Bundle to Another Machine¶
In the common case there is nothing to fix: a bundle exported by an older tw and imported under the current tw keeps its identity, because the server-id is taken from the CN of the bundled client certificate when xray.server_id is not set yet.
If tw refuses to start with:
client certificate CN %q does not match the stored server id %q; refusing to regenerate the CA (fix xray.server_id in config.yaml or restore the matching certificates)
(%q are the certificate CN and the stored id, e.g. "web-01-a1b2c3d4" and "laptop-a1b2c3d4"), read on. Current tw stores the server-id in config.yaml (xray.server_id) and never re-derives it from the hostname, so a bundle exported by an up-to-date tw can be imported on any machine (see Relay Authentication). Older versions re-derived the id from the local hostname on every run, so a relay or server bundle imported on a host with a different hostname could end up with a mismatched identity — this error, or tunnels the relay rejects because the certificates no longer match what it trusts.
Fix: on the machine that shows the problem, check the CN of the current certificate:
- The CN is the original id (the one in
xray.path,/tw/<server-id>): setxray.server_idinconfig.yamlto exactly that CN, then retry. - Otherwise (the older
twalready re-issued the certificates for the new hostname): upgradetwon the original machine, runtw config exportthere, and re-import the fresh bundle on the new machine withtw config import <bundle.twctx> --force.
After hand-editing config.yaml, restart the tw service so it picks up the change: tw service stop && tw service start (Windows: Restart-Service tw in an elevated PowerShell).
Old Relay Bundle Contains relay/.terraform/¶
Relay bundles exported by older versions include Terraform's provider cache (relay/.terraform/), which can be large. Nothing to do: the first Terraform call after importing (e.g. tw relay destroy, which runs terraform init first) deletes that directory and rebuilds the cache under <config-dir>/cache/terraform/<context-id>/, and tw config export never includes it, so any re-export is small. See File Layout.
Mode Enforcement Errors¶
Server-only commands (like tw server user invite) cannot run in client mode, and vice versa.
Fix: Ensure you're running the command on the correct machine, or check mode in your config.yaml.
Config Changed Notification¶
The dashboard shows "Configuration has changed. Restart/Reconnect to apply." when the config file on disk differs from what was loaded at startup.
Fix: Click the Restart (server) or Reconnect (client) button to apply changes.
"VLESS (with no Flow, etc.) is deprecated" Warning on the Relay¶
Relays provisioned by an older tw (which pinned Xray v26.2.6) may show this in the Xray logs (journalctl -u xray) at startup — newer Xray versions (the current pin is v26.6.27) no longer print it:
[Warning] common/errors: The feature VLESS (with no Flow, etc.) is deprecated, not recommended for using and might be removed. Please migrate to VLESS with Flow & Seed as soon as possible.
This is normal and harmless. It is an upstream xray-core advisory aimed at setups where VLESS is the only encryption layer. Tunnel Whisperer intentionally runs "plain" VLESS:
- Flow (XTLS Vision) requires a raw TCP+TLS transport. Tunnel Whisperer runs VLESS over XHTTP behind Caddy, where Flow does not apply by design.
- Seed (VLESS Encryption) would add encryption at the VLESS layer, but the stream is already wrapped in Caddy's TLS 1.3 with mutual TLS, and the payload inside is end-to-end SSH-encrypted. A third encryption layer adds nothing.
Upstream explicitly keeps plain VLESS working (it is their "non-removal" deprecation class), so no configuration change is needed.
Fix: None required — safe to ignore.