← Files TemporalARCHIVED FILE

skills/temporal-ops/references/ops/recipes.md

19.1 KB · Oct 4, 2026 · 12:08 UTC

↓ Download file

# Ops Recipes

End-to-end operational playbooks that chain commands from the ops reference files.
For triage-focused walkthroughs, see `../triage/recipes.md`.

---

## (a) Set up a new Cloud namespace with API key auth (end-to-end)

**When to use:** Provisioning a new Temporal Cloud namespace from scratch, using API key authentication.

### Step 1: Create the namespace

```bash
tcld namespace create \
    --namespace <namespace_name>.<account_suffix> \
    --region <region> \
    --auth-method api_key \
    --retention-days 30 \
    --enable-delete-protection=true
```

Requires Developer, Account Owner, or Global Admin account-level role.
The creator is automatically granted Namespace Admin permission.

Optional flags:
- `--search-attribute "name=type"` (types: `Bool`, `Datetime`, `Double`, `Int`, `Keyword`, `Text`).
- `--tag "key=value"` (up to 10 tags per namespace).
- `--user-namespace-permission "email=permission"` (permissions: `Admin`, `Write`, `Read`).
- Omit `--enable-delete-protection` (or pass `--enable-delete-protection=false`) to skip delete protection; it is disabled by default.

### Step 2: Create a service account for Workers

```bash
tcld service-account create -n "<name>" -d "<description>" --ar "developer" \
    --np "<namespace_name>.<account_suffix>=Write"
```

Note the returned `ServiceAccountId`.

### Step 3: Create an API key for the service account

```bash
tcld apikey create \
    --name <key_name> \
    --description "<description>" \
    --duration <duration> \
    --service-account-id <service-account-id>
```

Save the returned key secret.

### Step 4: Verify connectivity

Set the API key and test with the Temporal CLI:

```bash
export TEMPORAL_API_KEY=<key-secret>
temporal workflow list \
    --address <namespace_name>.<account_suffix>.tmprl.cloud:7233 \
    --namespace <namespace_name>.<account_suffix>
```

### Step 5: (Optional) Grant additional user access

```bash
tcld user set-namespace-permissions \
    --user-email <email> \
    --namespace-permission <namespace_name>.<account_suffix>=<permission>
```

Permissions: `Admin`, `Write`, `Read`.

---

## (b) Check current APS and capacity mode for a Cloud namespace

**When to use:** You need to know whether a namespace is On-Demand or Provisioned and what its current APS limit is.

1. Get the namespace details:

   ```bash
   tcld namespace get \
       --namespace <namespace_name>.<account_suffix>
   ```

   Output is JSON by default (no `--format` flag exists).

2. In the JSON output, look for the capacity configuration section. Key fields:

   - **Capacity mode**: `on_demand` or `provisioned`.
   - **TRU count** (if provisioned): the number of Temporal Resource Units allocated. Valid values: 2, 3, 4, 6, 8, 10, 12.
   - **APS limit**: On-Demand default is 500; each TRU provides 500 APS.

3. To check whether throttling is occurring, look for `temporal_cloud_v0_resource_exhausted_errors` in your metrics.

---

## (c) Switch capacity mode from On-Demand to Provisioned

**When to use:** You are preparing for a planned spike (load test, promotion, migration) and need to pre-provision capacity beyond the On-Demand auto-scaling limit.

1. Confirm current capacity mode (see playbook (b)):

   ```bash
   tcld namespace get \
       --namespace <namespace_name>.<account_suffix>
   ```

2. Switch to Provisioned mode with the desired TRU count:

   ```bash
   tcld namespace capacity update \
       --namespace <namespace_name>.<account_suffix> \
       --capacity-mode provisioned \
       --capacity-value <tru_count>
   ```

   Valid `--capacity-value` values: 2, 3, 4, 6, 8, 10, 12.

   Temporal aims to provision the additional capacity within two minutes.

   For requests in excess of 4 TRUs in regions outside of the US, submit a support ticket to ensure capacity availability.

   Requires **Global Admin** or **Namespace Admin** role.

3. Verify the change took effect:

   ```bash
   tcld namespace get \
       --namespace <namespace_name>.<account_suffix>
   ```

   Confirm the capacity mode is `provisioned` and the TRU count matches your request.

4. When the spike is over, switch back to On-Demand:

   ```bash
   tcld namespace capacity update \
       --namespace <namespace_name>.<account_suffix> \
       --capacity-mode on_demand
   ```

   When switching back to On-Demand mode, your APS limit resets to the running average from the last 7 days. Plan for this if your workload is sensitive to the transition.

---

## (d) Find and triage all hung workflows in a namespace

**When to use:** You suspect workflows are stuck and need to locate them and understand why they are not making progress.

### Step 1: Count potentially stuck workflows

```bash
temporal workflow count \
    --query "ExecutionStatus = 'Running' AND StartTime < '2024-01-15T09:00:00Z'"
```

Replace the timestamp with your threshold for "too long". A count greater than zero indicates workflows that have been running longer than expected.

### Step 2: List the stuck workflows

```bash
temporal workflow list \
    --query "ExecutionStatus = 'Running' AND StartTime < '2024-01-15T09:00:00Z'" \
    --limit 20
```

You can narrow further by Task Queue or Workflow Type:

```bash
temporal workflow list \
    --query "ExecutionStatus = 'Running' AND TaskQueue = 'my-task-queue' AND StartTime < '2024-01-15T09:00:00Z'"
```

### Step 3: Check worker health on the relevant Task Queue

```bash
temporal task-queue describe \
    --task-queue my-task-queue
```

Look for: active pollers present, `LastAccessTime` within the last minute, no growing `ApproximateBacklogCount`.

If there are no pollers, no Workers are running for this Task Queue and workflows on this queue cannot make progress.

### Step 4: Inspect individual stuck workflows

```bash
temporal workflow describe --workflow-id <id>
```

```bash
temporal workflow show --workflow-id <id> --reverse
```

```bash
temporal workflow stack --workflow-id <id>
```

### Step 5: Diagnose root cause

For diagnosing *why* a specific workflow is stuck (pending activities, pending child workflows, non-determinism, etc.), follow the triage procedures in `../triage/workflow-stuck.md`.

---

## (e) Rotate an API key without downtime

**When to use:** An API key is approaching expiration or needs to be rotated for security hygiene.

1. Create a new API key (for a user):

   ```bash
   tcld apikey create --name <name> \
       --description "<description>" \
       --duration <duration>
   ```

   Or for a Service Account:

   ```bash
   tcld apikey create \
       --name <name> \
       --description "<description>" \
       --duration <duration> \
       --service-account-id <service-account-id>
   ```

   You may reuse key names.

   Save the returned key secret -- it is only shown once.

2. Verify both the original and new key function properly:

   ```bash
   temporal workflow list \
       --address <namespace>.<account>.tmprl.cloud:7233 \
       --namespace <namespace_id>.<account_id>
   ```

   Set `TEMPORAL_API_KEY` to each key in turn and confirm the command succeeds.

3. Update clients and workers to load the new key.

4. Once no traffic uses the old key, delete it:

   ```bash
   tcld apikey delete --id <old_apikey_id>
   ```

   Alternatively, disable before deleting to validate nothing breaks:

   ```bash
   tcld apikey disable --id <old_apikey_id>
   ```

**Limits:** Up to 10 non-expired keys per user; up to 20 non-expired keys per Service Account. Maximum expiration: 2 years.

---

## (f) Audit namespace access (users + keys + service accounts)

**When to use:** You need a complete picture of who and what can access a namespace -- humans, API keys, and service accounts.

### Step 1: List all users with access to the namespace

```bash
tcld user list --namespace <namespace_name>.<account_suffix>
```

This filters to users with direct permissions on the specified namespace. Users with account-level roles (e.g., Account Owner, Global Admin) have implicit access to all namespaces but may not appear in this filtered list — check `tcld user list` (without `--namespace`) and inspect account roles to get the full picture.

### Step 2: Inspect individual user permissions

```bash
tcld user get --user-email <email>
```

Check the account role (`admin`, `developer`, `read`) and namespace-level permissions (`Admin`, `Write`, `Read`).

### Step 3: List all user groups

```bash
tcld user-group list
```

For each group with namespace access, list its members:

```bash
tcld user-group list-members --group-id <id>
```

### Step 4: List all service accounts

```bash
tcld service-account list
```

Review the output for service accounts that have permissions on the target namespace. Namespace-scoped Service Accounts always have a `Read` Account Role and are restricted to a single namespace.

### Step 5: List all API keys

```bash
tcld apikey list
```

Cross-reference the API key owners (user IDs or service account IDs) against the users and service accounts identified above.

---
## (g) Rotate mTLS certificates

**When to use:** A CA certificate is approaching expiration, or you need to switch to a new CA without disrupting running Workers.

Temporal Cloud sends email notifications 15 days before certificate expiration.

### Step 1: Generate a new CA certificate

```bash
tcld generate-certificates certificate-authority-certificate \
    --organization <value> \
    --validity-period <duration> \
    --ca-certificate-file <new_ca>.pem \
    --ca-key-file <new_ca>.key
```

Default key algorithm is ECDSA P-384. Maximum duration: 1 year.

### Step 2: Generate new end-entity (leaf) certificates

```bash
tcld generate-certificates end-entity-certificate \
    --organization <value> \
    --validity-period <duration> \
    --ca-certificate-file <new_ca>.pem \
    --ca-key-file <new_ca>.key \
    --certificate-file <new_client>.pem \
    --key-file <new_client>.key
```

End-entity certificate must expire before its root CA certificate.

### Step 3: Create a combined PEM bundle with old and new CA certificates

Concatenate both CA certificates into a single PEM file:

```
-----BEGIN CERTIFICATE-----
... old CA cert ...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... new CA cert ...
-----END CERTIFICATE-----
```

### Step 4: Upload the combined bundle (replaces all existing CAs)

```bash
tcld namespace accepted-client-ca set \
    --namespace <namespace_name>.<account_suffix> \
    --ca-certificate-file <combined>.pem
```

Both old and new end-entity certificates will now be accepted.

### Step 5: Roll out new end-entity certificates to Workers and Clients

Deploy the new leaf certificates to all Workers and Clients. Monitor traffic to the old certificate until it ceases.

### Step 6: Remove the old CA certificate

Create a file containing only the new CA certificate and run `set` again:

```bash
tcld namespace accepted-client-ca set \
    --namespace <namespace_name>.<account_suffix> \
    --ca-certificate-file <new_ca_only>.pem
```

### Step 7: Verify the namespace only has the new CA

```bash
tcld namespace accepted-client-ca list \
    --namespace <namespace_name>.<account_suffix>
```

Do NOT use a CA certificate signed with SHA-1 -- such signatures are rejected.

---

## (h) Check self-hosted cluster health

**When to use:** You want to verify that a self-hosted Temporal cluster is operational and inspect its configuration.

The full command reference for self-hosted cluster operations lives in [self-hosted-admin.md](self-hosted-admin.md). This recipe chains the key commands into a quick health check.

1. **Cluster health:** `temporal operator cluster health` — returns `SERVING` if healthy.
2. **Cluster details:** `temporal operator cluster describe --detail` — Cluster Name, persistence, visibility, shard count.
3. **Namespaces:** `temporal operator namespace list` — all Namespaces on the Service.
4. **Worker health on key Task Queues:** `temporal task-queue describe --task-queue <task_queue_name>` — look for active pollers and no growing `ApproximateBacklogCount`.
5. **Spot-check stuck workflows:** `temporal workflow count --query "ExecutionStatus = 'Running' AND StartTime < '<threshold>'"` — if high, follow playbook (d) above.

For remote clusters, pass `--address <host>:<port>` (and TLS flags if enabled). See [self-hosted-admin.md → Global flags](self-hosted-admin.md#global-flags-summary) for the full flag set.

---

## (i) View billing and generate a billing report

**When to use:** You need to understand your Temporal Cloud costs at the namespace level, or generate a CSV billing report for FinOps tooling.

### Step 1: Review billing in the Cloud UI

Navigate to the **Billing** page in the Temporal Cloud UI. Account Owners and Finance Admins can view:

- Current balance and recent bill
- Invoices table (with downloadable invoices for prior months)
- Credits table
- Cost by Namespace (per-namespace proportional cost breakdown)

### Step 2: Generate a billing report via the Billing API

The Billing API provides namespace-level cost attribution in CSV format.

Report generation is asynchronous:

1. Call `CreateBillingReport` with the desired date range (billing-month boundaries) and granularity. The response includes a `billing_report_id` and `async_operation_id`.
2. Poll `GetBillingReport` using the `billing_report_id` with exponential backoff.
3. When the state is `BILLING_REPORT_STATE_GENERATED`, retrieve the download URL.
4. Download the CSV before the URL expires.

Date range limits by granularity:

| Granularity | Available range |
|---|---|
| Hourly | Current + previous billing month |
| Daily | Current + previous two billing months |
| Monthly | Current + previous eleven billing months |

### Step 3: Interpret the report

Key columns to understand:

- `ContractedCost`: The actual cost (not `Cost` or `TotalCost`).
- `ResourceID`: `namespace_name.account_id` (e.g., `production.a2dd6`), not just the namespace name.
- `BillingCurrency`: Values are in cents (e.g., `USD (cents)`).

Only one billing report per account is generated at a time; additional requests are queued.

---

## (j) Configure an Audit Log sink

**When to use:** You need to stream Temporal Cloud control plane Audit Logs to your infrastructure for compliance or monitoring.

Audit Logs capture control plane events only -- they do NOT capture data plane events (Workflow Start, etc.).

Required role: Account Owner or Global Administrator.

### Option A: AWS Kinesis

1. Ensure you have a Kinesis Data Stream in your AWS account. An [AWS CloudFormation template](https://temporal-auditlogs-config.s3.us-west-2.amazonaws.com/cloudformation/iam-role-for-temporal-audit-logs.yaml) is available to create the required IAM role.

2. In the Cloud UI: **Settings** > **Audit Logs** > **Setup**.

3. Choose **Auto** (configure CloudFormation from the UI) or **Manual** (download a template).

4. Enter the **Kinesis ARN**, **Role name**, and **AWS region**.

5. Complete the CloudFormation stack creation.

6. Use the **Verify** button to confirm Temporal can write to the stream.

First logs appear within 10 minutes.

### Option B: GCP Pub/Sub

1. Create a Pub/Sub topic and set up a service account in the same GCP project (or skip if using Terraform).

2. In the Cloud UI: **Settings** > **Audit Logs** > **Setup** > **Pub/Sub**.

3. Enter the **service account email** and **Topic name**.

4. Choose **Manual** or **Deploy with Terraform** to configure permissions.

5. Use the **Verify** button, then click **Create**.

Audit Logs appear in Pub/Sub within 10 minutes.

### Verify the sink is working

The Audit Logs page of the Cloud UI shows the current status: an **On** badge if functioning normally, or an error summary if an issue is detected.

### Accessing logs via API

Audit Logs are accessible for the past 30 days without a sink.

**[Cloud Ops API](https://docs.temporal.io/ops) (retrieving log records):** Use `StartTimeInclusive`, `EndTimeExclusive`, `PageSize` (max 1000, default 100), and `PageToken` for pagination.

tcld does not retrieve log records; it only manages export sinks. List the configured sinks with:

```bash
tcld account audit-log kinesis list
tcld account audit-log pubsub list
```

---

## (k) Provision resources with Terraform

**When to use:** You want to automate Temporal Cloud resource management (Namespaces, Users, Service Accounts, API Keys, Nexus Endpoints) using infrastructure as code.

Provider source: [github.com/temporalio/terraform-provider-temporalcloud](https://github.com/temporalio/terraform-provider-temporalcloud).

### Step 1: Set up the Terraform provider

```bash
export TEMPORAL_CLOUD_API_KEY=<your-secret-key>
```

```hcl
terraform {
  required_providers {
    temporalcloud = {
      source = "temporalio/temporalcloud"
    }
  }
}

provider "temporalcloud" {

}
```

### Step 2: Define resources

Example Namespace:

```hcl
resource "temporalcloud_namespace" "namespace" {
  name               = "terraform"
  regions            = ["aws-us-east-1"]
  accepted_client_ca = base64encode(file("ca.pem"))
  retention_days     = 14
}
```

Example User with namespace access:

```hcl
resource "temporalcloud_user" "developer" {
  email          = "developer@example.com"
  account_access = "Developer"

  namespace_accesses = [{
    namespace_id = temporalcloud_namespace.namespace.id
    permission   = "Write"
  }]
}
```

### Step 3: Apply

```bash
terraform init
terraform apply
```

### Key limitations

- Once a resource is managed by Terraform, manage it only through Terraform.
- Terraform cannot create, update, or delete the Account Owner role.
- Namespace access must be managed from the User resource, not the Namespace resource.
- API keys cannot be imported into Terraform -- create new keys instead.
- The Terraform resource for API keys is `temporalcloud_apikey` (no underscore between `api` and `key`).

---

## (l) Set up SAML SSO

**When to use:** You want to enable single sign-on for your organization's Temporal Cloud account using your corporate identity provider.

SAML is available as an add-on for any Temporal Cloud plan.

### Step 1: Locate your Account Id

Find your Account Id (5-6 characters after the period in your Namespace Id, e.g., `f45a2`). Available from the Cloud UI profile dropdown or from any Namespace Id.

### Step 2: Construct the SAML URLs

Entity identifier:

```
urn:auth0:prod-tmprl:ACCOUNT_ID-saml
```

Callback URL:

```
https://login.tmprl.cloud/login/callback?connection=ACCOUNT_ID-saml
```

Replace `ACCOUNT_ID` with your actual Account Id.

### Step 3: Configure your IdP

**Microsoft Entra ID:** Create an Enterprise application, configure SAML with the entity identifier, callback URL, and sign on URL (`https://cloud.temporal.io/login/saml?connection=ACCOUNT_ID-saml`). Set NameID to `user.userprincipalname` with format `emailAddress`. Collect the Certificate (Base64) and Login URL.

**Okta:** Create a SAML 2.0 app integration. Set Single sign on URL to the callback URL. Set Audience URI to the entity identifier. Set Name ID format to `EmailAddress` with `email` and `name` attribute statements. Collect IdP settings and download the active certificate.

### Step 4: Submit a support ticket

Include:

- The sign-in URL from your application
- The X.509 SAML sign-in certificate in PEM format
- One or more IdP domains to map to the SAML connection

### Step 5: Verify

After Temporal confirms configuration, log in with your email and click **Continue** to be redirected to your IdP.

SHA-256: 1b68a2a9e4cab9f42cc5b09806d7132c964345dc39978bd3bf9899d2f010b551