Troubleshooting
This page covers the most common issues you may encounter when self-hosting Truss and how to resolve them.
CSRF token mismatch
Symptoms: 403 Forbidden or “CSRF token mismatch” errors on login, registration, or settings forms.
Causes:
- Cross-domain cookies: If your dashboard and API are on different subdomains (e.g.
app.example.comandapi.example.com), the CSRF cookie must be set withDomain=.example.comso both origins can read it. - Stale tokens after login: After a successful login, the session cookie replaces the anonymous CSRF token. If a stale CSRF token remains in a duplicate cookie, the server rejects it.
- SameSite mismatch: CSRF cookies should use
SameSite=Lax. If your reverse proxy strips or rewrites cookie attributes, the browser may not send the cookie.
Solutions:
- Ensure
CSRF_COOKIE_DOMAINis set to your root domain (e.g..example.com). - Clear all cookies and try again — duplicate cookies from previous sessions can cause mismatches.
- Use
parseAllCookieValues()on the server side to handle cases where the browser sends multiple cookies with the same name. - Verify your reverse proxy (Nginx, Caddy, Traefik) is not stripping
Set-Cookieheaders.
SMTP port 465 blocked
Symptoms: Email sending fails with connection timeout or ECONNREFUSED on port 465.
Cause: Many hosting providers (Hetzner, AWS Lightsail, GCP) block outbound port 465 (implicit TLS) to prevent spam.
Solution:
Use port 587 (explicit TLS / STARTTLS) instead. Update your environment variables:
SMTP_PORT=587SMTP_HOST=smtp.your-provider.comPort 587 with STARTTLS is the recommended standard for authenticated mail submission and is not blocked by most providers.
Demo mode leaking into authenticated sessions
Symptoms: After logging in, you still see the demo dashboard or get redirected to /demo/.
Cause: The demo session cookie persists after login, and the session refresh logic detects the demo tenant.
Solution:
- Clear all browser cookies for your Truss domain.
- Navigate directly to your dashboard URL (not
/demo/). - If the issue persists, open DevTools > Application > Cookies and delete any cookie containing
demoorory_kratos_session.
The refreshSession guard is designed to block demo tenants on non-/demo/ paths, but stale cookies can bypass this check.
Session not established after login
Symptoms: Login succeeds (Kratos returns a session), but the dashboard shows “Not authenticated” or redirects back to login.
Causes:
- CORS misconfiguration: The browser blocks the
Set-Cookieheader if the API origin is not inCORS_ALLOWED_ORIGINS. - Missing credentials mode: Fetch requests must include
credentials: 'include'for cross-origin cookie handling. - Cookie domain mismatch: The session cookie domain must match the dashboard domain.
Solutions:
- Verify
CORS_ALLOWED_ORIGINSincludes your dashboard URL (e.g.https://app.example.com). - Check that the session cookie has
Domain=.example.com,SameSite=None,Secure=true, andHttpOnly=true. - Confirm your dashboard is served over HTTPS — browsers reject
SameSite=Nonecookies on HTTP. - In DevTools > Network, inspect the login response headers. Look for
Set-Cookieand verify the browser accepts it (check the “Cookies” tab on the request).
502/503 from Ory services
Symptoms: API returns 502 Bad Gateway or 503 Service Unavailable for authentication, authorization, or OAuth2 endpoints.
Cause: One or more Ory services (Kratos, Keto, Hydra) are not running or not reachable from the API server.
Diagnostic steps:
- Check service health:
# Kratoscurl -f http://localhost:4433/health/alive
# Ketocurl -f http://localhost:4466/health/alive
# Hydra (if using OAuth2)curl -f http://localhost:4445/health/alive- Verify environment variables point to the correct hosts:
# Required for authKRATOS_PUBLIC_URL=http://localhost:4433KRATOS_ADMIN_URL=http://localhost:4434
# Required for authorizationKETO_READ_URL=http://localhost:4466KETO_WRITE_URL=http://localhost:4467
# Required for OAuth2HYDRA_ADMIN_URL=http://localhost:4445- Check Docker container status:
docker ps --filter "name=kratos" --filter "name=keto" --filter "name=hydra"- Review service logs for startup errors:
docker logs truss-kratos-1 --tail 50docker logs truss-keto-1 --tail 50Common fixes:
- Restart the crashed service:
docker compose up -d kratos keto hydra - If Kratos fails with a migration error, run:
docker compose exec kratos kratos migrate sql -e --yes - Ensure no port conflicts on 4433, 4434, 4445, 4466, 4467.