KurrentDB Secure 3-Node Cluster Setup Guide for Windows

Overview
Version: 25.x
Last Updated: January 2026
This guide provides step-by-step instructions for deploying a secure, highly-available 3-node KurrentDB cluster on Windows Server. KurrentDB is an event-native database designed for event sourcing, event-driven architectures, and microservices.
Key Features
- High Availability: Quorum-based replication ensures data durability and cluster resilience
- TLS Security: All communications encrypted with TLS certificates
- Automatic Failover: Leader election ensures continuous operation during node failures
- Shared Nothing Architecture: Each node maintains its own copy of data
Cluster Topology
A 3-node cluster can tolerate the failure of 1 node while maintaining full read/write capability. The cluster uses a quorum-based replication model where a majority of nodes (2 out of 3) must acknowledge writes before they are confirmed to clients.
Prerequisites
Hardware Requirements (Per Node)
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4+ cores |
| RAM | 4 GB | 8+ GB |
| Storage | 50 GB SSD | 100+ GB SSD |
| Network | 1 Gbps | 10 Gbps |
Software Requirements
- Windows Server 2019 or later (Windows 10/11 for development)
- OpenSSL (for certificate generation)
- PowerShell 5.1 or later
- Administrator privileges
Network Requirements
| Port | Protocol | Purpose |
|---|---|---|
| 2113 | TCP | HTTP/HTTPS - Client connections, Admin UI, gRPC |
| 1112 | TCP | Internal TCP - Cluster replication |
Important: Ensure these ports are open in:
- Windows Firewall on each node
- AWS Security Groups (if using AWS)
- Any network firewalls between nodes
Time Synchronization (Critical!)
All cluster nodes MUST have synchronized clocks. KurrentDB will reject gossip from nodes with clock differences greater than 60 seconds.
Configure time sync on all nodes:
# For AWS instances - use Amazon Time Sync Service
w32tm /config /manualpeerlist:"169.254.169.123" /syncfromflags:manual /reliable:yes /update
Restart-Service w32time
w32tm /resync /force
# Verify time sync
w32tm /query /statusExample Node Configuration
This guide uses the following example configuration:
| Node | Hostname | IP Address |
|---|---|---|
| Node 1 | node1.kurrentdb.local | 172.31.17.231 |
| Node 2 | node2.kurrentdb.local | 172.31.30.35 |
| Node 3 | node3.kurrentdb.local | 172.31.0.152 |
Important: Replace these values with your actual hostnames and IP addresses.
Architecture
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Node 1 │◄─────────►│ Node 2 │◄─────────►│ Node 3 │
│ (Leader) │ Internal │ (Follower) │ Internal │ (Follower) │
│ │ TCP │ │ TCP │ │
│ Port 2113 │ Port 1112│ Port 2113 │ Port 1112│ Port 2113 │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Data/ │ │ Data/ │ │ Data/ │
│ Index │ │ Index │ │ Index │
└─────────────┘ └─────────────┘ └─────────────┘Communication Flow
- Client to Node: HTTPS on port 2113 (gRPC protocol)
- Node to Node: Internal TCP on port 1112 (replication)
- Gossip Protocol: HTTPS on port 2113 (cluster discovery)
Certificate Generation with OpenSSL
KurrentDB requires TLS certificates for secure communication. We’ll use OpenSSL to create a private Certificate Authority (CA) and node certificates.
Step 1: Install OpenSSL
Option A - Using Chocolatey (Recommended):
choco install openssl -yOption B - Manual Download: Download from: https://slproweb.com/products/Win32OpenSSL.html
After installation, verify OpenSSL is available:
openssl versionStep 2: Create Directory Structure
Open PowerShell as Administrator and run:
# Create certificate directories
New-Item -ItemType Directory -Path "C:\KurrentDB\certs\ca" -Force
New-Item -ItemType Directory -Path "C:\KurrentDB\certs\node1" -Force
New-Item -ItemType Directory -Path "C:\KurrentDB\certs\node2" -Force
New-Item -ItemType Directory -Path "C:\KurrentDB\certs\node3" -Force
# Navigate to certs directory
cd C:\KurrentDB\certsStep 3: Generate CA Certificate
# Generate CA private key (2048-bit RSA)
openssl genrsa -out ca\ca.key 2048
# Generate CA certificate (valid for 10 years)
openssl req -new -x509 -days 3650 -key ca\ca.key -out ca\ca.crt -subj "/CN=KurrentDB CA"This creates:
C:\KurrentDB\certs\ca\ca.crt- CA public certificate (distribute to all nodes)C:\KurrentDB\certs\ca\ca.key- CA private key (keep secure!)
Step 4: Generate Node 1 Certificate
Create the OpenSSL configuration file for Node 1. Use Notepad to avoid encoding issues:
notepad node1\openssl.cnfPaste the following content and save as ANSI encoding:
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = eventstoredb-node
[v3_req]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = node1.kurrentdb.local
IP.1 = 127.0.0.1
IP.2 = 172.31.17.231Important: Replace 172.31.17.231 with your Node 1 IP address.
Generate the certificate:
# Generate private key
openssl genrsa -out node1\node.key 2048
# Generate certificate signing request (CSR)
openssl req -new -key node1\node.key -out node1\node.csr -config node1\openssl.cnf
# Sign with CA to create certificate
openssl x509 -req -days 3650 -in node1\node.csr -CA ca\ca.crt -CAkey ca\ca.key -CAcreateserial -out node1\node.crt -extensions v3_req -extfile node1\openssl.cnf
# Clean up CSR
Remove-Item node1\node.csrStep 5: Generate Node 2 Certificate
Create configuration file:
notepad node2\openssl.cnfContent (save as ANSI):
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = eventstoredb-node
[v3_req]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = node2.kurrentdb.local
IP.1 = 127.0.0.1
IP.2 = 172.31.30.35Generate certificate:
openssl genrsa -out node2\node.key 2048
openssl req -new -key node2\node.key -out node2\node.csr -config node2\openssl.cnf
openssl x509 -req -days 3650 -in node2\node.csr -CA ca\ca.crt -CAkey ca\ca.key -CAcreateserial -out node2\node.crt -extensions v3_req -extfile node2\openssl.cnf
Remove-Item node2\node.csrStep 6: Generate Node 3 Certificate
Create configuration file:
notepad node3\openssl.cnfContent (save as ANSI):
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = eventstoredb-node
[v3_req]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = node3.kurrentdb.local
IP.1 = 127.0.0.1
IP.2 = 172.31.0.152Generate certificate:
openssl genrsa -out node3\node.key 2048
openssl req -new -key node3\node.key -out node3\node.csr -config node3\openssl.cnf
openssl x509 -req -days 3650 -in node3\node.csr -CA ca\ca.crt -CAkey ca\ca.key -CAcreateserial -out node3\node.crt -extensions v3_req -extfile node3\openssl.cnf
Remove-Item node3\node.csrStep 7: Verify Certificates
# Verify certificate chain
openssl verify -CAfile ca\ca.crt node1\node.crt
openssl verify -CAfile ca\ca.crt node2\node.crt
openssl verify -CAfile ca\ca.crt node3\node.crt
# Check certificate details and SANs
openssl x509 -in node1\node.crt -noout -subject -dates -ext subjectAltName
openssl x509 -in node2\node.crt -noout -subject -dates -ext subjectAltName
openssl x509 -in node3\node.crt -noout -subject -dates -ext subjectAltNameExpected output for each verify command: nodeX\node.crt: OK
Step 8: Distribute Certificates
Copy the appropriate certificates to each node:
| File | Node 1 | Node 2 | Node 3 |
|---|---|---|---|
| ca\ca.crt | ✓ | ✓ | ✓ |
| node1\node.crt | ✓ | ||
| node1\node.key | ✓ | ||
| node2\node.crt | ✓ | ||
| node2\node.key | ✓ | ||
| node3\node.crt | ✓ | ||
| node3\node.key | ✓ |
Step 9: Install CA Certificate
Run on ALL nodes as Administrator:
Import-Certificate -FilePath "C:\KurrentDB\certs\ca\ca.crt" -CertStoreLocation Cert:\LocalMachine\RootNode Configuration
Create a YAML configuration file for each node. Important notes:
GossipSeedshould contain the OTHER nodes (not itself)NodeHostAdvertiseAsshould be set to the IP address to avoid DNS resolution issues- All paths must match your actual certificate locations
Node 1 Configuration
Create C:\KurrentDB\config\kurrentdb.conf on Node 1:
---
# Cluster Configuration
ClusterSize: 3
DiscoverViaDns: false
GossipSeed: 172.31.30.35:2113,172.31.0.152:2113
# Network Configuration
NodeIp: 172.31.17.231
NodePort: 2113
ReplicationIp: 172.31.17.231
ReplicationPort: 1112
# Advertise IP address (avoids DNS resolution issues)
NodeHostAdvertiseAs: 172.31.17.231
# Certificate Configuration
CertificateFile: C:\KurrentDB\certs\node1\node.crt
CertificatePrivateKeyFile: C:\KurrentDB\certs\node1\node.key
TrustedRootCertificatesPath: C:\KurrentDB\certs\ca
# Data Directories
Db: C:\KurrentDB\data
Log: C:\KurrentDB\logs
# Features
RunProjections: All
StartStandardProjections: true
EnableAtomPubOverHttp: trueNode 2 Configuration
Create C:\KurrentDB\config\kurrentdb.conf on Node 2:
---
# Cluster Configuration
ClusterSize: 3
DiscoverViaDns: false
GossipSeed: 172.31.17.231:2113,172.31.0.152:2113
# Network Configuration
NodeIp: 172.31.30.35
NodePort: 2113
ReplicationIp: 172.31.30.35
ReplicationPort: 1112
# Advertise IP address (avoids DNS resolution issues)
NodeHostAdvertiseAs: 172.31.30.35
# Certificate Configuration
CertificateFile: C:\KurrentDB\certs\node2\node.crt
CertificatePrivateKeyFile: C:\KurrentDB\certs\node2\node.key
TrustedRootCertificatesPath: C:\KurrentDB\certs\ca
# Data Directories
Db: C:\KurrentDB\data
Log: C:\KurrentDB\logs
# Features
RunProjections: All
StartStandardProjections: true
EnableAtomPubOverHttp: trueNode 3 Configuration
Create C:\KurrentDB\config\kurrentdb.conf on Node 3:
---
# Cluster Configuration
ClusterSize: 3
DiscoverViaDns: false
GossipSeed: 172.31.17.231:2113,172.31.30.35:2113
# Network Configuration
NodeIp: 172.31.0.152
NodePort: 2113
ReplicationIp: 172.31.0.152
ReplicationPort: 1112
# Advertise IP address (avoids DNS resolution issues)
NodeHostAdvertiseAs: 172.31.0.152
# Certificate Configuration
CertificateFile: C:\KurrentDB\certs\node3\node.crt
CertificatePrivateKeyFile: C:\KurrentDB\certs\node3\node.key
TrustedRootCertificatesPath: C:\KurrentDB\certs\ca
# Data Directories
Db: C:\KurrentDB\data
Log: C:\KurrentDB\logs
# Features
RunProjections: All
StartStandardProjections: true
EnableAtomPubOverHttp: trueConfiguration Parameters Reference
| Parameter | Description |
|---|---|
ClusterSize | Number of nodes in the cluster (3 for HA) |
DiscoverViaDns | Set to false when using gossip seeds |
GossipSeed | Comma-separated list of OTHER cluster nodes (not self) |
NodeIp | IP address for client connections |
NodePort | Port for HTTP/gRPC connections (default: 2113) |
ReplicationIp | IP address for internal cluster communication |
ReplicationPort | Port for replication traffic (default: 1112) |
NodeHostAdvertiseAs | IP/hostname advertised to clients (use IP to avoid DNS issues) |
CertificateFile | Path to node’s TLS certificate |
CertificatePrivateKeyFile | Path to node’s private key |
TrustedRootCertificatesPath | Directory containing CA certificate |
Installation Steps
Step 1: Install KurrentDB (on ALL nodes)
Option A - Chocolatey (Recommended):
choco install kurrentdb -yOption B - Manual Download: Download from https://cloudsmith.io/~eventstore/repos/kurrent-latest/packages/
Step 2: Create Directory Structure (on ALL nodes)
New-Item -ItemType Directory -Path "C:\KurrentDB\config" -Force
New-Item -ItemType Directory -Path "C:\KurrentDB\data" -Force
New-Item -ItemType Directory -Path "C:\KurrentDB\logs" -Force
New-Item -ItemType Directory -Path "C:\KurrentDB\certs\ca" -Force
New-Item -ItemType Directory -Path "C:\KurrentDB\certs\node1" -Force # Adjust nodeX per nodeStep 3: Configure Windows Firewall (on ALL nodes)
New-NetFirewallRule -DisplayName "KurrentDB HTTP" -Direction Inbound -Protocol TCP -LocalPort 2113 -Action Allow
New-NetFirewallRule -DisplayName "KurrentDB Replication" -Direction Inbound -Protocol TCP -LocalPort 1112 -Action AllowStep 4: Configure AWS Security Groups (if applicable)
Ensure your Security Group allows inbound traffic on ports 2113 and 1112 from your VPC CIDR block.
Step 5: Sync Time (on ALL nodes)
# For AWS - use Amazon Time Sync
w32tm /config /manualpeerlist:"169.254.169.123" /syncfromflags:manual /reliable:yes /update
Restart-Service w32time
w32tm /resync /forceStep 6: Verify Configuration
Test configuration on each node before starting:
KurrentDB.exe --what-if --config=C:\KurrentDB\config\kurrentdb.confStarting the Cluster
Option A: Run as Console Application (Testing)
Start each node in a separate PowerShell window:
KurrentDB.exe --config=C:\KurrentDB\config\kurrentdb.confImportant: Start all three nodes within 1-2 minutes of each other.
Option B: Run as Windows Service (Production)
Create the Service (on each node):
sc.exe create "KurrentDB" start=delayed-auto binpath="\"C:\ProgramData\chocolatey\lib\kurrentdb\tools\KurrentDB.exe\" --config=\"C:\KurrentDB\config\kurrentdb.conf\""Configure Service Recovery:
sc.exe failure "KurrentDB" reset=0 actions=restart/5000/restart/5000/restart/5000Start the Service:
sc.exe start "KurrentDB"Cluster Formation
- Start all three nodes within a short timeframe
- Nodes discover each other via gossip seeds
- Election occurs to select the leader
- Once quorum (2+ nodes) is reached, the cluster becomes operational
- You should see logs indicating “ELECTIONS DONE” and node roles (Leader/Follower)
Verification
Check Cluster Status via Admin UI
- Open a web browser
- Navigate to:
https://<any-node-ip>:2113 - Accept the certificate warning (if CA not installed on client)
- Login with default credentials:
- Username: admin
- Password: changeit
Important: Change the default passwords immediately after first login!
Check Node Health via API
Using curl (Recommended):
# Check if node is alive
curl.exe -k https://172.31.17.231:2113/health/live
# Check cluster gossip (shows all nodes and their roles)
curl.exe -k https://172.31.17.231:2113/gossipUsing PowerShell 5.1:
# First, disable certificate validation (run once per session)
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; }
}
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
# Then make requests
Invoke-WebRequest -Uri "https://172.31.17.231:2113/health/live"
Invoke-WebRequest -Uri "https://172.31.17.231:2113/gossip"Using PowerShell 7+:
Invoke-WebRequest -Uri "https://172.31.17.231:2113/health/live" -SkipCertificateCheckCluster Health Check Script
# Disable certificate validation for this session
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; }
}
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
$nodes = @(
"https://172.31.17.231:2113",
"https://172.31.30.35:2113",
"https://172.31.0.152:2113"
)
Write-Host "KurrentDB Cluster Health Check" -ForegroundColor Cyan
Write-Host "==============================" -ForegroundColor Cyan
foreach ($node in $nodes) {
try {
$response = Invoke-WebRequest -Uri "$node/health/live" -TimeoutSec 5
Write-Host "$node - OK" -ForegroundColor Green
}
catch {
Write-Host "$node - FAILED" -ForegroundColor Red
}
}Or use curl for a quick check:
curl.exe -k https://172.31.17.231:2113/health/live
curl.exe -k https://172.31.30.35:2113/health/live
curl.exe -k https://172.31.0.152:2113/health/liveVerify Cluster Membership
The gossip endpoint should show all three nodes:
- One node as Leader
- Two nodes as Follower
Client Connection
Connection String
kurrentdb://admin:changeit@172.31.17.231:2113,172.31.30.35:2113,172.31.0.152:2113?tls=true.NET Client Example
using KurrentDB.Client;
var settings = KurrentClientSettings.Create(
"kurrentdb://admin:changeit@172.31.17.231:2113,172.31.30.35:2113,172.31.0.152:2113?tls=true"
);
var client = new KurrentClient(settings);
// Append events
await client.AppendToStreamAsync(
"my-stream",
StreamState.Any,
new[] { eventData }
);Certificate Verification Options
Option 1: Install CA on Client Machine (Recommended)
Import-Certificate -FilePath "ca.crt" -CertStoreLocation Cert:\CurrentUser\RootOption 2: Skip Verification (Development Only)
kurrentdb://...?tls=true&tlsVerifyCert=falseDNS Configuration (Optional)
If you prefer to use DNS names, add to your hosts file (C:\Windows\System32\drivers\etc\hosts):
172.31.17.231 node1.kurrentdb.local
172.31.30.35 node2.kurrentdb.local
172.31.0.152 node3.kurrentdb.localTroubleshooting
Common Issues and Solutions
| Issue | Cause | Solution |
|---|---|---|
| Nodes can’t find each other | Firewall blocking | Open ports 2113 and 1112 in Windows Firewall and Security Groups |
| Certificate errors | Wrong SANs or path | Verify certificate includes correct IP/DNS; check file paths |
| Election timeouts | Only one node running | Start all nodes; need 2+ for quorum |
| Time difference errors | Clocks out of sync | Sync time with w32tm /resync /force |
| Redirect to DNS name fails | DNS not configured | Set NodeHostAdvertiseAs to IP address |
| Path not found errors | Wrong config paths | Verify certificate and data directory paths exist |
Diagnostic Commands
Check connectivity between nodes:
Test-NetConnection -ComputerName 172.31.30.35 -Port 2113
Test-NetConnection -ComputerName 172.31.30.35 -Port 1112Check time sync:
w32tm /query /status
[System.DateTime]::UtcNow.ToString("yyyy-MM-dd HH:mm:ss")Verify certificate details:
openssl x509 -in C:\KurrentDB\certs\node1\node.crt -noout -subject -dates -ext subjectAltName
openssl verify -CAfile C:\KurrentDB\certs\ca\ca.crt C:\KurrentDB\certs\node1\node.crtCheck firewall rules:
Get-NetFirewallRule -DisplayName "KurrentDB*" | Format-Table Name, DisplayName, Enabled, ActionTest configuration without starting:
KurrentDB.exe --what-if --config=C:\KurrentDB\config\kurrentdb.confLog Analysis
Review logs at C:\KurrentDB\logs\. Key entries to watch:
| Log Message | Meaning |
|---|---|
ELECTIONS DONE | Cluster successfully elected a leader |
Became Leader / Became Follower | Node role assignment |
Time difference... too great | Clock sync issue between nodes |
TIMED OUT! (S=ElectingLeader) | Can’t reach other nodes |
Security Best Practices
Password Management
- Change default passwords immediately after cluster setup
- Use strong, unique passwords for
adminandopsusers - Create application-specific user accounts with minimum necessary permissions
Certificate Security
- Protect private keys: Restrict access to
*.keyfiles - Regular rotation: Plan for certificate renewal before expiration
- Secure CA key: Keep CA private key offline or in secure storage
- Monitor expiration: Set up alerts for certificate expiry
Network Security
- Isolate cluster network: Use private subnets/VLANs
- Restrict Security Groups: Only allow necessary IPs and ports
- Use internal IPs: Keep cluster traffic on private network
Quick Reference
Essential Commands
| Task | Command |
|---|---|
| Start service | sc.exe start KurrentDB |
| Stop service | sc.exe stop KurrentDB |
| Check service status | sc.exe query KurrentDB |
| View logs | Get-Content C:\KurrentDB\logs\*.log -Tail 100 |
| Test config | KurrentDB.exe --what-if --config=... |
| Health check | curl.exe -k https://IP:2113/health/live |
| Sync time | w32tm /resync /force |
Default Credentials
| User | Password | Purpose |
|---|---|---|
| admin | changeit | Full administrative access |
| ops | changeit | Operational access |
Key Directories
| Path | Purpose |
|---|---|
C:\KurrentDB\config\ | Configuration files |
C:\KurrentDB\data\ | Event data |
C:\KurrentDB\logs\ | Log files |
C:\KurrentDB\certs\ | TLS certificates |
Ports
| Port | Purpose |
|---|---|
| 2113 | Client connections, Admin UI, gRPC, Gossip |
| 1112 | Internal cluster replication |
Support and Resources
- Documentation: https://docs.kurrent.io
- Community Forum: https://discuss.kurrent.io
- Discord: https://discord.gg/Phn9pmCw3t
- GitHub: https://github.com/kurrent-io/KurrentDB
Document prepared for KurrentDB v25.x on Windows Server.
