A proof-of-concept validating CockroachDB Cloud as a persistence backend for a self-hosted Temporal deployment.
What and Why
Temporal isn't on CockroachDB's official support matrix. It ships a postgres12 persistence plugin designed and tested against real PostgreSQL — not CockroachDB specifically. Before recommending CockroachDB as a backend for a self-hosted Temporal deployment, two questions need real answers rather than assumptions:
Does it actually work? CockroachDB is PostgreSQL-wire-compatible, but wire compatibility isn't the same as full SQL-dialect compatibility. Temporal's schema and query patterns need to be validated end-to-end.
What cluster size do I need to provision? Temporal persists every workflow state transition as a durable "checkpoint" (a history event). Sizing a cluster for a given checkpoint volume requires real, measured bytes-per-checkpoint data — not a vendor estimate.
This post documents the compatibility issues found and fixed, the load-testing methodology used to generate real data at scale, the results, and a reusable formula for extrapolating those results to any target checkpoint volume.
Architecture
Test environment:
- MacBook Pro (Apple M3 Pro, 18 GB RAM)
- Temporal Server v1.31.2 (self-hosted binary running locally on my laptop)
- Temporal CLI v1.7.3
- CockroachDB Cloud Advanced: 3 nodes, AWS us-east-1, 16 vCPU / 64 GiB RAM per node, v26.2.3
CockroachDB as persistence backend, via Temporal's postgres12 SQL plugin, split across two databases:
-
temporal— workflow execution state and event history (executions, history_node, history_tree, task-queue tables) -
temporal_visibility— the queryable, denormalized index used by dashboards, CLI, and search (executions_visibility)
Simulated workload: This project simulates a Temporal workload for an e-commerce application that specializes in return-automation and post-purchase platform. A post-purchase return-automation saga — return eligibility check (an AI-classification activity), a package-protection branch, a signal-driven wait for physical package receipt with an SLA timer, an AI-driven disposition decision (refund / exchange / store credit), the disposition activity itself with saga-style compensation on failure, and customer notification. Built to be representative of a real branching saga, not a synthetic microbenchmark — average ~32 checkpoints per execution, ranging 12–45 depending on branch taken. Full reference implementation is in the appendix.
Compatibility Issues Found and Fixed
Temporal's schema migrations for temporal_visibility (versions v1.2 through v1.13) hit six distinct CockroachDB/PostgreSQL incompatibilities. All were fixed by patching the migration SQL files directly — no application-code changes, no fork of Temporal itself.
A separate, non-schema finding: default numHistoryShards: 4 caused severe workflow-task contention under concurrent load (context deadline exceeded, WorkflowTaskTimedOut cascades) — not a CockroachDB limitation, but an under-provisioned Temporal-side setting for the target throughput. Raising to numHistoryShards: 256 (512 is recommendation for production) increases write throughput and increases number of concurrent persistence calls.
Patching the schema
# Get schema files matching the Temporal binary version
curl -L https://github.com/temporalio/temporal/archive/refs/tags/v1.31.2.tar.gz | \
tar -xz --strip-components=1 "temporal-1.31.2/schema"
cd schema/postgresql/v12/visibility/versioned
# v1.2 — remove CREATE EXTENSION block, strip jsonb_path_ops, fix convert_ts()
sed -i.bak \
-e '/^DO LANGUAGE/,/^\$\$;/d' \
-e 's/ jsonb_path_ops//g' \
v1.2/advanced_visibility.sql
sed -i '' "s/RETURN s::timestamptz at time zone 'UTC';/RETURN parse_timestamp(s);/" \
v1.2/advanced_visibility.sql
# v1.3, v1.7, v1.13 (search-attribute files) — strip jsonb_path_ops
sed -i.bak 's/ jsonb_path_ops//' v1.3/add_build_ids_search_attribute.sql
sed -i.bak 's/ jsonb_path_ops//' v1.7/add_pause_info_search_attribute.sql
sed -i.bak 's/ jsonb_path_ops//' v1.13/add_used_deployment_versions_search_attribute.sql
# v1.13 combined migration — remove dynamic cleanup block, strip CONCURRENTLY and jsonb_path_ops
sed -i.bak '/^DO LANGUAGE/,/^END \$\$;/d' v1.13/combined_v1.10_v1.13.sql
sed -i '' 's/CREATE INDEX CONCURRENTLY/CREATE INDEX/g' v1.13/combined_v1.10_v1.13.sql
sed -i '' 's/ jsonb_path_ops//g' v1.13/combined_v1.10_v1.13.sql
# Verify every fix landed
grep -rc "DO LANGUAGE" v1.2 v1.13 # expect 0 in every real .sql file
grep -rc jsonb_path_ops v1.2 v1.3 v1.7 v1.9 v1.13 # expect 0
grep -rc CONCURRENTLY v1.13 # expect 0
grep "RETURN parse_timestamp" v1.2/advanced_visibility.sql # expect 1
grep "LANGUAGE plpgsql" v1.2/advanced_visibility.sql # expect IMMUTABLE
Loading the Schema
export SQL_PASSWORD='<your-password>'
./temporal-sql-tool \
--plugin postgres12 \
--ep <your-cluster-host>.cockroachlabs.cloud \
-p 26257 \
-u <your-sql-user> \
--tls --tls-ca-file "<path-to-ca-cert>" \
--db temporal create
./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
-u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
--db temporal setup-schema -v 0.0
./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
-u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
--db temporal update-schema --schema-name postgresql/v12/temporal
./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
-u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
--db temporal_visibility create
./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
-u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
--db temporal_visibility setup-schema -v 0.0
./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
-u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
--db temporal_visibility update-schema -d ./schema/postgresql/v12/visibility/versioned
Starting Temporal Server
cd ~
~/temporal-server --config-file ~/temporal-server.yaml start
# In a new terminal
temporal --address localhost:7233 operator namespace create default
temporal --address localhost:7233 operator cluster health
temporal --address localhost:7233 -n temporal-system workflow list
Starting Temporal UI Server
which go || brew install go cd ~ git clone https://github.com/temporalio/ui-server.git cd ui-server go build -o ui-server ./cmd/server mkdir -p ~/ui-config cp ~/temporal-ui-server.yaml ~/ui-config/development.yaml ~/ui-server/ui-server --root ~ --config ui-config start
# open http://localhost:8233
Load Testing Methodology
Rather than relying on a single small sample, checkpoints were generated in escalating batches (100 → 1,000 → 5,000 → 20,000+) with verification at each stage:
- Smoke test (10–20 executions) to validate correctness end-to-end
- Staged batches, each followed by:
temporal workflow count --query "ExecutionStatus='Running'"to confirm full drainDirect SQL against
temporal/temporal_visibilityfor real execution/checkpoint counts (not estimates)CockroachDB Cloud Console storage metrics per table
- Final run: 34,000+ total executions, exceeding the 1,000,000-checkpoint target
A custom batch.py script drove load: bounded-concurrency workflow starts with immediate signaling, fire-and-forget (no blocking on completion at scale), self-reporting throughput. A tuned worker.py (moderate max_concurrent_activities/max_concurrent_workflow_tasks, sized to the test laptop's cores/RAM.
Verification queries in CockroachDB
-- Full drain confirmation, cross-checked in SQL
USE temporal_visibility;
SELECT status, count(*) FROM executions_visibility
WHERE namespace_id = '<your-namespace-id>'
GROUP BY status;
-- Real checkpoint totals -- the actual sizing data, not an estimate
USE temporal;
SELECT
count(*) AS total_executions,
sum(next_event_id - 1) AS total_checkpoints,
avg(next_event_id - 1) AS avg_checkpoints_per_execution
FROM executions e JOIN namespaces n ON n.id = e.namespace_id
WHERE n.name = 'default';
-- Logical (uncompressed, single-copy) bytes per checkpoint
SELECT
(SELECT sum(pg_column_size(data)) FROM history_node) AS total_logical_bytes,
(SELECT sum(next_event_id - 1) FROM executions e JOIN namespaces n ON n.id = e.namespace_id
WHERE n.name = 'default') AS total_checkpoints,
(SELECT sum(pg_column_size(data)) FROM history_node)::FLOAT
/ NULLIF((SELECT sum(next_event_id - 1) FROM executions e JOIN namespaces n ON n.id = e.namespace_id
WHERE n.name = 'default')::FLOAT, 0)
AS avg_logical_bytes_per_checkpoint;
-- Read/write mix, from actual observed statement statistics
SELECT
metadata->>'db' AS database_name,
left(metadata->>'querySummary', 6) AS verb,
sum((statistics->'statistics'->>'cnt')::INT) AS total_executions
FROM crdb_internal.statement_statistics
WHERE metadata->>'db' IN ('temporal', 'temporal_visibility')
AND metadata->>'stmtType' = 'TypeDML'
GROUP BY 1, 2
ORDER BY total_executions DESC;
Results
Final measured totals (34,000+ executions):
The ~2.2x gap between logical (140 bytes) and physical (~308 bytes) per-checkpoint size reflects CockroachDB's replication factor and index overhead — a legitimate planning multiplier, not compression failure.
A notable finding: temporal_visibility (Advanced Visibility's SQL-based schema) consumed more physical storage than temporal itself in our tests, despite holding far less raw data.
Read/write mix (from crdb_internal.statement_statistics, temporal DB): 56% SELECT, 23% INSERT, 15% UPDATE, 6% DELETE. Reads are the majority — Temporal's optimistic-concurrency design requires a consistency-check read before most writes.
Extrapolating to Your Own Workload
Use the measured per-unit rates above with your own target volume:
This work validates that CockroachDB Cloud Advanced functions correctly as a persistence
- Each checkpoint is a transaction in CockroachDB and the heuristic is 1 vCPU : 150 TPS
- executions/month = checkpoints_per_month ÷ avg_checkpoints_per_execution
- temporal DB storage = checkpoints_per_month × 308 bytes
- temporal_visibility size = executions_per_month × 13,300 bytes
- steady-state storage (N-day retention) = (temporal + temporal_visibility) × (retention_days ÷ 30)
- recommended total = steady-state storage × 1.30-1 (headroom: fragmentation, non-uniform load, growth margin)
- storage per node = recommended total ÷ node count
Disclaimer
This work validates that CockroachDB Cloud Advanced functions correctly as a persistence backend for self-hosted Temporal, given the schema patches documented above, and provides real measured data for capacity planning. Temporal is not on CockroachDB's official support matrix. This is compatibility-validated proof-of-concept work, not an officially supported or vendor-certified deployment pattern. Production adoption should include an engineering review of the schema patches, ongoing monitoring for behavior differences from Temporal's tested PostgreSQL backend, and the load-test validation flagged above before committing to a final cluster size.
Appendix: Reference Implementation
return_workflow.py
"""
Return-Automation Saga Workflow
================================
Simulates a post-purchase return/exchange/store-credit platform as a
durable Temporal saga. Designed to generate a realistic, varied checkpoint
(event history) shape for CockroachDB storage sizing.
Saga shape:
ReturnInitiated
-> EligibilityCheck (rules + AI classification activity)
-> PackageProtectionCheck (branch)
-> wait for carrier "package received" signal (with SLA timer)
-> DispositionDecision (AI agent activity: refund/exchange/store-credit)
-> disposition activity (with compensation on failure)
-> CustomerNotify
Concurrently: SupportEscalation signal can arrive at any point and
short-circuits to a human-handled resolution path.
Each Activity call, Timer, and Signal is one or more durable checkpoints
(history events) persisted to CockroachDB via the `temporal` database.
"""
from __future__ import annotations
import asyncio
from dataclasses import dataclass
from datetime import timedelta
from enum import Enum
from temporalio import workflow, activity
from temporalio.common import RetryPolicy
# ---------------------------------------------------------------------------
# Data contracts
# ---------------------------------------------------------------------------
class Disposition(str, Enum):
REFUND = "refund"
EXCHANGE = "exchange"
STORE_CREDIT = "store_credit"
@dataclass
class ReturnRequest:
order_id: str
customer_id: str
item_sku: str
return_reason: str
order_value_cents: int
has_package_protection: bool = False
@dataclass
class ReturnResult:
order_id: str
disposition: str
escalated: bool
compensated: bool
checkpoints_estimate: int = 0
# ---------------------------------------------------------------------------
# Activities (all external side effects live here -- never in workflow code)
# ---------------------------------------------------------------------------
@activity.defn
async def classify_return_reason(reason: str) -> dict:
"""Simulates an AI-agent classification call (e.g. LLM) of free-text
return reason into a structured eligibility signal. In production this
activity would call out to an LLM provider; here it's simulated."""
await asyncio.sleep(0.05)
is_eligible = "counterfeit" not in reason.lower() and "used" not in reason.lower()
return {"eligible": is_eligible, "confidence": 0.92, "category": "standard"}
@activity.defn
async def check_package_protection(order_id: str, protected: bool) -> dict:
await asyncio.sleep(0.02)
return {"claim_valid": protected, "coverage_cents": 5000 if protected else 0}
@activity.defn
async def recommend_disposition(req: ReturnRequest, eligibility: dict) -> str:
"""AI agent step: recommends refund / exchange / store credit based on
order value, inventory signals, and customer history (simulated)."""
await asyncio.sleep(0.05)
if req.order_value_cents > 10000:
return Disposition.EXCHANGE.value
return Disposition.STORE_CREDIT.value
@activity.defn
async def process_refund(order_id: str, amount_cents: int) -> str:
await asyncio.sleep(0.03)
return f"refund:{order_id}:{amount_cents}"
@activity.defn
async def process_exchange(order_id: str, sku: str) -> str:
await asyncio.sleep(0.03)
return f"exchange:{order_id}:{sku}"
@activity.defn
async def process_store_credit(order_id: str, amount_cents: int) -> str:
await asyncio.sleep(0.03)
return f"credit:{order_id}:{amount_cents}"
@activity.defn
async def compensate_disposition(order_id: str, disposition: str) -> None:
"""Saga compensation: reverse whatever the disposition activity did."""
await asyncio.sleep(0.02)
@activity.defn
async def notify_customer(order_id: str, disposition: str) -> None:
await asyncio.sleep(0.02)
# ---------------------------------------------------------------------------
# Workflow (deterministic orchestration only)
# ---------------------------------------------------------------------------
DEFAULT_RETRY = RetryPolicy(maximum_attempts=3, backoff_coefficient=2.0)
@workflow.defn
class ReturnWorkflow:
def __init__(self) -> None:
self._package_received = False
self._escalated = False
@workflow.signal
async def package_received(self) -> None:
self._package_received = True
@workflow.signal
async def escalate_to_support(self) -> None:
self._escalated = True
@workflow.run
async def run(self, req: ReturnRequest) -> ReturnResult:
# 1. Eligibility check (AI classification activity)
eligibility = await workflow.execute_activity(
classify_return_reason,
req.return_reason,
start_to_close_timeout=timedelta(seconds=60),
retry_policy=DEFAULT_RETRY,
)
if not eligibility["eligible"]:
await workflow.execute_activity(
notify_customer,
args=[req.order_id, "rejected"],
start_to_close_timeout=timedelta(seconds=60),
)
return ReturnResult(req.order_id, "rejected", False, False)
# 2. Package protection branch
if req.has_package_protection:
await workflow.execute_activity(
check_package_protection,
args=[req.order_id, req.has_package_protection],
start_to_close_timeout=timedelta(seconds=60),
retry_policy=DEFAULT_RETRY,
)
# 3. Wait for physical package OR support escalation, with an SLA timer.
# This produces TimerStarted/Fired and SignalReceived checkpoints.
try:
await workflow.wait_condition(
lambda: self._package_received or self._escalated,
timeout=timedelta(days=14),
)
except asyncio.TimeoutError:
# SLA breach: auto-expire the case
return ReturnResult(req.order_id, "expired", self._escalated, False)
if self._escalated:
await workflow.execute_activity(
notify_customer,
args=[req.order_id, "escalated"],
start_to_close_timeout=timedelta(seconds=60),
)
return ReturnResult(req.order_id, "escalated", True, False)
# 4. Disposition decision (AI agent step)
disposition = await workflow.execute_activity(
recommend_disposition,
args=[req, eligibility],
start_to_close_timeout=timedelta(seconds=60),
retry_policy=DEFAULT_RETRY,
)
# 5. Execute disposition, with saga-style compensation on failure
compensated = False
try:
if disposition == Disposition.REFUND.value:
await workflow.execute_activity(
process_refund,
args=[req.order_id, req.order_value_cents],
start_to_close_timeout=timedelta(seconds=60),
retry_policy=DEFAULT_RETRY,
)
elif disposition == Disposition.EXCHANGE.value:
await workflow.execute_activity(
process_exchange,
args=[req.order_id, req.item_sku],
start_to_close_timeout=timedelta(seconds=60),
retry_policy=DEFAULT_RETRY,
)
else:
await workflow.execute_activity(
process_store_credit,
args=[req.order_id, req.order_value_cents],
start_to_close_timeout=timedelta(seconds=60),
retry_policy=DEFAULT_RETRY,
)
except Exception:
await workflow.execute_activity(
compensate_disposition,
args=[req.order_id, disposition],
start_to_close_timeout=timedelta(seconds=60),
)
compensated = True
# 6. Notify
await workflow.execute_activity(
notify_customer,
args=[req.order_id, disposition],
start_to_close_timeout=timedelta(seconds=60),
)
return ReturnResult(req.order_id, disposition, False, compensated)
worker.py
"""
Worker for the return-automation saga.
Polls the `returns` task queue on the local Temporal Server
(backed by CockroachDB) and executes workflow tasks + activities.
Usage:
python worker.py
"""
import asyncio
import logging
from temporalio.client import Client
from temporalio.worker import Worker
from return_workflow import (
ReturnWorkflow,
classify_return_reason,
check_package_protection,
recommend_disposition,
process_refund,
process_exchange,
process_store_credit,
compensate_disposition,
notify_customer,
)
TEMPORAL_ADDRESS = "localhost:7233"
NAMESPACE = "default"
TASK_QUEUE = "returns"
logging.basicConfig(level=logging.INFO)
log = logging.getLogger("worker")
async def main() -> None:
client = await Client.connect(TEMPORAL_ADDRESS, namespace=NAMESPACE)
log.info(f"Connected to {TEMPORAL_ADDRESS}, namespace={NAMESPACE}")
worker = Worker(
client,
task_queue=TASK_QUEUE,
workflows=[ReturnWorkflow],
activities=[
classify_return_reason,
check_package_protection,
recommend_disposition,
process_refund,
process_exchange,
process_store_credit,
compensate_disposition,
notify_customer,
],
# Tuned down from SDK defaults to avoid CPU contention on a single
# test machine -- size these to your own available cores/RAM.
max_concurrent_activities=30,
max_concurrent_workflow_tasks=30,
max_concurrent_workflow_task_polls=8,
max_concurrent_activity_task_polls=8,
)
log.info(f"Worker polling task queue '{TASK_QUEUE}'... (Ctrl+C to stop)")
await worker.run()
if __name__ == "__main__":
asyncio.run(main())
batch.py
"""
Batch-starts return-case workflows at scale, toward a target checkpoint
count. Unlike waiting on each workflow's .result(), this does NOT block --
at thousands of workflows that's slow and memory-heavy. It starts +
signals each workflow with bounded concurrency, then lets the worker
process the backlog independently. Check progress separately via SQL
or `temporal workflow list`.
Usage:
python batch_start.py --count 1000 --concurrency 25
"""
import argparse
import asyncio
import random
import time
import uuid
from temporalio.client import Client
from return_workflow import ReturnRequest, ReturnWorkflow
TEMPORAL_ADDRESS = "localhost:7233"
NAMESPACE = "default"
TASK_QUEUE = "returns"
REASONS = [
"wrong size", "changed my mind", "arrived damaged",
"not as described", "found cheaper elsewhere", "defective item",
]
def random_request() -> ReturnRequest:
return ReturnRequest(
order_id=f"order-{uuid.uuid4().hex[:8]}",
customer_id=f"cust-{uuid.uuid4().hex[:6]}",
item_sku=f"sku-{random.randint(1000, 9999)}",
return_reason=random.choice(REASONS),
order_value_cents=random.randint(1500, 25000),
has_package_protection=random.random() < 0.3,
)
async def start_and_signal_one(client: Client, sem: asyncio.Semaphore, counters: dict):
async with sem:
req = random_request()
try:
handle = await client.start_workflow(
ReturnWorkflow.run,
req,
id=f"return-{req.order_id}",
task_queue=TASK_QUEUE,
)
await handle.signal(ReturnWorkflow.package_received)
counters["started"] += 1
except Exception as e:
counters["errors"] += 1
if counters["errors"] <= 10:
print(f" ERROR starting/signaling {req.order_id}: {e}")
async def main(count: int, concurrency: int) -> None:
client = await Client.connect(TEMPORAL_ADDRESS, namespace=NAMESPACE)
print(f"Connected. Starting {count} workflows, concurrency={concurrency}...")
sem = asyncio.Semaphore(concurrency)
counters = {"started": 0, "errors": 0}
start_time = time.monotonic()
tasks = [
asyncio.create_task(start_and_signal_one(client, sem, counters))
for _ in range(count)
]
last_report = 0
while not all(t.done() for t in tasks):
await asyncio.sleep(2)
done = counters["started"] + counters["errors"]
if done - last_report >= max(count // 20, 100):
elapsed = time.monotonic() - start_time
rate = done / elapsed if elapsed > 0 else 0
print(f" progress: {done}/{count} ({rate:.1f}/sec)")
last_report = done
await asyncio.gather(*tasks)
elapsed = time.monotonic() - start_time
print(f"\nDone. started={counters['started']} errors={counters['errors']} "
f"in {elapsed:.1f}s ({counters['started']/elapsed:.1f}/sec)")
print("Note: workflows may still be processing on the worker -- this only "
"confirms they were successfully started+signaled, not completed.")
print("Check completion progress with:")
print(f' temporal --address {TEMPORAL_ADDRESS} -n {NAMESPACE} workflow count '
f'--query "WorkflowType=\'ReturnWorkflow\' AND ExecutionStatus=\'Running\'"')
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--count", type=int, required=True)
parser.add_argument("--concurrency", type=int, default=25)
args = parser.parse_args()
asyncio.run(main(args.count, args.concurrency))
temporal-server.yaml
log:
stdout: true
level: info
persistence:
defaultStore: cockroachdb-default
visibilityStore: cockroachdb-visibility
numHistoryShards: 256
datastores:
cockroachdb-default:
sql:
pluginName: "postgres12"
databaseName: "temporal"
connectAddr: "<your-cluster-host>.cockroachlabs.cloud:26257"
connectProtocol: "tcp"
user: "<your-sql-user>"
password: "<your-password>"
maxConns: 200
maxIdleConns: 200
maxConnLifetime: "1h"
tls:
enabled: true
caFile: "<path-to-ca-cert>"
cockroachdb-visibility:
sql:
pluginName: "postgres12"
databaseName: "temporal_visibility"
connectAddr: "<your-cluster-host>.cockroachlabs.cloud:26257"
connectProtocol: "tcp"
user: "<your-sql-user>"
password: "<your-password>"
maxConns: 100
maxIdleConns: 100
maxConnLifetime: "1h"
tls:
enabled: true
caFile: "<path-to-ca-cert>"
global:
membership:
maxJoinDuration: 30s
broadcastAddress: "127.0.0.1"
pprof:
port: 7936
services:
frontend:
rpc:
grpcPort: 7233
membershipPort: 6933
bindOnIP: '0.0.0.0'
httpPort: 7243
matching:
rpc:
grpcPort: 7235
membershipPort: 6935
bindOnLocalHost: true
history:
rpc:
grpcPort: 7234
membershipPort: 6934
bindOnLocalHost: true
worker:
rpc:
membershipPort: 6939
clusterMetadata:
enableGlobalNamespace: false
failoverVersionIncrement: 10
masterClusterName: "active"
currentClusterName: "active"
clusterInformation:
active:
enabled: true
initialFailoverVersion: 1
rpcName: "frontend"
rpcAddress: "localhost:7233"
httpAddress: "localhost:7243"
dcRedirectionPolicy:
policy: "noop"
temporal-ui-server.yaml
temporalGrpcAddress: 127.0.0.1:7233
host: 0.0.0.0
port: 8233
enableUi: true
defaultNamespace: default




Top comments (0)