Deploy IdentitySuite on Docker: Complete Guide with PostgreSQL
TL;DR - Quick Guide Overview
This guide walks you through deploying IdentitySuite—a self-hosted OpenID Connect and OAuth 2.0 server—inside a Linux Docker container, backed by a PostgreSQL database running in a separate container. By the end you'll have a fully working, containerized authentication server that you can run on any machine with Docker installed, with no dependency on the host OS or any pre-installed database engine.
Reading Time: ~15 minutes
Implementation Time: ~20-30 minutes
What You'll Accomplish:
Prerequisites:
Table of Contents:
Isolation, portability, and zero dependency on the host OS
Scaffold, install packages, and configure Program.cs
Multi-stage Dockerfile and docker-compose.yml
Connection string, persistent volume, and service networking
Environment variables, Initialize flag, and settings file
docker compose up, first login, and what to expect
A complete, step-by-step guide to running IdentitySuite and PostgreSQL in Docker Linux containers
Introduction — Why Docker for IdentitySuite?
Running an authentication server in production means dealing with runtime dependencies, database engines, and environment configuration. On a bare metal or VM deployment, you're responsible for installing the correct .NET runtime, provisioning the database, managing connection strings, and keeping everything consistent across environments.
Docker solves all of this at once. By packaging IdentitySuite and PostgreSQL as containers, you get:
- Isolation: IdentitySuite and PostgreSQL each run in their own container with their own filesystem and network namespace. No conflicts with other services on the host.
- Portability: The same
docker-compose.ymlfile runs identically on your laptop, a CI/CD pipeline, or a production Linux server. - Zero host dependencies: The host machine needs only Docker. No .NET SDK, no PostgreSQL installation, no manual path configuration.
- Reproducibility: Image versions are pinned, so your stack behaves the same today and six months from now.
This guide uses a single docker-compose.yml to orchestrate both services, which is the
most practical approach for self-hosted deployments. The accompanying example project is available on
GitHub so you can compare your setup at any point.
Step 1 — Create the Blazor project
The starting point is identical to what's described in the Getting Started guide. Open a terminal, navigate to the directory where you want the project, and run:
dotnet new blazor -o IdentitySuite.Linux --empty --interactivity Server --all-interactiveWhy IdentitySuite.Linux and not IdentitySuite?
Naming the project IdentitySuite would conflict with the NuGet package of the
same name, causing circular reference errors at build time. Using a distinct name like
IdentitySuite.Linux avoids the issue entirely.
Enter the project directory:
cd IdentitySuite.LinuxInstall the NuGet packages
Install the core IdentitySuite package:
dotnet add package IdentitySuiteThen install the PostgreSQL provider:
dotnet add package IdentitySuite.EntityFrameworkCore.PostgreSqlConfigure Program.cs
Replace the contents of Program.cs with the standard three-line IdentitySuite setup:
using IdentitySuite;
var builder = WebApplication.CreateBuilder(args);
// Registers all required services (authentication, authorization, Blazor, OpenIddict, etc.)
builder.AddIdentitySuiteServices();
var app = builder.Build();
// Creates or migrates the database based on the current configuration
await app.SetupIdentitySuiteDbAsync();
// Enables all runtime middleware (authentication, routing, Blazor, etc.)
app.UseIdentitySuiteServices();
await app.RunAsync();
That's all the application code needed. IdentitySuite registers and configures every service internally,
so Program.cs stays minimal.
Step 2 — Add Docker support
Create two files in the root of the solution (the directory that contains the
IdentitySuite.Linux project folder):
a Dockerfile placed inside the project folder, and a
docker-compose.yml at the solution root.
Dockerfile
Create the file at IdentitySuite.Linux/Dockerfile:
# ── Runtime base image ────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
# ── Build stage ───────────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["IdentitySuite.Linux/IdentitySuite.Linux.csproj", "IdentitySuite.Linux/"]
RUN dotnet restore "./IdentitySuite.Linux/IdentitySuite.Linux.csproj"
COPY . .
WORKDIR "/src/IdentitySuite.Linux"
RUN dotnet build "./IdentitySuite.Linux.csproj" -c $BUILD_CONFIGURATION -o /app/build
# ── Publish stage ─────────────────────────────────────────────────────────────
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./IdentitySuite.Linux.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# ── Final image ───────────────────────────────────────────────────────────────
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "IdentitySuite.Linux.dll"]
This is a standard multi-stage build. The build and
publish stages use the full .NET SDK image, while the
final image is based only on the lightweight ASP.NET runtime.
The result is a small production image that contains no build tooling.
Build context
Notice that the COPY command in the build stage uses a path relative to the
solution root, not the project folder. This is intentional: when you run
docker compose build, Docker sets the build context to the
context: . directory defined in
docker-compose.yml, which is the solution root.
docker-compose.yml
Create the file at the solution root. This file defines both services and wires them together:
services:
identitysuite.linux:
image: ${DOCKER_REGISTRY-}identitysuitelinux
build:
context: .
dockerfile: IdentitySuite.Linux/Dockerfile
ports:
- "5000:8080" # expose container port 8080 on localhost:5000
depends_on:
- db
environment:
# PostgreSQL connection string using the Docker service name as host
- IdentitySuiteOptions__Database__ConnectionStrings__PostgreSqlConnection=Host=db;Port=5432;Database=identitydb;Username=identity;Password=secret
db:
image: postgres:16
restart: always
environment:
POSTGRES_USER: identity
POSTGRES_PASSWORD: secret
POSTGRES_DB: identitydb
ports:
- "5433:5432" # optional: exposes PostgreSQL outside Docker for local inspection
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
A few things worth noting here:
- Service networking: Docker Compose creates an internal network automatically. The
IdentitySuite container reaches PostgreSQL using the service name
dbas the hostname— that's why the connection string usesHost=dbrather thanlocalhost. - depends_on: Ensures the
dbcontainer starts before IdentitySuite. Note that this does not wait for PostgreSQL to be ready to accept connections— IdentitySuite handles this gracefully through its own retry logic during initialization. - Persistent volume: The
pgdatanamed volume ensures that database data survives container restarts and rebuilds. Without it, all data would be lost every time thedbcontainer is recreated. - Port 5433: The PostgreSQL port mapping (
5433:5432) is optional. It's useful during development to inspect the database from the host using tools like pgAdmin or DBeaver, but it is not required for the application to work.
Step 3 — Configure PostgreSQL
The PostgreSQL configuration lives entirely in docker-compose.yml. The
db service uses the official postgres:16 image and creates the
database, user, and password automatically on first startup via the POSTGRES_*
environment variables.
The IdentitySuite container connects to PostgreSQL using the environment variable:
IdentitySuiteOptions__Database__ConnectionStrings__PostgreSqlConnection=Host=db;Port=5432;Database=identitydb;Username=identity;Password=secret
In ASP.NET Core, double underscores (__) in environment variable names are the standard
way to represent nested configuration keys. This variable maps to the following JSON path in
IdentitySuiteSettings.{environment}.json):
{
"IdentitySuiteOptions": {
"Database": {
"ConnectionStrings": {
"PostgreSqlConnection": "Host=db;Port=5432;Database=identitydb;Username=identity;Password=secret"
}
}
}
}
When running inside Docker, the environment variable takes precedence over any value in the settings file, which is exactly the behavior you want: the settings file provides development defaults, the environment variable provides the runtime value.
Production recommendation
For production deployments, avoid committing credentials to docker-compose.yml.
Move sensitive values to a .env file (which you add to
.gitignore) and reference them with
${VARIABLE_NAME} syntax in the compose file, or use
Docker Secrets / your cloud provider's secrets manager.
Step 4 — Configure IdentitySuite
After installing the IdentitySuite NuGet package, a directory named IdentitySuite is
created at the root of your project, containing the settings files:
IdentitySuiteSettings.{environment}.json
For this guide the relevant file is IdentitySuiteSettings.Development.json.
The minimum configuration required to run with PostgreSQL is:
{
"IdentitySuiteOptions": {
"Database": {
"ConnectionStrings": {
"PostgreSqlConnection": "Host=db;Port=5432;Database=identitydb;Username=identity;Password=secret"
}
}
"Initialize": true,
}
}
The two key settings here are:
- Initialize: true — must be set explicitly before the first run. When enabled, IdentitySuite creates the database schema and applies all EF Core migrations on startup. Without this flag, the database will not be created and the application will fail to start correctly.
- PostgreSqlConnection — the connection string that points to the
dbservice. When running locally (outside Docker), you would useHost=localhostinstead ofHost=db. Inside Docker Compose,Host=dbresolves correctly through the internal network.
Environment variable override
The connection string defined in the docker-compose.yml environment section
overrides whatever is in the settings file at runtime. This means you can keep
Host=db in both places, or keep Host=localhost
in the file for local development and let the compose file inject the correct value for the containerized
environment. Either approach works.
Step 5 — Run and verify
With all files in place, your solution structure should look like this:
.
├── docker-compose.yml
└── IdentitySuite.Linux/
├── Dockerfile
├── IdentitySuite.Linux.csproj
├── Program.cs
└── IdentitySuite/
└── IdentitySuiteSettings.Development.json
From the solution root, build and start both containers:
docker compose up --build
The first run will take a bit longer as Docker pulls the base images and builds the application. On
subsequent runs, --build can be omitted unless you have changed the source code.
What you'll see in the console output:
- The
dbcontainer starts and initializes the PostgreSQL instance. - The
identitysuite.linuxcontainer starts and connects to the database. -
IdentitySuite detects
Initialize: true, runs the database migrations, and seeds the initial data (default admin user, default roles, etc.). - The ASP.NET Core application starts listening on port 8080 inside the container.
Once you see a line similar to Now listening on: http://[::]:8080 in the logs,
open your browser and navigate to:
http://localhost:5000You should see the IdentitySuite login page. Log in with the default administrator credentials defined during initialization, and you'll land on the admin dashboard where you can register client applications, manage users, and configure the server.
Stopping the stack
Press Ctrl+C to stop the containers, or run
docker compose down from another terminal. The
pgdata volume is preserved between runs, so your data is safe.
To also delete the volume (full reset), use
docker compose down -v.
Complete Example Repository
The full working project from this guide is available on GitHub, including:
- Complete
Dockerfileanddocker-compose.yml - Pre-configured
IdentitySuiteSettings.Development.json - Minimal
Program.csready to run
Continue Your IdentitySuite Journey
First-time setup
New to IdentitySuite? The Getting Started guide covers installation, configuration, and first login in detail.
Getting StartedSecure your SPA
With IdentitySuite running, the next step is registering a client application. Follow the Authentication Made Easy guide for Angular, React, and Blazor WebAssembly.
Authentication Made Easy GuideAbout IdentitySuite
IdentitySuite simplifies enterprise authentication for .NET developers. Built on proven technologies like ASP.NET Core Identity and Openiddict, we eliminate the complexity of OAuth 2.0 and OpenID Connect implementation while maintaining enterprise-grade security standards.