Preserve user sessions over Keycloak restart

What I have tried

Attempt 1 - Using Keycloak 11.0.3 and configure the built-in Infinispan cache to use file-based persistence (Ideas taken from - Preserve login session over KeyCloak restart with Infinispan local file-based cache - Configuring the server - Keycloak)

Result - Keycloak container starts without any errors but failed to persist user sessions after restarting the container. (The web front-end redirected to the login page after refreshing the page)

  • Created a Dockerfile with the following contents

FROM Quay

RUN mkdir /opt/jboss/keycloak/cache
RUN chmod 777 /opt/jboss/keycloak/cache

  • docker-compose.yml

keycloak:
container_name: ${APP_NAME}_keycloak
image: keycloak

    build:
        context: ./keycloak
        dockerfile: Dockerfile

    restart: unless-stopped

    volumes:
        - ./scripts/keycloak-import:/opt/jboss/keycloak/imports
        - my_volume:/opt/jboss/keycloak/cache
        - ./keycloak/standalone/configuration/standalone.xml:/opt/jboss/keycloak/standalone/configuration/standalone.xml

    command: -b 0.0.0.0 -Dkeycloak.profile.feature.upload_scripts=enabled -Dkeycloak.import=${KEYCLOAK_DIR}/imports/keycloak-${APP_ENV}.json -Dkeycloak.migration.strategy=IGNORE_EXISTING

    ports:
        - 8080:8080

    depends_on:
        - db    

volumes:
my_volume:
external: true
certificates:

  • ./keycloak/standalone/configuration/standalone.xml (I took a copy of the original one and edited). Here’s the XML for the Infinispan

     > <subsystem xmlns="urn:jboss:domain:infinispan:10.0">
    
        <cache-container name="keycloak" module="org.keycloak.keycloak-model-infinispan">
            <local-cache name="realms">
                <object-memory size="10000"/>
            </local-cache>
            <local-cache name="users">
                <object-memory size="10000"/>
            </local-cache>
            <local-cache name="sessions">
                 <expiration lifespan="-1"/>
                 <file-store path="/opt/jboss/keycloak/cache" shared="false" preload="true" purge="false" fetch-state="true"/>
            </local-cache>
            <local-cache name="authenticationSessions">
                 <expiration lifespan="-1"/>
                 <file-store path="/opt/jboss/keycloak/cache" shared="false" preload="true" purge="false" fetch-state="true"/>
            </local-cache>
            <local-cache name="offlineSessions"/>
            <local-cache name="clientSessions"/>
            <local-cache name="offlineClientSessions"/>
            <local-cache name="loginFailures"/>
            <local-cache name="work"/>
            <local-cache name="authorization">
                <object-memory size="10000"/>
            </local-cache>
            <local-cache name="keys">
                <object-memory size="1000"/>
                <expiration max-idle="3600000"/>
            </local-cache>
            <local-cache name="actionTokens">
                <object-memory size="-1"/>
                <expiration interval="300000" max-idle="-1"/>
            </local-cache>
        </cache-container>
        <cache-container name="server" default-cache="default" module="org.wildfly.clustering.server">
            <local-cache name="default">
                <transaction mode="BATCH"/>
            </local-cache>
        </cache-container>
        <cache-container name="web" default-cache="passivation" module="org.wildfly.clustering.web.infinispan">
            <local-cache name="passivation">
                <locking isolation="REPEATABLE_READ"/>
                <transaction mode="BATCH"/>
                <file-store passivation="true" purge="false"/>
            </local-cache>
            <local-cache name="sso">
                <locking isolation="REPEATABLE_READ"/>
                <transaction mode="BATCH"/>
            </local-cache>
            <local-cache name="routing"/>
        </cache-container>
        <cache-container name="ejb" aliases="sfsb" default-cache="passivation" module="org.wildfly.clustering.ejb.infinispan">
            <local-cache name="passivation">
                <locking isolation="REPEATABLE_READ"/>
                <transaction mode="BATCH"/>
                <file-store passivation="true" purge="false"/>
            </local-cache>
        </cache-container>
        <cache-container name="hibernate" module="org.infinispan.hibernate-cache">
            <local-cache name="entity">
                <object-memory size="10000"/>
                <expiration max-idle="100000"/>
            </local-cache>
            <local-cache name="local-query">
                <object-memory size="10000"/>
                <expiration max-idle="100000"/>
            </local-cache>
            <local-cache name="timestamps"/>
        </cache-container>
    </subsystem>