Skip to main content

Deployment Guide

This guide covers the complete deployment process for the eduID Wallet Matching Portal, from a quick-start local development setup using Docker Compose to production deployment considerations. The portal consists of multiple interconnected services that must be configured and orchestrated correctly to function as a cohesive system.

Prerequisites

Before deploying the portal, ensure the following tools and runtimes are available in your environment:

PrerequisiteVersionPurpose
Docker24+Container runtime for all services
Docker Composev2.20+Service orchestration and dependency management
PostgreSQL15+Primary database (provided via Docker or external)
Node.js20+Portal frontend development and build (development only)
JDK21+Kotlin backend service development and build (development only)

For local development, Docker and Docker Compose are the only strict requirements, as all other dependencies are encapsulated in the container images. Node.js and JDK are needed only if you intend to build the services from source rather than using pre-built images.

Quick Start

The fastest way to get the full portal running locally is through the provided Docker Compose configuration:

cd deploy/docker
docker compose up

This single command starts all services with their default development configurations. Once all containers are healthy, the following endpoints become available:

ServiceURLDescription
Portal (Frontend)http://localhost:3000The web application that wallet holders interact with
STS (Security Token Service)http://localhost:8092Issues and validates OAuth2/OIDC tokens
Auth Bridgehttp://localhost:8090Identity matching, reconciliation, and binding management

To start the services in the background (detached mode):

cd deploy/docker
docker compose up -d

To stop all services and remove the containers:

cd deploy/docker
docker compose down

To stop all services and also remove persistent volumes (this deletes all database data):

cd deploy/docker
docker compose down -v

Docker Compose Services

The Docker Compose configuration defines five services with explicit dependency relationships. The following table describes each service, its image, exposed port, and what it depends on:

ServiceImagePortDepends OnDescription
postgrespostgres:155432--PostgreSQL database shared by the STS and Auth Bridge services. Initialized with the required databases and roles on first startup.
service-stsportal/service-sts8092postgresSecurity Token Service. Handles OAuth2 authorization server functionality, token issuance, and upstream federation with providers like SURF and Keycloak.
service-auth-bridgeportal/service-auth-bridge8090postgresAuth Bridge service. Manages identity matching, reconciliation sessions, identity link bindings, and the external API for third-party lookups.
service-blobstore-camelportal/blobstore8081--Blob storage service based on Apache Camel. Handles document and artifact storage. Does not depend on the database.
portalportal/frontend3000service-sts, service-auth-bridgeNext.js frontend application. Depends on both backend services being available for API calls.

Startup Order

Docker Compose manages startup ordering through the depends_on configuration with health checks. The startup sequence is:

  1. PostgreSQL starts first and becomes healthy when it accepts connections.
  2. STS and Auth Bridge start in parallel once PostgreSQL is healthy. Each runs its own database migrations on startup.
  3. Blobstore starts independently (no database dependency).
  4. Portal starts last, once both STS and Auth Bridge report healthy status.

Environment Variables

Each service is configured through environment variables. The following sections list the key variables for each service, organized by category.

PostgreSQL

VariableDefaultDescription
POSTGRES_USERportalSuperuser name for the PostgreSQL instance.
POSTGRES_PASSWORDportalSuperuser password. Must be changed for production.
POSTGRES_DBportalDefault database name.

STS (Service Token Service)

VariableDefaultDescription
DATABASE_URLjdbc:postgresql://postgres:5432/stsJDBC connection URL for the STS database.
DATABASE_USERNAMEportalDatabase username.
DATABASE_PASSWORDportalDatabase password.
ISSUER_URLhttp://localhost:8092The issuer URL included in issued tokens. Must match the externally reachable URL in production.
SURF_CLIENT_ID--OIDC client ID registered with SURF for federation.
SURF_CLIENT_SECRET--OIDC client secret for SURF.
SURF_DISCOVERY_URL--OIDC discovery endpoint URL for SURF.
KEYCLOAK_URLhttp://keycloak:8080Keycloak instance URL for upstream authentication.
KEYCLOAK_REALMportalKeycloak realm name.
KEYCLOAK_CLIENT_IDportal-stsOIDC client ID registered in Keycloak.
KEYCLOAK_CLIENT_SECRET--OIDC client secret for Keycloak.

Auth Bridge

VariableDefaultDescription
DATABASE_URLjdbc:postgresql://postgres:5432/auth_bridgeJDBC connection URL for the Auth Bridge database.
DATABASE_USERNAMEportalDatabase username.
DATABASE_PASSWORDportalDatabase password.
DATABASE_MAX_POOL_SIZE5Maximum number of connections in the connection pool.
KMS_PROVIDERsoftwareKMS provider type. Use software for development, azure or aws for production.
KMS_KEY_A_ID--Key identifier for HMAC Key A (holder hashing).
KMS_KEY_B_ID--Key identifier for HMAC Key B (institution hashing).
KMS_KEY_C_ID--Key identifier for AES Key C (encryption).
EXTERNAL_API_JWT_ISSUER--Expected issuer claim in JWT tokens for the external API.
EXTERNAL_API_JWT_AUDIENCE--Expected audience claim in JWT tokens for the external API.
RETENTION_INACTIVE_DAYS730Days of inactivity before soft deletion of bindings.
RETENTION_SOFT_DELETE_DAYS30Days a soft-deleted record is retained before hard deletion.

Portal (Frontend)

VariableDefaultDescription
NEXTAUTH_URLhttp://localhost:3000The canonical URL of the portal. Must match the externally reachable URL in production.
NEXTAUTH_SECRET--Secret used for encrypting NextAuth.js session tokens. Must be a strong random value in production.
STS_URLhttp://service-sts:8092Internal URL of the STS service.
AUTH_BRIDGE_URLhttp://service-auth-bridge:8090Internal URL of the Auth Bridge service.
NEXT_PUBLIC_STS_URLhttp://localhost:8092Publicly accessible URL of the STS service (used by the browser).

Blobstore

VariableDefaultDescription
BLOB_STORAGE_PATH/data/blobsFile system path where blobs are stored inside the container.
MAX_FILE_SIZE10MBMaximum allowed file size for uploads.

Volumes

The Docker Compose configuration defines the following persistent volumes:

VolumeMounted ToServiceDescription
pgdata/var/lib/postgresql/datapostgresPostgreSQL data directory. Persists database contents across container restarts.
blobdata/data/blobsservice-blobstore-camelBlob storage directory. Persists uploaded files across container restarts.
auth-bridge-keystore/app/keystoreservice-auth-bridgeKeystore directory for the Auth Bridge. In development mode with the software KMS provider, key material is stored here. Not used when an external KMS (Azure Key Vault, AWS KMS) is configured.

To inspect the contents of a volume:

docker volume inspect deploy-docker_pgdata

Production Deployment Considerations

The Docker Compose setup is designed for local development and testing. Deploying to production requires several important changes:

External PostgreSQL

In production, use a managed PostgreSQL service (such as Azure Database for PostgreSQL or AWS RDS) rather than the containerized PostgreSQL instance. This provides:

  • Automated backups and point-in-time recovery
  • High availability with failover
  • Monitoring and alerting
  • Security patching

Update the DATABASE_URL environment variables for both STS and Auth Bridge to point to the external PostgreSQL instance.

Azure Key Vault for KMS

The software KMS provider stores key material on the local filesystem and is suitable only for development. In production, configure the Auth Bridge to use Azure Key Vault (or AWS KMS) for cryptographic key management:

sphereon:
app:
kms:
provider: azure
azure:
vault-url: "https://your-vault.vault.azure.net/"
tenant-id: "${env:AZURE_TENANT_ID}"
client-id: "${env:AZURE_CLIENT_ID}"
client-secret: "${env:AZURE_CLIENT_SECRET}"

This ensures that cryptographic keys are managed by a hardware security module (HSM) and never leave the KMS boundary.

Real SURF Credentials

The development setup may use mock or test credentials for SURF federation. In production, register proper OIDC client credentials with SURF and configure them in the STS environment variables.

HTTPS Termination

All services must be accessed over HTTPS in production. Use a reverse proxy (such as NGINX, Traefik, or a cloud load balancer) to terminate TLS and forward traffic to the backend services. Ensure that:

  • The ISSUER_URL for the STS uses the https:// scheme.
  • The NEXTAUTH_URL for the portal uses the https:// scheme.
  • The NEXT_PUBLIC_STS_URL uses the https:// scheme.
  • All redirect URIs registered with OIDC providers use https://.

NEXTAUTH_SECRET

The NEXTAUTH_SECRET must be set to a cryptographically strong random value in production. Generate one with:

openssl rand -base64 32

This secret is used to encrypt session tokens and must remain consistent across portal container restarts. Store it in a secrets manager rather than in plain text in the Docker Compose file.

Image Registry

In production, push built images to a private container registry (Azure Container Registry, AWS ECR, GitHub Container Registry) and reference them by digest rather than tag to ensure immutable deployments.

Resource Limits

Set CPU and memory limits on all containers to prevent any single service from consuming all available resources:

services:
service-auth-bridge:
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M

Health Checks

Each service exposes a health check endpoint that can be used for monitoring and orchestration:

ServiceHealth EndpointHealthy Response
PortalGET /api/health200 OK with {"status": "ok"}
STSGET /health200 OK
Auth BridgeGET /health200 OK with readiness details
PostgreSQLTCP connection on port 5432Connection accepted

Docker Compose uses these health checks to determine when a service is ready to accept traffic and to manage the startup ordering of dependent services. In a Kubernetes deployment, these same endpoints should be configured as liveness and readiness probes.

Verifying Health After Startup

After starting the services, verify that all health checks pass:

# Check portal health
curl -s http://localhost:3000/api/health | jq .

# Check STS health
curl -s http://localhost:8092/health | jq .

# Check Auth Bridge health
curl -s http://localhost:8090/health | jq .

If any service reports unhealthy, check the container logs for error details:

docker compose logs service-auth-bridge --tail 100