Skip to content

Deployment

Deploy IdentityScribe on Docker, Kubernetes, or bare metal. This page covers installation, verification, and platform-specific setup.

Run one non-readonly instance that performs LDAP transcription, maintenance, and reconciliation. That instance owns all writes to the identity history.

You may run additional read-only replicas (readonly = true, or -r / SCRIBE_READONLY=true) behind a load balancer for query channels only — REST, GraphQL, LDAP read paths, and the Operator UI. Read-only replicas do not run sync, maintenance, or reconciliation tasks.

Do not run multiple non-readonly instances against the same database. Each writer would schedule maintenance and reconciliation independently, process LDAP changes in parallel, and rely on in-process locking that does not coordinate across JVMs — which can corrupt identity history without surfacing an error. Coordinated multi-writer deployment is not available in this release.

  1. Download the executable for your platform

  2. Create identity-scribe.conf:

    database {
    url = "jdbc:postgresql://localhost:5432/identity_scribe"
    user = "identity_scribe"
    password = ${DB_PASSWORD}
    }
    ldap {
    url = "ldap://your-ldap-server:389"
    bind-dn = "cn=admin,ou=sa,o=system"
    bind-password = ${LDAP_BIND_PASSWORD}
    }
    transcribes {
    user {
    ldap {
    base = "ou=users,o=data"
    filter = "(objectClass=Person)"
    }
    }
    }
    channels.rest.enabled = true

    The distribution includes three starter templates: minimal.conf, recommended.conf, and production.conf. See Starter templates for what each one provides.

  3. Run: ./identity-scribe

  4. Verify traffic readiness: curl http://localhost:8080/readyz

services:
identity-scribe:
image: identity-scribe:latest
environment:
SCRIBE_LDAP_URL: "ldap://ldap:389"
SCRIBE_LDAP_BIND_DN: "cn=admin,dc=example,dc=com"
SCRIBE_LDAP_BIND_PASSWORD: "${LDAP_ADMIN_PASSWORD}"
SCRIBE_DATABASE_URL: "jdbc:postgresql://postgres:5432/identity_scribe"
SCRIBE_DATABASE_USER: "scribe"
SCRIBE_DATABASE_PASSWORD: "${DB_PASSWORD}"
SCRIBE_HTTP_PORT: "8080"
SCRIBE_LOG_LEVEL: "info"
apiVersion: v1
kind: ConfigMap
metadata:
name: identity-scribe-config
data:
SCRIBE_LDAP_URL: "ldap://ldap-service:389"
SCRIBE_DATABASE_URL: "jdbc:postgresql://postgres-service:5432/identity_scribe"
SCRIBE_HTTP_PORT: "8080"
SCRIBE_LOG_LEVEL: "info"
SCRIBE_CONCURRENCY: "16"
---
apiVersion: v1
kind: Secret
metadata:
name: identity-scribe-secrets
type: Opaque
stringData:
SCRIBE_LDAP_BIND_DN: "cn=admin,dc=example,dc=com"
SCRIBE_LDAP_BIND_PASSWORD: "secret"
SCRIBE_DATABASE_USER: "scribe"
SCRIBE_DATABASE_PASSWORD: "secret"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: identity-scribe
spec:
template:
spec:
containers:
- name: identity-scribe
envFrom:
- configMapRef:
name: identity-scribe-config
- secretRef:
name: identity-scribe-secrets
# Add startup, liveness, and readiness probes — see the
# "Kubernetes probes" section below for the full safe block
# (default Kubernetes startup-probe thresholds restart the pod
# before service initialization, database setup, and migrations finish).

Keep container output on stdout and stderr. Kubernetes and your cluster logging pipeline should capture those streams, apply retention, and export incident windows. SCRIBE_LOG_FILE only changes where the application logger stream writes; it is not a complete process-output capture setting.

Configure startup, liveness, and readiness probes for Kubernetes. The default example below uses the same socket as the ConfigMap above: SCRIBE_HTTP_PORT=8080.

startupProbe:
httpGet:
path: /startedz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 90 # 15min max for services + DB init
livenessProbe:
httpGet:
path: /livez
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 0
periodSeconds: 5
failureThreshold: 1
successThreshold: 1

If you want probes on a dedicated monitoring socket, configure and expose that socket first, then change the probe and convergence examples to the monitoring port:

http.sockets.monitoring {
port = 8081
host = "0.0.0.0"
}
monitoring.socket = "monitoring"

Expose port 8081 only to the cluster paths that need health, metrics, and Observe access.

Use /readyz only for Kubernetes Service routing. It reports when the pod can safely receive traffic; it is not a "wait until every sync and index build is finished" gate.

Use /observe/convergence for deployment scripts or manual checks that must wait for all initial ingest and required index work to finish. Observe JSON endpoints require an operator session or accepted credential by default; the example below uses a bearer token. For trusted local evaluation only, you may opt out with monitoring.observe.auth.enabled=false.

Terminal window
until curl -fsS -H "Authorization: Bearer <token>" http://identity-scribe:8080/observe/convergence >/dev/null; do
sleep 10
done

The startup probe has a high failureThreshold for service initialization, database connection setup, and migrations. Once the startup probe passes, liveness and readiness probes take over.

For the full list of health endpoints, see the Endpoints Reference.

Choose database ordering before the first IdentityScribe startup and keep it identical across production, standby, and restore environments.

For multilingual directories, use stable Unicode ordering so names and attributes sort consistently across platforms. For ASCII-only deployments where locale-aware ordering is not needed, byte-wise ordering can reduce comparison overhead.

Changing ordering after data exists requires a database rebuild and full resync; do not switch it in place.

Recommended multi-language production database:

CREATE DATABASE "identity_scribe"
TEMPLATE template0
ENCODING = 'UTF8'
LC_COLLATE = 'und-x-icu'
LC_CTYPE = 'und-x-icu'
CONNECTION LIMIT = -1;

Performance-focused ASCII-only database:

CREATE DATABASE "identity_scribe"
TEMPLATE template0
ENCODING = 'UTF8'
LC_COLLATE = 'C'
LC_CTYPE = 'C'
CONNECTION LIMIT = -1;

For slow query analysis, enable the query statistics extension through your PostgreSQL configuration or managed-service control plane. Managed PostgreSQL services (AWS RDS, Azure, GCP) typically provide a UI setting for this.

Enabling it takes two ordered steps: add pg_stat_statements to shared_preload_libraries and restart, then enable the pg_stat_statements extension in the IdentityScribe database. In development and staging, set database.auto-create-query-stats-extension = true to have IdentityScribe enable it for you once the library is preloaded — it stays off in production by default.

Once enabled, IdentityScribe's monitoring UI and /observe/stats/queries show slow query statistics automatically. See Enabling query statistics for provider-specific instructions.

When IdentityScribe starts, a banner displays runtime information:

identity-scribe v1.2.3 ready in 847ms
┃ Mode production
┃ License ACME Corp — expires 2027-01-15 (365d)
┃ Config
┃ /etc/scribe/identity-scribe.conf
┃ environment variables
┃ Auth
┃ Methods bearer
┃ Anonymous deny
┃ default https://auth.example.com
┃ Database postgresql://db.example.com:5432/scribe (ssl)
┃ LDAP ldaps://dc01.example.com:636
┃ Transcribes
┃ users ou=users,dc=example,dc=com (objectClass=person)
┃ groups ou=groups,dc=example,dc=com (objectClass=group) [disabled]
┃ Channels
┃ REST API http://localhost:8080/api
┃ LDAP ldap://localhost:10389

The sections shown depend on your configuration. Credentials are never displayed — database and LDAP URLs are sanitized to show only scheme, host, port, and path.

usage: identity-scribe
-c,--config <file> use config file (default: 'identity-scribe.conf')
-d,--debug print debugging information
-D <arg> key=value to set as property
-f,--logfile <file> application logger output destination (default: System.out)
-h,--help print this message
-k,--license <file> use license file (default: 'identity-scribe.lic')
-l,--loglevel <level> trace, debug, info, warn, error, fatal (default: info)
-m,--mode <mode> set application mode: dev, development, local, test, production, staging, etc.
-p,--printconfig print the resolved config and exit
-q,--quiet be extra quiet
-r,--readonly Readonly mode — only channels are started
-s,--sync Force re-sync of all entries
-t,--trace print tracing information
-v,--version print the version information and exit
  • Linux: Ubuntu, Debian, RHEL, CentOS, Amazon Linux, Oracle Linux, SLES, OpenSUSE, Alpine
  • macOS: Big Sur, Catalina, Mojave, Monterey
  • Windows: Server 2019/2016/2012/2008 R2, Windows 11/10/8/7 SP1+
  • PostgreSQL 18 (primary) or 17 (supported)
  • Supports plain, SSL, and mutual-SSL connections

The distribution binary is built for compatibility mode: it runs on a wide range of x64 and ARM64 CPUs. If you see illegal instruction or similar errors on older hardware, see CPU Compatibility.

  • Memory: Uses up to 80% of available physical memory (4 GB RAM = 3.2 GB max)
  • CPU: I/O-bound; multiple cores benefit parallel processing

After starting, verify the deployment:

Terminal window
# Readiness
curl -fsS http://localhost:8080/readyz
# Strict ingest/index convergence
curl -s -H "Authorization: Bearer <token>" http://localhost:8080/observe/convergence | jq
# Metrics available
curl -fsS http://localhost:8080/metrics | head
# System health
curl -fsS -H "Authorization: Bearer <token>" http://localhost:8080/observe/doctor | jq