Deploy IdentitySuite on Docker with PostgreSQL

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:

Create a new Blazor project with IdentitySuite installed
Write a production-ready multi-stage Dockerfile
Orchestrate IdentitySuite + PostgreSQL with docker-compose
Configure IdentitySuite via environment variables
Let IdentitySuite automatically initialize the database on first run
Verify the running stack and access the admin interface

Prerequisites:

.NET SDK installed (version 10)
Docker Desktop (Windows/macOS) or Docker Engine + Docker Compose (Linux)
Basic familiarity with C#, ASP.NET Core and the command line
No prior Docker expertise required—every command is explained

Table of Contents:

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.yml file 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:

copy
dotnet new blazor -o IdentitySuite.Linux --empty --interactivity Server --all-interactive

Why 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:

copy
cd IdentitySuite.Linux

Install the NuGet packages

Install the core IdentitySuite package:

copy
dotnet add package IdentitySuite

Then install the PostgreSQL provider:

copy
dotnet add package IdentitySuite.EntityFrameworkCore.PostgreSql

Configure Program.cs

Replace the contents of Program.cs with the standard three-line IdentitySuite setup:

copy

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:

copy

# ── 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:

copy

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 db as the hostname— that's why the connection string uses Host=db rather than localhost.
  • depends_on: Ensures the db container 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 pgdata named volume ensures that database data survives container restarts and rebuilds. Without it, all data would be lost every time the db container 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:

copy
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):

copy

{
  "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:

copy

{
  "IdentitySuiteOptions": {
    "Database": {
      "ConnectionStrings": {
        "PostgreSqlConnection": "Host=db;Port=5432;Database=identitydb;Username=identity;Password=secret"
      }
    }
    "Initialize": true,
  }
}
        

The two key settings here are:

  • Initialize: truemust 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 db service. When running locally (outside Docker), you would use Host=localhost instead of Host=db. Inside Docker Compose, Host=db resolves 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:

copy

.
├── docker-compose.yml
└── IdentitySuite.Linux/
    ├── Dockerfile
    ├── IdentitySuite.Linux.csproj
    ├── Program.cs
    └── IdentitySuite/
        └── IdentitySuiteSettings.Development.json
        

From the solution root, build and start both containers:

copy
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:

  1. The db container starts and initializes the PostgreSQL instance.
  2. The identitysuite.linux container starts and connects to the database.
  3. IdentitySuite detects Initialize: true, runs the database migrations, and seeds the initial data (default admin user, default roles, etc.).
  4. 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:

copy
http://localhost:5000

You 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 Dockerfile and docker-compose.yml
  • Pre-configured IdentitySuiteSettings.Development.json
  • Minimal Program.cs ready to run
View Example Project on GitHub

Continue Your IdentitySuite Journey

First-time setup

New to IdentitySuite? The Getting Started guide covers installation, configuration, and first login in detail.

Getting Started
Secure 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 Guide

Found this helpful? Share it with your team!

Logo

About 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.