Troubleshooting¶
"Handled" errors are prefixed with a stable code [CODE] in the output,
and the message includes a remediation (-> ...).
Error codes¶
AD configuration (configure-ad.ps1)¶
| Code | Meaning | Remediation |
|---|---|---|
LDAPS-E-CAEXPORT |
Unable to export the LDAPS CA (TLS + local store + certutil all failed). | Enable LDAPS on the DC (server certificate in LocalMachine\My) or ADCS. See Scenario 1. |
AD-E-NTAUTH |
Publishing the Teleport CA to NTAuth failed. | Run configure-ad.ps1 as Enterprise Admin (Domain Admin is not enough). Without the CA in NTAuth: "smart card isn't supported for your account". |
General API (authentication, scripts, callbacks)¶
| Response | Meaning | Remediation |
|---|---|---|
429 {error:'too_many_attempts', retryAfterSeconds} + Retry-After header |
Per-user login lockout after too many failures (in addition to the limit of 5 attempts/minute). | Wait Retry-After seconds; the delay grows with each additional failure. |
403 on password login |
TELEPORT_ONLY=true: only Teleport SSO login is allowed. |
Go through the Teleport application, or reinstall without --sso-only. |
410 on /api/i/<id>/<key>/install.sh |
install.sh is served only once. |
Generate a new deployment. |
409 on /api/i/... |
Deployment not ready or expired. | Regenerate the deployment. |
403 on /api/i/... (from a server) |
Not from Tenantry: an Access policy, WAF rule or country block in front of it refuses that server. | See Exposure: never expose raw. |
401 on /api/i/<id>/<key>/steward-bundle.sh |
The Steward bundle link is wrong or older than 30 minutes. | Generate the install command again on the Steward page. |
409 ad_output_already_received (callback) |
The AD output has already been received: a second write is refused. | Generate a new deployment if the data must change. |
422 insecure_public_base |
The public URL (PUBLIC_API_BASE) is not an acceptable https:// URL (localhost, Vite port...). |
Fix --public-url / PUBLIC_API_BASE. |
422 validation_failed |
Invalid deployment fields. | Read validation in the response. |
Steward integration API (/api/steward/*)¶
| Response | Meaning | Remediation |
|---|---|---|
422 {ok:false, issues} (preview, apply, verify) |
Invalid answers (https://host[:port] URI, host:port proxy, cluster name, distinct role names, freshness 30-3600 s...). |
Fix the fields listed in issues (path + message). |
409 {error:'would_overwrite', conflicts:[...]} (apply) |
A role or application with the same name exists without the label tenantry-managed: 'true'; nothing was modified. |
Check conflicts, rename, or confirm "Replace existing" (resends with overwrite:true). |
422 reserved role name |
Name reserved by Teleport (access, editor, auditor, admin, tenantry, member, requester, reviewer, prefixes teleport-, bot-, preset-). |
Choose another name. |
apply: pending:true |
Application created but not yet readable after 5 s (Teleport auth cache). | Run Verify again. |
502 on apply, steps[].name = "roles" |
tctl create -f of the roles failed; the app step is skipped. |
Read steps[].output; rules permissions of the Tenantry role (Steward integration, a), tctl version. |
502 on apply, steps[].name = "app" |
Roles created, application registration refused. | Typically access to app denied: Tenantry role without matching app_labels (Steward integration, a). |
502 {error:'resource_state_unknown'} or one of the teleport_* codes above (apply, verify) |
The state of the Teleport resources could not be read (tctl unavailable or failing); nothing is modified or asserted. |
Check tctl authentication (tctl status), then retry. |
verify: role <name> failed |
Role missing from the cluster. | Run Apply again. |
verify: application <name> failed |
Application not registered (or Teleport cache lagging). | Apply, then run Verify again after a few seconds. |
verify: application service failed |
No app service serves the application (tctl apps ls does not list it). |
app_service block from teleport-app-service.snippet.yaml on the agent, or resources with the label. |
verify: URI failed |
Teleport URI differs from the expected URI. | Re-apply with the correct URI. |
verify: label failed |
The application's label does not match the roles'. | Re-apply. |
Fleet API (/api/nodes/*)¶
| Response | Meaning | Remediation |
|---|---|---|
400 |
Node identifier that is not a UUID, message longer than 200 characters, duration not in the list (1, 4, 12, 24, 72, 168 h or null), or unknown field. |
Fix the request; use the interface. |
403 on lock / unlock |
insufficient_level: read-only technician on this client. (Another client's node answers 404.) |
Operator level on the client (Technicians) or an administrator. |
404 not_found |
server-id not in the fleet (agent deleted or never joined). |
Refresh the page. |
404 not_locked (unlock) |
No active lock on this node. | Refresh: it may have expired or been removed. |
409 protected_node |
The node runs Auth or Proxy (reason: protected_service): locking it would cut off the cluster; or its type is not lockable (reason: unsupported_kind). Attempt logged as denied with refused: <reason>. |
Do not lock it. |
409 already_locked |
The node already has an active lock. | Unlock first to change the message or duration. |
502 teleport_credentials_expired |
The tctl identity file has expired (your credentials have expired, please log in using tsh login). Body: message + hint, never the raw tctl output. |
--auth identity: renew with tctl auth sign --user=<user> --format=file --out=identity --ttl=<duration>, then replace the mounted file (no restart). Long-term: --auth machine-id. --auth machine-id: tbot stopped renewing, see "Machine ID (tbot)" below. |
502 teleport_access_denied |
Tenantry role lacks a permission (access denied to perform action "update" on "lock"). The body carries only verb and resource ([a-z_], 40 chars max). |
Add the rule to Tenantry role (Steward "Generated files", option allowNodeLock). |
502 teleport_identity_missing |
The configured identity file (TELEPORT_IDENTITY_FILE) is missing or unreadable. |
Check the mounted volume and the path. |
502 teleport_unreachable |
Teleport is not responding (connection refused, timeout, unknown host). | Teleport service running, network and firewall between Tenantry and --auth-server. |
502 teleport_unavailable |
Unable to read the cluster for another reason (tctl failing); never an empty list. |
tctl status from the container, valid identity (see "Application tctl auth"). |
502 lock_failed |
tctl lock refused. |
Tenantry role: lock with create and update (Client proxies). |
502 unlock_failed |
Lock removal refused. | delete permission on lock. |
200 with pending:true |
Action done, but the Teleport auth cache did not catch up within 5 s. | Refresh a few seconds later. |
Common symptoms (no code)¶
| Symptom | Likely cause | Action |
|---|---|---|
configure-ad: "LDAPS TLS export failed … forcibly closed" |
LDAPS not configured (no server certificate). | Install a server certificate (ADCS or self-signed), retest the port 636 handshake. |
| Teleport RDP: "smart card isn't supported for your account" | Teleport CA missing from NTAuth and/or RDP cert template not applied to the target server. | Teleport Access Policy GPO linked to the server's OU + Gateway+AD scenario (configure-ad) run (publishes the CA to NTAuth). |
Installer, wizard, tunnel and local Teleport¶
Errors printed by scripts/install.sh (it exits non-zero and changes nothing before validation succeeds).
| Message or symptom | Cause | Action |
|---|---|---|
No option given. Run it in a terminal for the guided installation... |
No terminal (ssh without a tty, cron, CI) and no option. | Use ssh -t, or pass the options (the equivalent command printed by the wizard). |
| Wizard: "No Teleport detected on this server" | No Teleport container whose tctl status answers, no local tctl with /etc/teleport.yaml. Not run as root. |
Run with sudo. Name the container (docker ps), or choose "Elsewhere". |
--teleport-local auto: several local Teleport auth services |
More than one local Teleport. | Choose one: --teleport-local docker:CONTAINER. |
tctl does not answer in <container> / tctl status failed there |
The container is not the auth service, or you are not root. | Test it: docker exec <container> tctl status. |
container <name> is only on Docker's default bridge network |
Names do not resolve on the default bridge. | Run Teleport on a user-defined network (Compose does), or pass --auth-server ADDR:3025 and --add-host. |
--teleport-local always uses --auth machine-id / --bot-token-file is not used with --teleport-local |
Conflicting options. | Remove --auth and --bot-token-file. |
tctl create of the tenantry role failed |
The local tctl could not create the role. |
Run as root. Check docker exec <container> tctl create --help; read the Teleport logs. |
tctl create of the role steward-admin failed |
Same, for a Steward console role. | Same; or create it yourself (scripts/install.sh --print-console-roles). An existing role of that name is never replaced. |
the image ... prints a different tenantry role than this installer |
The installer and the image are not the same release (or the image was tampered with). Nothing was created in Teleport. | Run the install.sh of the release you install (git checkout v<version>), or --build. |
--version X.Y.Z is required / "latest" is refused |
First install without a release. | Pass --version <x.y.z> (or --build, --image). Re-runs keep the pinned image. |
the signature of ... does not verify / --require-signature: cosign is not installed |
cosign verify failed for the pulled digest, or cosign is missing with --require-signature. |
Check the version and your registry/mirror; install cosign. Do not bypass a failed verification. |
--build with tctl X.Y.Z: pass --teleport-commit |
Only the commit of the embedded tctl version is pinned in the Dockerfile. | --teleport-commit $(git ls-remote https://github.com/gravitational/teleport 'refs/tags/vX.Y.Z^{}' \| cut -f1) after checking it. |
no free /29 for the edge network / Docker Pool overlaps |
Every candidate subnet of the edge network is in use. | Pass --edge-subnet <a private /29 used nowhere else>. |
Every user hits the sign-in rate limit together (behind --expose proxy) |
No --trust-proxy: all requests come from the proxy's address. |
Re-run with --trust-proxy <proxy address> (printed at the end of the install for a proxy on this server). |
could not read the join token from tctl's output |
tctl bots add output changed or failed. The output is not shown on purpose (it may contain a token). |
Create the bot by hand (variants) and use --auth machine-id --bot-token-file. |
--auth machine-id needs --bot-token-file on the first install |
Teleport elsewhere, first install, no token. | Create the bot and save the token. |
--tls applies to --expose local |
--tls with --expose proxy or cloudflare-tunnel. |
Drop --tls: Cloudflare or your proxy serves the certificate. |
--public-url must be https://<hostname> ... with --expose cloudflare-tunnel |
A port or http:// in the URL. |
Use https://tenantry.acme.ch. |
--expose cloudflare-tunnel needs --tunnel-token-file FILE on the first install |
No terminal to type the token. | Pass --tunnel-token-file. |
does not look like a tunnel token |
Wrong string. | Copy the long base64 string after --token in the command Cloudflare shows. |
--public-url must be https://name:port with --tls |
The URL does not match --tls-domain and --https-port. |
Use https://<name>:<https-port> (port 443 may be omitted). |
| Port 443 or 80 already in use | Teleport (443) or another web server. | --https-port 9443, and --no-http-redirect if port 80 is taken. The wizard does this itself. |
a public CA cannot issue for this name |
The name ends with .local, .lan, .internal... |
Use a name under your public domain, or --acme-server with your own ACME server. |
certificate issuance failed |
Wrong or under-privileged DNS token, or port 80 not reachable (HTTP-01). | docker compose -p tenantry-<name> logs acme. Delete <dir>/secrets/acme-dns.env to type the credentials again. Test with --acme-server letsencrypt_test. |
--dns-setting X: this variable is a secret |
A secret passed on the command line. | Put it in --dns-credentials-file. |
--dns-provider dns_xxx: name its secret variables with --dns-secret-var |
A provider Tenantry does not know. | See Other acme.sh provider. |
could not chown ... to 65532 / ... to 1000:1000 |
Not run as root. | Re-run with sudo. cloudflared or tbot cannot read its token file otherwise. |
Cloudflare Tunnel¶
| Symptom | Cause | Action |
|---|---|---|
cloudflared restarts or logs an authentication error |
Wrong, revoked or unreadable token. | docker compose -p tenantry-<name> logs cloudflared. Replace the token: delete <dir>/cloudflared/tunnel-token, run install.sh again. |
| Error 502 or 1033 at the public name | No public hostname on the tunnel, or the service is not http://tenantry:8443. |
Check the tunnel's Public hostname. |
| Public name answers without a login page | The Access application is missing or does not cover the hostname. | Create it first, then check the hostname. |
| Scripts on a client server get a Cloudflare login page (HTML) or 403 | /api/i and /api/callback are behind Access. |
Add the Bypass application for these two paths (Exposure). |
tsh login or an agent cannot join through the tunnel |
Access blocks non-browser clients. | Letting tsh and agents through. |
| Teleport behind the tunnel: web UI loads but sessions fail | Match SNI to Host off, or the origin certificate does not match. | Turn on Match SNI to Host; use https://teleport:443. |
| A Steward console behind Access is "down" in Tenantry | The poll is a non-browser client. | Bypass or Allow for Tenantry's egress IP. |
Machine ID with a local Teleport¶
| Symptom | Cause | Action |
|---|---|---|
tbot produced no identity after 60 s right after a local install |
tbot cannot reach <container>:3025 (network), or CA pins do not match. |
docker compose -p tenantry-<name> logs tbot. Check compose.teleport-network.yml and that the Teleport container is on that network. Re-run install.sh --teleport-local auto: it issues a new join token for the existing bot. |
| Token expired before the join | The join token has a short lifetime. | Re-run the installer: a new token is issued (tctl bots instances add tenantry). |
Debugging¶
Useful commands¶
# LDAPS (from the DC or a machine that can reach it)
$h='dc1.example.local'; try { $t=[Net.Sockets.TcpClient]::new($h,636); $s=[Net.Security.SslStream]::new($t.GetStream(),$false,{$true}); $s.AuthenticateAsClient($h); "OK $($s.RemoteCertificate.Subject)"; $s.Dispose(); $t.Dispose() } catch { "KO: $($_.Exception.Message)" }
Application tctl auth¶
docker compose -p tenantry-<name> exec tenantry tctl status
On failure: --auth local → check the /etc/teleport.yaml + /var/lib/teleport mounts
and permissions (root). --auth identity → check TELEPORT_IDENTITY_FILE (present,
renewed) and TELEPORT_AUTH_SERVER. --auth machine-id → see below. Unknown certificate → --ca-file; unresolved
name → --add-host. Health: curl http://127.0.0.1:8443/api/health.
Machine ID (tbot)¶
docker compose -p tenantry-<name> ps tbot # running? restarting in a loop?
docker compose -p tenantry-<name> logs --tail 100 tbot
docker compose -p tenantry-<name> exec tenantry ls -l /identity/ # identity rewritten every renewal_interval
| Symptom | Likely cause | Action |
|---|---|---|
install.sh: "tbot produced no identity after 60 s" (exit 1, token kept) |
Wrong, already used or expired join token; --auth-server unreachable; private CA. |
Read the tbot logs. Expired/used token: tctl bots instances add tenantry (new token for the same bot), save it again, re-run install.sh --bot-token-file. CA: --ca-file (applies to tbot too). Name: --add-host. |
tbot logs: permission denied on /var/lib/teleport/bot, /identity-out or the token |
Volumes not owned by uid 1000 (tbot-init failed), or install.sh not run as root (token folder not chowned: the script warns). |
docker compose -p tenantry-<name> logs tbot-init; re-run install.sh as root. |
tbot logs: token ... not found / already used after a re-install |
The tbot-storage volume was deleted: the bot lost its renewable identity and the old token is gone (single-use, deleted). |
New token: tctl bots instances add tenantry, then install.sh --bot-token-file. |
tbot logs: renewal access denied / bot locked / generation mismatch |
The bot was locked or deleted, or its identity was used twice (copied volume): Teleport refuses the renewal by design. | Remove the lock (tctl get locks), or recreate the bot (tctl bots rm tenantry, then the Security page commands). Never copy tbot-storage between hosts. |
Dashboard / Security page: "Machine ID has not renewed ... since ..."; alert identity_not_renewed |
The identity file was not rewritten within 2 x renewal_interval (tbot stopped, cannot reach Teleport, or crash-loops). The current certificate works until its certificate_ttl ends. |
docker compose -p tenantry-<name> up -d tbot, then the logs. Once the file is rewritten the warning and the alert resolve by themselves. |
| "Machine ID has not produced Tenantry's Teleport credentials yet" | No /identity/identity at all: tbot never joined. |
First row of this table. |
tbot image pull fails (manifest unknown) |
No tbot-distroless tag for that exact --teleport-version, or no access to public.ecr.aws. |
Use the cluster's exact version, or --tbot-image <mirror>/tbot-distroless:<version>. |