Docker Configuration

Overview

IdentitySuite runs in Docker with no special setup for a quick local test. Going to a real deployment, though, requires attention to what a container recreation resets by default: the application's own writable state — settings, OpenIddict certificates, and the license binding — normally lives on the container's writable layer, which starts fresh every time the container is recreated.

A step-by-step walkthrough with a full docker-compose.yml and PostgreSQL is available in the Docker deployment guide. This page is the configuration reference for the persistence aspects covered in that guide's "Production considerations" section.

What Needs to Persist

Without persistent volumes, every container recreation — an image update, a docker compose up --force-recreate, a host reboot — silently resets:

  • The IdentitySuite data folder — where IdentitySuite persists any setting written at runtime after first boot (themes, client logos, and other admin-side customization).
  • The Certificates folder — where OpenIddict generates its signing and encryption certificates on first run. A new certificate on every restart invalidates every existing access and refresh token, forcing every user to log in again, and breaks decryption of the ASP.NET Core DataProtection keyring if it's shared through the database.
  • The license environment binding — inside a container, IdentitySuite backs the license's environment fingerprint with a persisted seed file rather than a hardware identifier, since containers are, by design, hardware-agnostic. If that seed doesn't survive a recreation, the license has to reactivate against the license server (or offline, on air-gapped machines — see the License documentation).

None of this is destructive by itself — certificates and the license both regenerate or reactivate automatically — but it means the application never reaches a stable configuration in a container that gets recreated regularly, and every recreation forces a fresh login for every user.

The IDENTITYSUITE_DATA_DIR Environment Variable

The license environment fingerprint is normally backed by two independent anchors for redundancy: the application's own working directory, and the OS-provided LocalApplicationData folder. Inside a container, LocalApplicationData resolves under the application's own working directory rather than a real home directory, which collapses the second anchor onto the first one and defeats the redundancy.

Setting IDENTITYSUITE_DATA_DIR overrides where that second anchor is written, so it can point at a genuinely separate persistent volume instead:

copy

environment:
  - IDENTITYSUITE_DATA_DIR=/data
volumes:
  - suite-seed:/data
            

Optional, but recommended for containers

Leaving IDENTITYSUITE_DATA_DIR unset falls back to the previous behavior and changes nothing for existing installations. On non-container hosts (Windows, bare-metal Linux, macOS) the two anchors are already independent, so there's no need to set it there.

Persistent Volumes and the Dockerfile

Mount persistent volumes on the IdentitySuite and Certificates folders, alongside the seed volume from the section above:

copy

services:
  identitysuite:
    # ...
    volumes:
      - suite-config:/app/IdentitySuite
      - suite-certs:/app/Certificates
      - suite-seed:/data
    environment:
      - IDENTITYSUITE_DATA_DIR=/data

volumes:
  suite-config:
  suite-certs:
  suite-seed:
            

A named volume that's never been used before is created empty and owned by root — which the non-root $APP_UID user in the official runtime images can't write to. Pre-create and chown the mount points in the Dockerfile, before switching to $APP_UID and before WORKDIR:

copy

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
RUN mkdir -p /data /app/IdentitySuite /app/Certificates \
    && chown -R $APP_UID:$APP_UID /data /app
USER $APP_UID
WORKDIR /app
            

Chown the whole /app, not just the two subfolders

mkdir -p /app/IdentitySuite also creates the parent /app as a side effect, while this RUN is still executing as root. A chown limited to the two subfolders alone would leave /app itself root-owned and break every other file the app needs to write there directly, such as license.token.

Also make sure the final stage's COPY keeps ownership consistent with the non-root user — this is easy to miss since the default .NET container templates don't include it:

copy
COPY --from=publish --chown=$APP_UID:$APP_UID /app/publish .

Tradeoff of persisting these folders

Once these volumes exist, a future image update that ships new default settings, themes, or assets under IdentitySuite/ won't automatically reach an already-deployed instance, since the volume's content wins over what's baked into the image. That's the correct behavior here: certificates, license state, and admin customization should never be silently overwritten by an image update.