Troubleshooting
This page covers the most common issues you may encounter when self-hosting Truss and how to resolve them.
CSRF token mismatch
Section titled “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
Section titled “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.
Session not established after login
Section titled “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
Section titled “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.