Temporal Retention & History Limits
Approval gates can park a run for days. The graph engine parks on an unbounded wait_condition, so a parked run adds O(1) history events per approval decision — far below Temporal's per-execution history limits. Two deployment-side settings back this up:
- Namespace retention: 168h (7 days). How long closed workflow histories stay queryable (
tctl,handle.describe(), run status reconciliation for recently finished runs). The Temporal console is closed by default and opened on demand — see Temporal Admin Access. - Raised history limits. Warn at 51,200 events / 25 MiB and error at 262,144 events / 100 MiB, so a slow event leak degrades into server-log warnings instead of killing multi-day runs.
New deployments
Nothing to do. Both compose files set on the temporal service:
environment:
- DEFAULT_NAMESPACE_RETENTION=168h
- DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/custom.yaml
volumes:
- ./temporal/dynamicconfig.yaml:/etc/temporal/config/dynamicconfig/custom.yaml:rotemporalio/auto-setup passes DEFAULT_NAMESPACE_RETENTION to temporal operator namespace create when it creates the default namespace, and the server loads the mounted dynamic config at boot.
Changing retention on a running deployment
DEFAULT_NAMESPACE_RETENTION only applies on first boot — auto-setup skips namespace creation when the default namespace already exists, so recreating the container does not change retention. Update it in place:
docker exec <temporal-container> sh -c 'tctl --address $(hostname -i):7233 --ns default namespace update --retention 168h'The explicit --address matters: the compose files leave BIND_ON_IP unset, so the image's entrypoint binds the frontend to the container's network IP — not 127.0.0.1, which is where a bare tctl connects (this is also why the compose healthcheck passes --address temporal:7233). A bare tctl inside the container will fail with a connection error.
The dynamic-config half needs no manual step: recreate the service (docker compose up -d temporal) so the volume mount and DYNAMIC_CONFIG_FILE_PATH take effect. Temporal also hot-reloads the file while running (~60s poll), so later edits to deploy/compose/temporal/dynamicconfig.yaml apply without a restart.
Verifying
Retention shows 168h0m0s:
docker exec <temporal-container> sh -c 'tctl --address $(hostname -i):7233 --ns default namespace describe'Dynamic config loaded — the server logs the file at startup:
docker logs <temporal-container> 2>&1 | grep -i "dynamic config"The keys in deploy/compose/temporal/dynamicconfig.yaml (limit.historyCount.warn/error, limit.historySize.warn/error) are verified against Temporal server v1.24.2 (common/dynamicconfig/constants.go); a typo'd key is silently ignored, so check the file against those constants when bumping the server image.
What these limits do NOT fix
The limits are defense in depth, not a license to generate history. A workflow that legitimately approaches the warn thresholds (very large graphs, thousands of activities) is a candidate for continue-as-new — the worker logs a warning once per run when Temporal suggests it (is_continue_as_new_suggested). Flow runs do not roll over automatically, so a run approaching the error threshold should be split into smaller flows.