Portainer SSO with Keycloak using OAuth2
Configure Keycloak SSO for Portainer CE using built-in OAuth2 authentication. Covers confidential client setup, Portainer authentication settings, and the URL fields for authorization, token, and userinfo.
KeycloakPro Team
KeycloakPro Team
Introduction
Portainer Community Edition includes OAuth2 authentication under Settings → Authentication. You fill in Keycloak's authorization, token, and userinfo URLs, save the config, and Portainer redirects unauthenticated users to Keycloak on their next visit.
This guide covers Portainer CE 2.19+ with Keycloak 24+. Portainer Business Edition adds OIDC discovery and automatic team sync from group claims. This guide focuses on CE.
Prerequisites
- Keycloak 24+ with HTTPS
- Portainer CE 2.19+ with admin access
- Portainer running at a fixed URL
Step 1 — Create the Keycloak confidential client
In the Keycloak admin console:
- Clients → Create client
- Client type:
OpenID Connect - Client ID:
portainer - Click Next
Capability config:
- Client authentication: ON (confidential client)
- Standard flow: ON
- Direct access grants: OFF
- Click Next
Login settings:
- Root URL:
https://portainer.example.com - Valid redirect URIs:
https://portainer.example.com/* - Valid post logout redirect URIs:
https://portainer.example.com/ - Web origins:
https://portainer.example.com

1.1 — Copy the client secret
Clients → portainer → Credentials → copy the client secret.

Step 2 — Configure OAuth in Portainer
In the Portainer admin console:
- Settings → Authentication
- Authentication method: OAuth
- Provider: Custom
Fill in:
| Field | Value |
|---|---|
| Client ID | portainer |
| Client secret | (paste from Step 1.1) |
| Authorization URL | https://keycloak.example.com/realms/YOUR_REALM/protocol/openid-connect/auth |
| Access token URL | https://keycloak.example.com/realms/YOUR_REALM/protocol/openid-connect/token |
| Resource URL | https://keycloak.example.com/realms/YOUR_REALM/protocol/openid-connect/userinfo |
| Redirect URL | https://portainer.example.com |
| Logout URL | https://keycloak.example.com/realms/YOUR_REALM/protocol/openid-connect/logout?redirect_uri=https%3A%2F%2Fportainer.example.com |
| Scopes | openid profile email |
| Auth style | In params |
| User identifier | preferred_username |
Replace YOUR_REALM and keycloak.example.com with your realm and hostname.
The Logout URL uses a URL-encoded redirect URI. Replace the encoded portion with your own Portainer URL if it differs: https://portainer.example.com encodes to https%3A%2F%2Fportainer.example.com.

2.1 — Hide the internal login prompt (optional)
At the bottom of the Authentication settings page, toggle Hide internal authentication prompt. This removes the username/password form and sends users directly to Keycloak.
Keep the toggle OFF during initial testing. That way, you can still log in with the local admin account if the OAuth config breaks.

Save the settings.
Step 3 — Test the login
Open a private browser window and go to https://portainer.example.com. Portainer shows an OAuth sign-in button alongside the standard form, or redirects directly to Keycloak if the internal prompt is hidden.

After login, Portainer creates the user account and shows the environments list:

Step 4 — Automatic team assignment (Portainer Business Edition)
Portainer CE creates OAuth users with no team membership. An admin must manually assign them to teams after first login.
Portainer Business Edition adds automatic team assignment via a groups claim. If you have BE:
- Add a Group Membership mapper to the Keycloak client (same as the GitLab guide, Step 1.2)
- In Portainer → Settings → Authentication → Automatic team membership: ON
- Set the claim attribute to
groups - Add mappings: Keycloak group name → Portainer team name
With this in place, users land in the correct Portainer teams on first login without any manual admin action.
Troubleshooting common issues
"Invalid redirect_uri" from Keycloak
Portainer sends a redirect URI during the OAuth flow. Check Keycloak's error page for the exact URI received, then compare it to the Valid redirect URIs on the client. A slight difference in the path or a missing slash triggers this. Using https://portainer.example.com/* as the valid redirect URI (with a wildcard) covers Portainer's internal OAuth path variations.
Login succeeds but Portainer shows "Your account was not found"
Portainer creates a new account on first OAuth login only if the user identifier claim is present. If preferred_username is missing from the userinfo response, Portainer can't build an account. Check Clients → portainer → Client scopes and confirm a mapper for preferred_username is active. Alternatively, change the User identifier field to email.
Logout button doesn't sign out of Keycloak
The Logout URL must include a redirect_uri parameter pointing back to Portainer. Without it, Keycloak's end-session endpoint doesn't redirect after logout. The value must be URL-encoded: https://portainer.example.com becomes https%3A%2F%2Fportainer.example.com. If the parameter is missing or malformed, Portainer's local session clears but the Keycloak session stays active.
OAuth button disappears after a Portainer restart
Portainer stores OAuth config in its database. The button disappears only if the config was cleared or the database was reset. Go back to Settings → Authentication and confirm the OAuth configuration is still present.
Production checklist
- HTTPS on both Portainer and Keycloak
- Client secret stored in a secrets manager, not in notes or a screenshot
- Hide internal authentication prompt left OFF until SSO is verified
- User identifier claim (
preferred_usernameoremail) confirmed present in the Keycloak userinfo response - Logout URL configured with a URL-encoded
redirect_uri - At least one local Portainer admin account kept active as a recovery path
Need help integrating Portainer with Keycloak?
We deliver production-ready Portainer + Keycloak integrations in 1–3 weeks.
Fixed-price, zero vendor lock-in, full source code ownership.