Issue
I’m running a single KeyCloak instance in a docker container (Quay). When KeyCloak is restarted all users have to login again. I want to preserve the logins over KeyCloak restart.
My approach to a solution
Configure the included/embedded infinispan cache to use file-based persistence. The cache file is written to a location where a Docker volume has been mounted. A custom image is used to give write permission for this file location, because without it I got ‘Permission denied’ errors. With the configuration below KeyCloak starts fine and shows no errors, however logins are NOT preserved. Users still have to login again after restart. Did I miss something?
Docker compose
services:
keycloak:
image: keycloak-with-chmod:latest # based on quay.io/keycloak/keycloak:19.0.3
command: >-
start
--db=postgres
--http-relative-path=/kc
--cache=ispn --cache-config-file=cache-ispn.xml
--proxy=edge
volumes:
- infinispan_data:/opt/keycloak/cache
- ./cache-ispn.xml:/opt/keycloak/conf/cache-ispn.xml:ro
volumes:
infinispan_data:
driver: local
Custom docker image
FROM quay.io/keycloak/keycloak:19.0.3
RUN mkdir /opt/keycloak/cache
RUN chmod 777 /opt/keycloak/cache
cache-ispn.xml
<infinispan xmlns="urn:infinispan:config:11.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:infinispan:config:11.0 http://www.infinispan.org/schemas/infinispan-config-11.0.xsd">
<cache-container name="keycloak">
<transport lock-timeout="60000"/>
<global-state>
<persistent-location path="/opt/keycloak/cache"/>
</global-state>
<local-cache name="realms">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<memory max-count="10000"/>
</local-cache>
<local-cache name="users">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<memory max-count="10000"/>
</local-cache>
<local-cache name="sessions">
<expiration lifespan="-1"/>
<persistence passivation="false">
<file-store path="/opt/keycloak/cache" shared="false" purge="false"/>
</persistence>
</local-cache>
<local-cache name="clientSessions">
<expiration lifespan="-1"/>
<persistence passivation="false">
<file-store path="/opt/keycloak/cache" shared="false" purge="false"/>
</persistence>
</local-cache>
<local-cache name="authenticationSessions">
<expiration lifespan="-1"/>
<persistence passivation="false">
<file-store path="/opt/keycloak/cache" shared="false" purge="false"/>
</persistence>
</local-cache>
<local-cache name="offlineSessions">
<expiration lifespan="-1"/>
</local-cache>
<local-cache name="offlineClientSessions">
<expiration lifespan="-1"/>
</local-cache>
<local-cache name="loginFailures">
<expiration lifespan="-1"/>
</local-cache>
<local-cache name="authorization">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<memory max-count="10000"/>
</local-cache>
<replicated-cache name="work">
<expiration lifespan="-1"/>
</replicated-cache>
<local-cache name="keys">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<expiration max-idle="3600000"/>
<memory max-count="1000"/>
</local-cache>
<local-cache name="actionTokens">
<encoding>
<key media-type="application/x-java-object"/>
<value media-type="application/x-java-object"/>
</encoding>
<expiration max-idle="-1" lifespan="-1" interval="300000"/>
<memory max-count="-1"/>
</local-cache>
</cache-container>
</infinispan>