| Browser/curl rejects the cert |
Server is on ACME staging (untrusted). Switch off staging + restart, or use curl -k for testing. (Switching staging→production now moves the cached staging cert aside automatically so production re-issues.) |
| New domain shows a cert warning at first, but works in an incognito window (then your normal profile "catches up") |
A browser reached the URL before issuance/DNS settled and cached the failure. The client now verifies a browser-trusted certificate is actually being served before it prints Tunnel live — wait for that line, then hard-reload the tab (Cmd/Ctrl-Shift-R) to clear the cached error. |
404 no tunnel configured |
No frontend exists for that Host. Create one (serve). |
502 tunnel offline |
The frontend exists but no client is connected. Start the client; it auto-reconnects. |
403 Blocked host (Rails/Django/etc.) |
Your app rejects the public hostname. Add it to the framework's allowed-hosts (e.g. Rails config.hosts). |
| First request to a new domain is slow / fails |
Cert is issuing on demand; ensure DNS points at the server and :80/:443 are reachable. |
| Too many requests (429) |
Repeated bad API keys/tokens trigger a temporary per-IP lockout. Wait, then use the correct credential. |
Client in Docker logs cannot reach local target |
SEIKAN_TARGET is dialled from inside the client container. Use the target's service/container name and the port it listens on in that network (app:80), with the client attached to that network — not an address on the docker host. If the target really is a process on the host, host.docker.internal needs extra_hosts: ["host.docker.internal:host-gateway"] (Linux; Docker Desktop has it) and a process not bound to 127.0.0.1 alone. |
| TCP frontend says "Tunnel live", but its public port refuses connections |
The port has to be published by the server container as well: it must fall inside both SEIKAN_TCP_RANGE and the ports: range mapped for the server. A port outside the published range binds fine inside the container and is simply unreachable from outside. |
| Container exits immediately: a frontend for … already exists, and this client key does not own it |
Another client key owns that domain or port. The server lists a key only its own frontends, so it won't appear in seikan list either — find it in the admin UI (Clients), or point this container somewhere else. |
| Container restarts every ~30s with unauthorized |
SEIKAN_API_KEY is wrong or was revoked. The container exits rather than retrying (retrying a bad key just locks the address out), so restart: unless-stopped turns it into a slow loop — fix the key and the next restart picks it up. |
SEIKAN_DOMAIN seems to be ignored |
SEIKAN_TOKEN is also set. A pre-minted token already identifies its frontend, so it and SEIKAN_API_KEY are alternatives; the key wins and the container logs a warning saying the token was ignored. |
| Two containers keep taking a tunnel from each other |
Both were given the same domain or port. Each start rotates the connect token, invalidating the other's — run exactly one container per frontend. The one that loses exits with a message naming this cause. |
seikan forward connects but every relayed connection is refused/closed |
No provider is currently connected for that peer frontend — start seikan serve --peer on the box with the actual service. forward itself doesn't need the provider up to connect, only to relay a real connection through. |
seikan forward/serve --peer says the token is the wrong role |
A peer frontend has two independent tokens. serve --peer needs the provider token; forward needs the consumer token (printed once at creation, never re-shown). Using the other one is rejected rather than silently misbehaving. |