{
    "version": "https://jsonfeed.org/version/1",
    "title": "ClickHouse Knowledge Base Feed",
    "home_page_url": "https://clickhouse.com/docs/knowledgebase",
    "description": "Feed of articles posted to the ClickHouse Knowledge Base",
    "items": [
        {
            "id": "https://clickhouse.com/docs/knowledgebase/terraform_example",
            "content_html": "This covers an example of how you can use terraform to create/delete clusters using the API<!-- -->\n<!-- -->\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"question\">Question<a href=\"https://clickhouse.com/docs/knowledgebase/terraform_example#question\" class=\"hash-link\" aria-label=\"Direct link to Question\" title=\"Direct link to Question\">​</a></h2>\n<p>How can I use API to manage clusters on ClickHouse Cloud?</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"answer\">Answer<a href=\"https://clickhouse.com/docs/knowledgebase/terraform_example#answer\" class=\"hash-link\" aria-label=\"Direct link to Answer\" title=\"Direct link to Answer\">​</a></h2>\n<p>We will use Terraform to configure our infra and <a href=\"https://registry.terraform.io/providers/ClickHouse/clickhouse/latest/docs\" target=\"_blank\" rel=\"noopener noreferrer\">ClickHouse Provider</a></p>\n<p>Steps:</p>\n<p>1). Create an API Key on Cloud.\nFollow the docs here - <a href=\"https://clickhouse.com/docs/cloud/manage/openapi\" target=\"_blank\" rel=\"noopener noreferrer\">https://clickhouse.com/docs/cloud/manage/openapi</a></p>\n<p>Save the creds locally.</p>\n<p>2). Install Terraform using - <a href=\"https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli\" target=\"_blank\" rel=\"noopener noreferrer\">https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli</a></p>\n<p>You can use Homebrew package manager if you're on Mac.</p>\n<p>3). Create a directory anywhere you like:</p>\n<div class=\"wrapper_EBtA\" style=\"height:56.550000000000004px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-txt\"><code class=\"language-txt\">mkdir test\n➜  test pwd\n/Users/jaijhala/Desktop/terraform/test\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>4). Create 2 files: <code>main.tf</code> and <code>secret.tfvars</code></p>\n<p>Copy the following:</p>\n<p><code>main.tf</code> file would be:</p>\n<div class=\"wrapper_EBtA\" style=\"height:980.2px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-txt\"><code class=\"language-txt\">terraform {\n required_providers {\n   clickhouse = {\n     source = \"ClickHouse/clickhouse\"\n     version = \"0.0.2\"\n   }\n }\n}\n\nvariable \"organization_id\" {\n  type = string\n}\n\nvariable \"token_key\" {\n  type = string\n}\n\nvariable \"token_secret\" {\n  type = string\n}\n\nprovider clickhouse {\n  environment \t= \"production\"\n  organization_id = var.organization_id\n  token_key   \t= var.token_key\n  token_secret\t= var.token_secret\n}\n\n\nvariable \"service_password\" {\n  type = string\n  sensitive   = true\n}\n\nresource \"clickhouse_service\" \"service123\" {\n  name       \t= \"jai-terraform\"\n  cloud_provider = \"aws\"\n  region     \t= \"us-east-2\"\n  tier       \t= \"development\"\n  idle_scaling   = true\n  password  = var.service_password\n  ip_access = [\n\t{\n    \tsource  \t= \"0.0.0.0/0\"\n    \tdescription = \"Anywhere\"\n\t}\n  ]\n}\n\noutput \"CLICKHOUSE_HOST\" {\n  value = clickhouse_service.service123.endpoints.0.host\n}\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>You can replace your own parameters like service name, region etc.. in the resources section above.</p>\n<p><code>secret.tfvars</code> is where you'll put all the API Key related info that you downloaded earlier. The idea behind this file is that all your secret credentials will be hidden from the main config file.</p>\n<p>It would be something like (replace these parameters):</p>\n<div class=\"wrapper_EBtA\" style=\"height:75.4px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-txt\"><code class=\"language-txt\">organization_id = \"e957a5f7-4qe3-4b05-ad5a-d02b2dcd0593\"\ntoken_key = \"QWhhkMeytqQruTeKg\"\ntoken_secret = \"4b1dNmjWdLUno9lXxmKvSUcPP62jvn7irkuZPbY\"\nservice_password = \"password123!\"\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>5).  Run <code>terraform init</code> from this directory</p>\n<p>Expected output:</p>\n<div class=\"wrapper_EBtA\" style=\"height:471.25000000000006px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-txt\"><code class=\"language-txt\">Initializing the backend...\n\nInitializing provider plugins...\n- Finding clickhouse/clickhouse versions matching \"0.0.2\"...\n- Installing clickhouse/clickhouse v0.0.2...\n- Installed clickhouse/clickhouse v0.0.2 (self-signed, key ID D7089EE5C6A92ED1)\n\nPartner and community providers are signed by their developers.\nIf you'd like to know more about provider signing, you can read about it here:\nhttps://www.terraform.io/docs/cli/plugins/signing.html\n\nTerraform has created a lock file .terraform.lock.hcl to record the provider\nselections it made above. Include this file in your version control repository\nso that Terraform can guarantee to make the same selections by default when\nyou run \"terraform init\" in the future.\n\nTerraform has been successfully initialized!\n\nYou may now begin working with Terraform. Try running \"terraform plan\" to see\nany changes that are required for your infrastructure. All Terraform commands\nshould now work.\n\nIf you ever set or change modules or backend configuration for Terraform,\nrerun this command to reinitialize your working directory. If you forget, other\ncommands will detect it and remind you to do so if necessary.\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>6). Run <code>terraform apply -var-file=secret.tfvars</code> command.</p>\n<p>Something like:</p>\n<div class=\"wrapper_EBtA\" style=\"height:697.45px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-txt\"><code class=\"language-txt\">➜  test terraform apply -var-file=secret.tfvars\n\nTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with\nthe following symbols:\n  + create\n\nTerraform will perform the following actions:\n\n  # clickhouse_service.service123 will be created\n  + resource \"clickhouse_service\" \"service123\" {\n      + cloud_provider = \"aws\"\n      + endpoints      = (known after apply)\n      + id             = (known after apply)\n      + idle_scaling   = true\n      + ip_access      = [\n          + {\n              + description = \"Anywhere\"\n              + source      = \"0.0.0.0/0\"\n            },\n        ]\n      + last_updated   = (known after apply)\n      + name           = \"jai-terraform\"\n      + password       = (sensitive value)\n      + region         = \"us-east-2\"\n      + tier           = \"development\"\n    }\n\nPlan: 1 to add, 0 to change, 0 to destroy.\n\nChanges to Outputs:\n  + CLICKHOUSE_HOST = (known after apply)\n\nDo you want to perform these actions?\n  Terraform will perform the actions described above.\n  Only 'yes' will be accepted to approve.\n\n  Enter a value: yes\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>Type <code>yes</code> and hit enter</p>\n<p>Side note: Notice it says <code>password       = (sensitive value)</code> above.\nThis is because we set <code>sensitive   = true</code> for the password in the main.tf file.</p>\n<p>7). It will take a couple of mins to create the service but eventually it should come up like:</p>\n<div class=\"wrapper_EBtA\" style=\"height:395.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-txt\"><code class=\"language-txt\">  Enter a value: yes\n\nclickhouse_service.service123: Creating...\nclickhouse_service.service123: Still creating... [10s elapsed]\nclickhouse_service.service123: Still creating... [20s elapsed]\nclickhouse_service.service123: Still creating... [30s elapsed]\nclickhouse_service.service123: Still creating... [40s elapsed]\nclickhouse_service.service123: Still creating... [50s elapsed]\nclickhouse_service.service123: Still creating... [1m0s elapsed]\nclickhouse_service.service123: Still creating... [1m10s elapsed]\nclickhouse_service.service123: Still creating... [1m20s elapsed]\nclickhouse_service.service123: Still creating... [1m30s elapsed]\nclickhouse_service.service123: Still creating... [1m40s elapsed]\nclickhouse_service.service123: Creation complete after 1m41s [id=aa8d8d63-1878-4600-8470-630715af38ed]\n\nApply complete! Resources: 1 added, 0 changed, 0 destroyed.\n\nOutputs:\n\nCLICKHOUSE_HOST = \"h3ljlaqez6.us-east-2.aws.clickhouse.cloud\"\n➜  test\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>8). Check Cloud Console, you should be able to see the service created.</p>\n<p>9). To clean up/destroy the service again, run <code>terraform destroy -var-file=secret.tfvars</code></p>\n<p>Something like:</p>\n<div class=\"wrapper_EBtA\" style=\"height:395.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-txt\"><code class=\"language-txt\">Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with\nthe following symbols:\n  - destroy\n\nTerraform will perform the following actions:\n\n  # clickhouse_service.service123 will be destroyed\n  - resource \"clickhouse_service\" \"service123\" {\n      - cloud_provider = \"aws\" -&gt; null\n      - ............\n\nPlan: 0 to add, 0 to change, 1 to destroy.\n\nChanges to Outputs:\n  - CLICKHOUSE_HOST = \"h3ljlaqez6.us-east-2.aws.clickhouse.cloud\" -&gt; null\n\nDo you really want to destroy all resources?\n  Terraform will destroy all your managed infrastructure, as shown above.\n  There is no undo. Only 'yes' will be accepted to confirm.\n\n  Enter a value:\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>Type yes and hit enter</p>\n<p>10).</p>\n<div class=\"wrapper_EBtA\" style=\"height:113.10000000000001px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-txt\"><code class=\"language-txt\">clickhouse_service.service123: Destroying... [id=aa8d8d63-1878-4600-8470-630715af38ed]\nclickhouse_service.service123: Still destroying... [id=aa8d8d63-1878-4600-8470-630715af38ed, 10s elapsed]\nclickhouse_service.service123: Still destroying... [id=aa8d8d63-1878-4600-8470-630715af38ed, 20s elapsed]\nclickhouse_service.service123: Destruction complete after 27s\n\nDestroy complete! Resources: 1 destroyed.\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>And it should be gone from the Cloud Console.</p>\n<p>More details about the Cloud API can be found here - <a href=\"https://clickhouse.com/docs/cloud/manage/api/api-overview\" target=\"_blank\" rel=\"noopener noreferrer\">https://clickhouse.com/docs/cloud/manage/api/api-overview</a></p><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2026-07-09T09:13:58.000Z\">Jul 9, 2026</time> · <!-- -->5 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/terraform_example",
            "title": "Terraform example on how to use Cloud API",
            "summary": "This covers an example of how you can use terraform to create/delete clusters using the API",
            "date_modified": "2026-07-09T09:13:58.000Z",
            "tags": [
                "Native Clients and Interfaces"
            ]
        },
        {
            "id": "https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster",
            "content_html": "Setup steps to expose an MSK cluster to ClickPipes through AWS PrivateLink VPC endpoint services.<!-- -->\n<!-- -->\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"overview\">Overview<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#overview\" class=\"hash-link\" aria-label=\"Direct link to Overview\" title=\"Direct link to Overview\">​</a></h2>\n<p>This guide explains how to expose an Amazon MSK cluster to ClickPipes with <a href=\"https://docs.aws.amazon.com/vpc/latest/privatelink/privatelink-share-your-services.html\" target=\"_blank\" rel=\"noopener noreferrer\">AWS PrivateLink VPC endpoint services</a>.</p>\n<p>Use this approach when <a href=\"https://clickhouse.com/docs/integrations/clickpipes/aws-privatelink#msk-multi-vpc\">MSK multi-VPC connectivity</a> is not available or not sufficient, for example:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Cross-region connectivity from ClickPipes to MSK.</li>\n<li class=\"custom-li\">MSK Express clusters.</li>\n<li class=\"custom-li\">Kafka setups that must preserve broker hostnames over private connectivity.</li>\n</ul>\n<p>For same-region standard MSK clusters, MSK multi-VPC connectivity is usually simpler and is the recommended option.</p>\n<p>The same PrivateLink pattern can also be used for self-managed or on-premises Kafka clusters that are exposed through AWS, but the examples in this article use MSK.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-the-setup-works\">How the setup works<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#how-the-setup-works\" class=\"hash-link\" aria-label=\"Direct link to How the setup works\" title=\"Direct link to How the setup works\">​</a></h2>\n<p>Kafka clients first connect to bootstrap brokers, fetch cluster metadata, and then connect directly to the broker hostnames advertised in that metadata.</p>\n<p>For that reason, a single load balancer in front of all brokers is not sufficient for a reliable MSK PrivateLink setup. Instead, create one PrivateLink path per broker:</p>\n<div class=\"wrapper_EBtA\" style=\"height:94.25px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">MSK broker hostname\n  -&gt; ClickPipes reverse private endpoint with custom private DNS\n  -&gt; VPC endpoint service\n  -&gt; Network Load Balancer\n  -&gt; MSK broker private IP\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>Each broker gets its own:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Internal Network Load Balancer (NLB).</li>\n<li class=\"custom-li\">IP target group that targets the broker private IP.</li>\n<li class=\"custom-li\">VPC endpoint service backed by that NLB.</li>\n<li class=\"custom-li\">ClickPipes reverse private endpoint (RPE).</li>\n<li class=\"custom-li\">Custom private DNS name matching that broker hostname.</li>\n</ul>\n<p>The NLB should use TCP passthrough. This allows the Kafka client to keep using the original MSK broker hostname for TLS SNI and certificate validation.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"requirements\">Requirements<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#requirements\" class=\"hash-link\" aria-label=\"Direct link to Requirements\" title=\"Direct link to Requirements\">​</a></h2>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">An MSK cluster in private subnets.</li>\n<li class=\"custom-li\">Broker hostnames and private IP addresses for the MSK cluster.</li>\n<li class=\"custom-li\">Permission to create NLBs, target groups, listeners, and VPC endpoint services in the MSK VPC.</li>\n<li class=\"custom-li\">The ClickPipes AWS account principal allowed on each VPC endpoint service: <code>arn:aws:iam::072088201116:root</code>.</li>\n<li class=\"custom-li\">The custom private DNS feature enabled for your ClickHouse Cloud service. Contact ClickHouse support if the <code>Custom private DNS name</code> field is not available when creating a reverse private endpoint.</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"get-broker-hostnames-and-private-ips\">Get broker hostnames and private IPs<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#get-broker-hostnames-and-private-ips\" class=\"hash-link\" aria-label=\"Direct link to Get broker hostnames and private IPs\" title=\"Direct link to Get broker hostnames and private IPs\">​</a></h2>\n<p>Use the AWS CLI to list MSK broker nodes:</p>\n<div class=\"wrapper_EBtA\" style=\"height:75.4px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-bash\"><code class=\"language-bash\">aws kafka list-nodes \\\n    --cluster-arn &lt;MSK_CLUSTER_ARN&gt; \\\n    --query 'NodeInfoList[].BrokerNodeInfo.{BrokerId:BrokerId,Host:Endpoints[0],PrivateIp:ClientVpcIpAddress}' \\\n    --output table\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>Record each broker hostname, private IP address, and Kafka port. For MSK IAM authentication, the port is usually <code>9098</code>.</p>\n<p>The ClickPipe will use these broker hostnames as its Kafka brokers. Do not use the MSK bootstrap broker string for this UI-based setup.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-one-endpoint-service-per-broker\">Create one endpoint service per broker<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#create-one-endpoint-service-per-broker\" class=\"hash-link\" aria-label=\"Direct link to Create one endpoint service per broker\" title=\"Direct link to Create one endpoint service per broker\">​</a></h2>\n<p>Repeat these steps for each MSK broker.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-an-internal-nlb\">Create an internal NLB<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#create-an-internal-nlb\" class=\"hash-link\" aria-label=\"Direct link to Create an internal NLB\" title=\"Direct link to Create an internal NLB\">​</a></h3>\n<p>Create an internal Network Load Balancer in the MSK VPC. The NLB should use private subnets and must not be internet-facing.</p>\n<p>If a security group is attached to the NLB, disable <code>Enforce Security Group Inbound Rules on Private Link Traffic</code>. This is required to allow traffic from the ClickPipes VPC endpoint to reach the NLB.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-an-ip-target-group\">Create an IP target group<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#create-an-ip-target-group\" class=\"hash-link\" aria-label=\"Direct link to Create an IP target group\" title=\"Direct link to Create an IP target group\">​</a></h3>\n<p>Create a target group with:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Target type: <code>IP</code>.</li>\n<li class=\"custom-li\">Protocol: <code>TCP</code>.</li>\n<li class=\"custom-li\">Port: the broker Kafka port, for example <code>9098</code> for MSK IAM.</li>\n<li class=\"custom-li\">Target: the private IP address of exactly one broker.</li>\n</ul>\n<p>Use TCP health checks on the traffic port.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-a-tcp-listener\">Create a TCP listener<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#create-a-tcp-listener\" class=\"hash-link\" aria-label=\"Direct link to Create a TCP listener\" title=\"Direct link to Create a TCP listener\">​</a></h3>\n<p>Create an NLB listener on the broker Kafka port and forward traffic to the broker-specific target group.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-the-vpc-endpoint-service\">Create the VPC endpoint service<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#create-the-vpc-endpoint-service\" class=\"hash-link\" aria-label=\"Direct link to Create the VPC endpoint service\" title=\"Direct link to Create the VPC endpoint service\">​</a></h3>\n<p>Create a VPC endpoint service backed by the broker-specific NLB.</p>\n<p>Add the ClickPipes AWS principal to the allowed principals:</p>\n<div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">arn:aws:iam::072088201116:root\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>If the MSK cluster and ClickPipes are in different AWS regions, enable <a href=\"https://docs.aws.amazon.com/vpc/latest/privatelink/privatelink-share-your-services.html#endpoint-service-cross-region\" target=\"_blank\" rel=\"noopener noreferrer\">cross-region access</a> by adding the ClickPipes region to the endpoint service supported regions.</p>\n<p>Record the endpoint service name. It will look similar to:</p>\n<div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">com.amazonaws.vpce.&lt;region&gt;.vpce-svc-0123456789abcdef0\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-clickpipes-reverse-private-endpoints\">Create ClickPipes reverse private endpoints<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#create-clickpipes-reverse-private-endpoints\" class=\"hash-link\" aria-label=\"Direct link to Create ClickPipes reverse private endpoints\" title=\"Direct link to Create ClickPipes reverse private endpoints\">​</a></h2>\n<p>In the ClickHouse Cloud console, create one reverse private endpoint for each broker endpoint service:</p>\n<ol class=\"custom-ol\">\n<li class=\"custom-li\">Go to your ClickHouse Cloud service.</li>\n<li class=\"custom-li\">Start creating a ClickPipe.</li>\n<li class=\"custom-li\">Choose <code>Reverse private endpoint</code>.</li>\n<li class=\"custom-li\">Choose <code>VPC endpoint service</code> as the VPC endpoint type.</li>\n<li class=\"custom-li\">Enter the broker-specific VPC endpoint service name.</li>\n<li class=\"custom-li\">In <code>Custom private DNS name</code>, enter the original MSK broker hostname for that broker.</li>\n<li class=\"custom-li\">Create the endpoint.</li>\n<li class=\"custom-li\">If the endpoint is pending acceptance, accept the endpoint connection request in the AWS VPC endpoint service console.</li>\n</ol>\n<p>Repeat the process until every broker hostname has a matching RPE.</p>\n<p>For custom private DNS behavior, naming rules, and API/Terraform options, see\n<a href=\"https://clickhouse.com/docs/integrations/clickpipes/aws-privatelink#custom-private-dns\">Custom private DNS</a>.</p>\n<p>You can also create the reverse private endpoints with <a href=\"https://clickhouse.com/docs/cloud/manage/api/swagger#tag/ClickPipes/operation/clickPipeReversePrivateEndpointCreate\" target=\"_blank\" rel=\"noopener noreferrer\">OpenAPI</a> or Terraform.</p>\n<p>For OpenAPI, create one reverse private endpoint per broker with <code>type</code> set to <code>VPC_ENDPOINT_SERVICE</code>, the broker-specific VPC endpoint service name, and one custom private DNS mapping for the broker hostname:</p>\n<div class=\"wrapper_EBtA\" style=\"height:188.5px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-json\"><code class=\"language-json\">{\n  \"description\": \"MSK broker 1\",\n  \"type\": \"VPC_ENDPOINT_SERVICE\",\n  \"vpcEndpointServiceName\": \"com.amazonaws.vpce.eu-central-1.vpce-svc-0123456789abcdef0\",\n  \"customPrivateDnsMappings\": [\n    {\n      \"privateDnsName\": \"b-1.example-cluster.abcde.c2.kafka.eu-central-1.amazonaws.com\"\n    }\n  ]\n}\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>For Terraform, use <a href=\"https://registry.terraform.io/providers/ClickHouse/clickhouse/latest/docs/resources/clickpipes_reverse_private_endpoint\" target=\"_blank\" rel=\"noopener noreferrer\"><code>clickhouse_clickpipes_reverse_private_endpoint</code></a> to create the endpoint and <a href=\"https://registry.terraform.io/providers/ClickHouse/clickhouse/latest/docs/resources/clickpipes_reverse_private_endpoint_custom_private_dns\" target=\"_blank\" rel=\"noopener noreferrer\"><code>clickhouse_clickpipes_reverse_private_endpoint_custom_private_dns</code></a> to configure the broker hostname mapping.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-the-kafka-clickpipe\">Create the Kafka ClickPipe<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#create-the-kafka-clickpipe\" class=\"hash-link\" aria-label=\"Direct link to Create the Kafka ClickPipe\" title=\"Direct link to Create the Kafka ClickPipe\">​</a></h2>\n<p>Follow the <a href=\"https://clickhouse.com/docs/integrations/clickpipes/aws-privatelink#creating-clickpipe\">ClickPipes AWS PrivateLink guide</a>\nto create the ClickPipe with reverse private endpoints.</p>\n<p>When creating the Kafka ClickPipe:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Use the original MSK broker hostnames as the broker list, including the port used by your MSK listener and authentication method.</li>\n<li class=\"custom-li\">Do not use the MSK bootstrap broker string.</li>\n<li class=\"custom-li\">Select or attach all broker-specific reverse private endpoints.</li>\n<li class=\"custom-li\">Configure the authentication method required by your MSK cluster, such as IAM role authentication.</li>\n</ul>\n<p>The port depends on your MSK listener. For example, IAM authentication commonly uses <code>9098</code>, SASL/SCRAM commonly uses <code>9096</code>, and TLS commonly uses <code>9094</code>. Confirm the correct port from your MSK cluster configuration or bootstrap broker output.</p>\n<p>Example broker list:</p>\n<div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">b-1.example-cluster.abcde.c2.kafka.eu-central-1.amazonaws.com:9098,b-2.example-cluster.abcde.c2.kafka.eu-central-1.amazonaws.com:9098,b-3.example-cluster.abcde.c2.kafka.eu-central-1.amazonaws.com:9098\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>Each hostname in the broker list must match the <code>Custom private DNS name</code> configured on one of the RPEs.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"terraform-automation\">Terraform automation<a href=\"https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster#terraform-automation\" class=\"hash-link\" aria-label=\"Direct link to Terraform automation\" title=\"Direct link to Terraform automation\">​</a></h2>\n<p>The <a href=\"https://github.com/ClickHouse/clickpipes-terraform-modules/tree/main/modules/aws-msk-vpc-endpoint-service\" target=\"_blank\" rel=\"noopener noreferrer\">aws-msk-vpc-endpoint-service Terraform module</a> shows how to automate this setup. It creates broker-specific NLBs, target groups, VPC endpoint services, ClickPipes RPEs, and custom private DNS mappings.</p>\n<p>The Terraform module can manage multiple custom private DNS mappings per RPE, including both bootstrap and broker hostnames. This article focuses on the manual ClickPipes UI workflow, where each RPE uses one broker hostname as its custom private DNS name.</p><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2026-07-09T00:00:00.000Z\">Jul 9, 2026</time> · <!-- -->6 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/aws-privatelink-vpc-endpoint-service-for-msk-cluster",
            "title": "AWS PrivateLink VPC endpoint service for MSK cluster",
            "summary": "Setup steps to expose an MSK cluster to ClickPipes through AWS PrivateLink VPC endpoint services.",
            "date_modified": "2026-07-09T00:00:00.000Z",
            "tags": [
                "Security and Authentication",
                "Managing Cloud"
            ]
        },
        {
            "id": "https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes",
            "content_html": "How to connect ClickPipes to an existing Confluent Cloud Kafka cluster over AWS PrivateLink or GCP Private Service Connect.<!-- -->\n<!-- -->\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"overview\">Overview<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#overview\" class=\"hash-link\" aria-label=\"Direct link to Overview\" title=\"Direct link to Overview\">​</a></h2>\n<p>This guide explains how to connect ClickPipes to an existing Confluent Cloud Kafka cluster through private networking.</p>\n<p>Use this guide for:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Confluent Cloud Dedicated clusters with AWS PrivateLink or GCP Private Service Connect (PSC)</li>\n<li class=\"custom-li\">Confluent Cloud serverless clusters with AWS PrivateLink or GCP Private Service Connect (PSC)</li>\n</ul>\n<p>This guide assumes the Confluent Cloud Kafka cluster, topic, and Kafka credentials already exist.\nIt focuses on private connectivity and ClickPipes reverse private endpoints (RPEs).</p>\n<div class=\"theme-admonition theme-admonition-warning alert alert--warning admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Note</div><p>You cannot currently complete this Confluent Cloud setup with only the ClickHouse Cloud console.</p><p>Confluent Cloud requires multiple custom private DNS mappings for a single reverse private endpoint.\nThe ClickHouse Cloud console currently supports only one custom private DNS name per RPE.</p><p>Use the <a href=\"https://clickhouse.com/docs/cloud/manage/api/swagger#tag/ClickPipes/operation/clickPipeReversePrivateEndpointCreate\" target=\"_blank\" rel=\"noopener noreferrer\">ClickHouse Cloud OpenAPI</a>\nor Terraform to create or update RPE custom DNS mappings.</p></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-the-setup-works\">How the setup works<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#how-the-setup-works\" class=\"hash-link\" aria-label=\"Direct link to How the setup works\" title=\"Direct link to How the setup works\">​</a></h2>\n<p>Kafka clients connect to a bootstrap hostname, fetch metadata, and then connect to the broker hostnames advertised by Confluent Cloud.\nFor private Confluent Cloud networking, those hostnames must resolve inside the ClickPipes network to the private endpoint created by ClickPipes.</p>\n<p>ClickPipes uses:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">A reverse private endpoint to create the cloud-provider private network connection.</li>\n<li class=\"custom-li\">Custom private DNS mappings on that RPE so Confluent bootstrap and broker hostnames resolve privately.</li>\n<li class=\"custom-li\">The Confluent Cloud Kafka bootstrap endpoint as the broker address in the ClickPipe.</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"requirements\">Requirements<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#requirements\" class=\"hash-link\" aria-label=\"Direct link to Requirements\" title=\"Direct link to Requirements\">​</a></h2>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">A ClickHouse Cloud service where you will create the Kafka ClickPipe.</li>\n<li class=\"custom-li\">A Confluent Cloud Kafka cluster in the same cloud and region you want to connect to privately.</li>\n<li class=\"custom-li\">A Kafka API key or service account credentials that ClickPipes can use.</li>\n<li class=\"custom-li\">For AWS PrivateLink, <a href=\"https://clickhouse.com/docs/integrations/clickpipes/aws-privatelink#custom-private-dns\">ClickPipes custom private DNS</a> is in Private Preview; contact ClickHouse support if it is not enabled for your service.</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"dedicated-clusters\">Dedicated clusters<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#dedicated-clusters\" class=\"hash-link\" aria-label=\"Direct link to Dedicated clusters\" title=\"Direct link to Dedicated clusters\">​</a></h2>\n<p>Use this section for Confluent Cloud Dedicated clusters connected through a Confluent private network.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"collect-dedicated-network-details\">Collect Confluent Cloud network details<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#collect-dedicated-network-details\" class=\"hash-link\" aria-label=\"Direct link to Collect Confluent Cloud network details\" title=\"Direct link to Collect Confluent Cloud network details\">​</a></h3>\n<p>From the Confluent Cloud network attached to your Dedicated cluster, collect:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">The private DNS domain, shown below as <code>{dns_domain}</code>.</li>\n<li class=\"custom-li\">The cloud-provider zones used by the Confluent network.</li>\n<li class=\"custom-li\">For AWS, the Confluent PrivateLink endpoint service name.</li>\n<li class=\"custom-li\">For GCP, the PSC service attachment for each Confluent network zone.</li>\n</ul>\n<p>The zone values are part of the DNS names that ClickPipes must resolve.\nFor AWS they are availability zone IDs such as <code>use1-az1</code>.\nFor GCP they are zones such as <code>us-central1-a</code>.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"authorize-clickpipes-dedicated\">Authorize ClickPipes in Confluent Cloud<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#authorize-clickpipes-dedicated\" class=\"hash-link\" aria-label=\"Direct link to Authorize ClickPipes in Confluent Cloud\" title=\"Direct link to Authorize ClickPipes in Confluent Cloud\">​</a></h3>\n<p>Add a PrivateLink or PSC access entry in Confluent Cloud for the ClickPipes consumer identity:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">AWS: allow ClickPipes AWS account ID <code>072088201116</code>.</li>\n<li class=\"custom-li\">GCP: allow the ClickPipes GCP project <code>clickpipes-production</code>.</li>\n</ul>\n<p>This authorization is required before the ClickPipes RPE can connect to the Confluent Cloud private network.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-dedicated-rpes\">Create the ClickPipes RPEs<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#create-dedicated-rpes\" class=\"hash-link\" aria-label=\"Direct link to Create the ClickPipes RPEs\" title=\"Direct link to Create the ClickPipes RPEs\">​</a></h3>\n<p>Create the RPEs with <a href=\"https://clickhouse.com/docs/cloud/manage/api/swagger#tag/ClickPipes/operation/clickPipeReversePrivateEndpointCreate\" target=\"_blank\" rel=\"noopener noreferrer\">OpenAPI</a>\nor Terraform.</p>\n<p>With OpenAPI, use <code>customPrivateDnsMappings</code> in the create request.\nIf you update an existing RPE with the <a href=\"https://clickhouse.com/docs/cloud/manage/api/swagger#tag/ClickPipes/operation/clickPipeReversePrivateEndpointUpdate\" target=\"_blank\" rel=\"noopener noreferrer\">update operation</a>,\nremember that <code>customPrivateDnsMappings</code> is a full replacement list.</p>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"aws-dedicated\">AWS Dedicated<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#aws-dedicated\" class=\"hash-link\" aria-label=\"Direct link to AWS Dedicated\" title=\"Direct link to AWS Dedicated\">​</a></h4>\n<p>Create one <code>VPC_ENDPOINT_SERVICE</code> RPE that points to the Confluent Cloud PrivateLink endpoint service.</p>\n<p>Add all of these custom private DNS mappings to the same RPE:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><code>*.{dns_domain}</code></li>\n<li class=\"custom-li\"><code>*.{zone}.{dns_domain}</code> for each Confluent network zone</li>\n</ul>\n<p>Example OpenAPI request body:</p>\n<div class=\"wrapper_EBtA\" style=\"height:358.15000000000003px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-json\"><code class=\"language-json\">{\n  \"description\": \"Confluent Cloud Dedicated AWS PrivateLink\",\n  \"type\": \"VPC_ENDPOINT_SERVICE\",\n  \"vpcEndpointServiceName\": \"com.amazonaws.vpce.us-east-1.vpce-svc-0123456789abcdef0\",\n  \"customPrivateDnsMappings\": [\n    {\n      \"privateDnsName\": \"*.abcde12345.us-east-1.aws.confluent.cloud\"\n    },\n    {\n      \"privateDnsName\": \"*.use1-az1.abcde12345.us-east-1.aws.confluent.cloud\"\n    },\n    {\n      \"privateDnsName\": \"*.use1-az2.abcde12345.us-east-1.aws.confluent.cloud\"\n    },\n    {\n      \"privateDnsName\": \"*.use1-az3.abcde12345.us-east-1.aws.confluent.cloud\"\n    }\n  ]\n}\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"gcp-dedicated\">GCP Dedicated<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#gcp-dedicated\" class=\"hash-link\" aria-label=\"Direct link to GCP Dedicated\" title=\"Direct link to GCP Dedicated\">​</a></h4>\n<p>Create one <code>GCP_PSC_SERVICE_ATTACHMENT</code> RPE per Confluent Cloud zone.\nEach RPE points to that zone's PSC service attachment.</p>\n<p>Add <code>*.{zone}.{dns_domain}</code> to the matching zone RPE.\nAdd <code>*.{dns_domain}</code> to one of the RPEs only.</p>\n<p>Example first-zone OpenAPI request body:</p>\n<div class=\"wrapper_EBtA\" style=\"height:245.05px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-json\"><code class=\"language-json\">{\n  \"description\": \"Confluent Cloud Dedicated PSC us-central1-a\",\n  \"type\": \"GCP_PSC_SERVICE_ATTACHMENT\",\n  \"gcpServiceAttachment\": \"projects/confluent-prod/regions/us-central1/serviceAttachments/confluent-us-central1-a\",\n  \"customPrivateDnsMappings\": [\n    {\n      \"privateDnsName\": \"*.us-central1-a.abcde12345.us-central1.gcp.confluent.cloud\"\n    },\n    {\n      \"privateDnsName\": \"*.abcde12345.us-central1.gcp.confluent.cloud\"\n    }\n  ]\n}\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>Example additional-zone OpenAPI request body:</p>\n<div class=\"wrapper_EBtA\" style=\"height:188.5px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-json\"><code class=\"language-json\">{\n  \"description\": \"Confluent Cloud Dedicated PSC us-central1-b\",\n  \"type\": \"GCP_PSC_SERVICE_ATTACHMENT\",\n  \"gcpServiceAttachment\": \"projects/confluent-prod/regions/us-central1/serviceAttachments/confluent-us-central1-b\",\n  \"customPrivateDnsMappings\": [\n    {\n      \"privateDnsName\": \"*.us-central1-b.abcde12345.us-central1.gcp.confluent.cloud\"\n    }\n  ]\n}\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"serverless-clusters\">Serverless clusters<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#serverless-clusters\" class=\"hash-link\" aria-label=\"Direct link to Serverless clusters\" title=\"Direct link to Serverless clusters\">​</a></h2>\n<p>Use this section for Confluent Cloud serverless clusters that use Confluent private ingress gateways and access points.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-ingress-gateway\">Create a Confluent ingress private connectivity gateway<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#create-ingress-gateway\" class=\"hash-link\" aria-label=\"Direct link to Create a Confluent ingress private connectivity gateway\" title=\"Direct link to Create a Confluent ingress private connectivity gateway\">​</a></h3>\n<p>Create the private ingress gateway in Confluent Cloud for the same cloud and region as the Confluent Kafka cluster:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">AWS: create an ingress PrivateLink gateway.</li>\n<li class=\"custom-li\">GCP: create an ingress Private Service Connect gateway.</li>\n</ul>\n<p>Record the gateway target that ClickPipes must connect to:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">AWS: the gateway VPC endpoint service name.</li>\n<li class=\"custom-li\">GCP: the gateway PSC service attachment.</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-serverless-rpe\">Create the ClickPipes RPE<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#create-serverless-rpe\" class=\"hash-link\" aria-label=\"Direct link to Create the ClickPipes RPE\" title=\"Direct link to Create the ClickPipes RPE\">​</a></h3>\n<p>Create one ClickPipes RPE for the Confluent ingress gateway.</p>\n<p>For AWS, create a <code>VPC_ENDPOINT_SERVICE</code> RPE:</p>\n<div class=\"wrapper_EBtA\" style=\"height:94.25px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-json\"><code class=\"language-json\">{\n  \"description\": \"Confluent Cloud ingress AWS PrivateLink\",\n  \"type\": \"VPC_ENDPOINT_SERVICE\",\n  \"vpcEndpointServiceName\": \"com.amazonaws.vpce.us-east-1.vpce-svc-0123456789abcdef0\"\n}\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>For GCP, create a <code>GCP_PSC_SERVICE_ATTACHMENT</code> RPE:</p>\n<div class=\"wrapper_EBtA\" style=\"height:94.25px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-json\"><code class=\"language-json\">{\n  \"description\": \"Confluent Cloud ingress PSC\",\n  \"type\": \"GCP_PSC_SERVICE_ATTACHMENT\",\n  \"gcpServiceAttachment\": \"projects/confluent-prod/regions/us-central1/serviceAttachments/confluent-ingress\"\n}\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>Wait until the RPE response includes an <code>endpointId</code>.\nYou need this ID to create the Confluent access point.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-access-point\">Create the Confluent access point<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#create-access-point\" class=\"hash-link\" aria-label=\"Direct link to Create the Confluent access point\" title=\"Direct link to Create the Confluent access point\">​</a></h3>\n<p>Create a Confluent Cloud ingress access point for the ClickPipes endpoint:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">AWS: use the ClickPipes RPE <code>endpointId</code> as the VPC endpoint ID.</li>\n<li class=\"custom-li\">GCP: use the ClickPipes RPE <code>endpointId</code> as the PSC connection ID.</li>\n</ul>\n<p>After the access point is created, collect:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">The access point DNS domain, shown below as <code>{access_point_dns_domain}</code>.</li>\n<li class=\"custom-li\">The bootstrap endpoint for the access point.</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"patch-serverless-rpe-dns\">Patch the RPE custom DNS mappings<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#patch-serverless-rpe-dns\" class=\"hash-link\" aria-label=\"Direct link to Patch the RPE custom DNS mappings\" title=\"Direct link to Patch the RPE custom DNS mappings\">​</a></h3>\n<p>Patch the ClickPipes RPE with both custom private DNS mappings:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><code>*.{region}.{cloud}.accesspoint.glb.confluent.cloud</code></li>\n<li class=\"custom-li\"><code>*.{access_point_dns_domain}</code></li>\n</ul>\n<p>Example OpenAPI update request body:</p>\n<div class=\"wrapper_EBtA\" style=\"height:188.5px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-json\"><code class=\"language-json\">{\n  \"customPrivateDnsMappings\": [\n    {\n      \"privateDnsName\": \"*.us-east-1.aws.accesspoint.glb.confluent.cloud\"\n    },\n    {\n      \"privateDnsName\": \"*.abcd1234.us-east-1.aws.confluent.cloud\"\n    }\n  ]\n}\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<div class=\"theme-admonition theme-admonition-note alert alert--secondary admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Note</div><p>The <code>*.{region}.{cloud}.accesspoint.glb.confluent.cloud</code> mapping is broad and is unique per cloud and region.\nBecause custom private DNS names must be unique across all RPEs in a ClickHouse service,\nyou cannot currently create multiple Confluent ingress access points in the same cloud and region for the same ClickHouse service.</p></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"terraform-setup\">Terraform setup<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#terraform-setup\" class=\"hash-link\" aria-label=\"Direct link to Terraform setup\" title=\"Direct link to Terraform setup\">​</a></h2>\n<p>With Terraform, use:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><a href=\"https://registry.terraform.io/providers/ClickHouse/clickhouse/latest/docs/resources/clickpipes_reverse_private_endpoint\" target=\"_blank\" rel=\"noopener noreferrer\"><code>clickhouse_clickpipes_reverse_private_endpoint</code></a> to create RPEs.</li>\n<li class=\"custom-li\"><a href=\"https://registry.terraform.io/providers/ClickHouse/clickhouse/latest/docs/resources/clickpipes_reverse_private_endpoint_custom_private_dns\" target=\"_blank\" rel=\"noopener noreferrer\"><code>clickhouse_clickpipes_reverse_private_endpoint_custom_private_dns</code></a> to manage the full set of custom private DNS mappings for each RPE.</li>\n</ul>\n<p>The ClickPipes Terraform modules also provide complete Confluent examples:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><a href=\"https://github.com/ClickHouse/clickpipes-terraform-modules/tree/main/modules/confluent-dedicated\" target=\"_blank\" rel=\"noopener noreferrer\"><code>confluent-dedicated</code></a> for Confluent Cloud Dedicated private networking.</li>\n<li class=\"custom-li\"><a href=\"https://github.com/ClickHouse/clickpipes-terraform-modules/tree/main/modules/confluent-serverless\" target=\"_blank\" rel=\"noopener noreferrer\"><code>confluent-serverless</code></a> for Confluent Cloud serverless private ingress.</li>\n</ul>\n<p>Use the same DNS mapping patterns from this guide.\nIn Terraform, field names use snake_case, for example <code>vpc_endpoint_service_name</code>, <code>gcp_service_attachment</code>, and <code>private_dns_name</code>.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-the-kafka-clickpipe\">Create the Kafka ClickPipe<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#create-the-kafka-clickpipe\" class=\"hash-link\" aria-label=\"Direct link to Create the Kafka ClickPipe\" title=\"Direct link to Create the Kafka ClickPipe\">​</a></h2>\n<p>After the RPEs are ready:</p>\n<ol class=\"custom-ol\">\n<li class=\"custom-li\">Create or edit the Kafka ClickPipe.</li>\n<li class=\"custom-li\">Use the Confluent Cloud bootstrap endpoint as the broker address, without the <code>SASL_SSL://</code> prefix.</li>\n<li class=\"custom-li\">Select the RPEs created for the Confluent Cloud private connection.</li>\n<li class=\"custom-li\">Use <code>PLAIN</code> authentication with the Confluent Cloud Kafka API key and secret.</li>\n<li class=\"custom-li\">Configure topics, consumer group, data format, and destination table as usual.</li>\n</ol>\n<p>If you use a Confluent Schema Registry over private networking, configure the schema registry URL in the ClickPipe and ensure its hostname resolves through the same RPE custom private DNS setup.\nFor more details, see <a href=\"https://clickhouse.com/docs/integrations/clickpipes/kafka/schema-registries\">Schema registries for Kafka ClickPipe</a>.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"troubleshooting\">Troubleshooting<a href=\"https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes#troubleshooting\" class=\"hash-link\" aria-label=\"Direct link to Troubleshooting\" title=\"Direct link to Troubleshooting\">​</a></h2>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">If the RPE remains pending, verify the Confluent Cloud private networking authorization and accept or complete the connection in Confluent Cloud.</li>\n<li class=\"custom-li\">If the ClickPipe connectivity check fails, verify that every bootstrap and broker hostname returned by Confluent Cloud matches one of the custom private DNS mappings.</li>\n<li class=\"custom-li\">If you patch custom DNS mappings, include the full desired list. The update replaces the existing list.</li>\n<li class=\"custom-li\">If a DNS mapping is rejected as duplicate, check all RPEs in the same ClickHouse service for overlapping exact or wildcard names.</li>\n</ul><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2026-07-09T00:00:00.000Z\">Jul 9, 2026</time> · <!-- -->7 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/confluent-cloud-private-connectivity-for-clickpipes",
            "title": "Confluent Cloud private connectivity for ClickPipes",
            "summary": "How to connect ClickPipes to an existing Confluent Cloud Kafka cluster over AWS PrivateLink or GCP Private Service Connect.",
            "date_modified": "2026-07-09T00:00:00.000Z",
            "tags": [
                "Security and Authentication",
                "Managing Cloud"
            ]
        },
        {
            "id": "https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts",
            "content_html": "How to load data atomically and keep multiple tables consistent in ClickHouse Cloud without multi-statement transactions, using staging tables and partition-level operations.<!-- -->\n<!-- -->\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"problem\">Problem<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#problem\" class=\"hash-link\" aria-label=\"Direct link to Problem\" title=\"Direct link to Problem\">​</a></h2>\n<p>ClickHouse Cloud does not support multi-statement transactions in the traditional RDBMS sense.\nThis creates two common challenges:</p>\n<ol class=\"custom-ol\">\n<li class=\"custom-li\">Single-table atomicity for bulk loads: A common approach is to insert into temporary partial keys, then copy records to the actual key and delete the temporary records. This approach performs poorly — particularly the delete step, which can consume over 90% of the total operation time.</li>\n<li class=\"custom-li\">Multi-table consistency: When a pipeline loads Table A successfully but fails on Table B, Table A is already committed and cannot be rolled back. Analysts querying across both tables see data that is out of sync.</li>\n</ol>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"background\">Background<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#background\" class=\"hash-link\" aria-label=\"Direct link to Background\" title=\"Direct link to Background\">​</a></h2>\n<p>ClickHouse guarantees atomicity at the single-insert, single-partition level: if an <code>INSERT</code> succeeds, all rows in that block are visible; if it fails, none are. However, there is no built-in mechanism to atomically commit data across multiple inserts or multiple tables.</p>\n<p>The partition manipulation commands (<a href=\"https://clickhouse.com/docs/sql-reference/statements/alter/partition#move-partition-to-table\"><code>MOVE PARTITION TO TABLE</code></a>, <a href=\"https://clickhouse.com/docs/sql-reference/statements/alter/partition#replace-partition\"><code>REPLACE PARTITION</code></a>, <a href=\"https://clickhouse.com/docs/sql-reference/statements/alter/partition#attach-partition-from\"><code>ATTACH PARTITION FROM</code></a>) operate at the metadata level when the source and destination tables share the same storage policy.</p>\n<p>This means they execute almost instantaneously regardless of data size, making them ideal building blocks for atomic-swap patterns.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"recommended-solution\">Recommended solution<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#recommended-solution\" class=\"hash-link\" aria-label=\"Direct link to Recommended solution\" title=\"Direct link to Recommended solution\">​</a></h2>\n<p>Instead of inserting directly into production tables and attempting to clean up on failure, use dedicated staging tables as a landing zone. After validating the data, use partition-level operations to atomically promote the data into production.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-by-step\">Step-by-step for single-table atomicity<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#step-by-step\" class=\"hash-link\" aria-label=\"Direct link to Step-by-step for single-table atomicity\" title=\"Direct link to Step-by-step for single-table atomicity\">​</a></h2>\n<div class=\"sc-xro1ly-0 QlHdI vertical-stepper \"><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-0\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Create a staging table</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-staging-table\">Create a staging table<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#create-staging-table\" class=\"hash-link\" aria-label=\"Direct link to Create a staging table\" title=\"Direct link to Create a staging table\">​</a></h3><p>Use the same schema, partition key, <code>ORDER BY</code>, and storage policy as the production table.</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">CREATE TABLE my_table_staging AS my_table_prod;\n</code></pre></div><div class=\"activity_PoTP\"></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-1\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Insert data into the staging table</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"insert-into-staging\">Insert data into the staging table<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#insert-into-staging\" class=\"hash-link\" aria-label=\"Direct link to Insert data into the staging table\" title=\"Direct link to Insert data into the staging table\">​</a></h3><p>Run your insert against the staging table instead of the production table.</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">INSERT INTO my_table_staging SELECT ... FROM source;\n</code></pre></div><div class=\"activity_PoTP\"></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-2\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">If the insert fails, truncate and retry</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"on-failure\">If the insert fails, truncate and retry<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#on-failure\" class=\"hash-link\" aria-label=\"Direct link to If the insert fails, truncate and retry\" title=\"Direct link to If the insert fails, truncate and retry\">​</a></h3><p>Truncate the staging table and run the load again. No production data has been affected.</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">TRUNCATE TABLE my_table_staging;\n</code></pre></div><div class=\"activity_PoTP\"></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-3\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">If the insert succeeds, move the partitions to production</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"on-success\">If the insert succeeds, move the partitions to production<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#on-success\" class=\"hash-link\" aria-label=\"Direct link to If the insert succeeds, move the partitions to production\" title=\"Direct link to If the insert succeeds, move the partitions to production\">​</a></h3><p>To move the data into production and remove it from the staging table, use <code>MOVE PARTITION</code>.</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">ALTER TABLE my_table_staging MOVE PARTITION &lt;partition_expr&gt; TO TABLE my_table_prod;\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>Alternatively, copy the data into an existing partition in production with <code>ATTACH PARTITION</code>.</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">ALTER TABLE my_table_prod ATTACH PARTITION tuple() FROM my_table_staging;\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>Both operations are metadata-level changes on the same storage policy and complete almost instantaneously.</p></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-4\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Clean up the staging table</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"clean-up\">Clean up the staging table<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#clean-up\" class=\"hash-link\" aria-label=\"Direct link to Clean up the staging table\" title=\"Direct link to Clean up the staging table\">​</a></h3><p>Once all partitions have been moved, truncate the staging table to leave it empty for the next load.</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">TRUNCATE TABLE my_table_staging;\n</code></pre></div><div class=\"activity_PoTP\"></div></div></div></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"multi-table-consistency\">Multi-table consistency<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#multi-table-consistency\" class=\"hash-link\" aria-label=\"Direct link to Multi-table consistency\" title=\"Direct link to Multi-table consistency\">​</a></h2>\n<p>The same pattern solves pipelines where two or more tables must all be loaded before any of them becomes visible to analysts. Load each table's data into its own staging table and validate all of them, then run the partition moves together. Because each move is a near-instant metadata operation, the window in which the tables disagree shrinks from the duration of the full load to the time it takes to swap partitions.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"requirements-and-constraints\">Requirements and constraints<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#requirements-and-constraints\" class=\"hash-link\" aria-label=\"Direct link to Requirements and constraints\" title=\"Direct link to Requirements and constraints\">​</a></h2>\n<p>For <code>MOVE PARTITION TO TABLE</code>, <code>REPLACE PARTITION</code>, and <code>ATTACH PARTITION FROM</code>, the source and destination tables must have:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">The same column structure</li>\n<li class=\"custom-li\">The same partition key, <code>ORDER BY</code> key, and primary key</li>\n<li class=\"custom-li\">The same storage policy</li>\n<li class=\"custom-li\">The destination table must include all indices and projections from the source table</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"example\">Example<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#example\" class=\"hash-link\" aria-label=\"Direct link to Example\" title=\"Direct link to Example\">​</a></h2>\n<p>You can run the example below interactively using <a href=\"https://fiddle.clickhouse.com/7ef9ed84-ac14-4f2c-9ca5-d5913089769a\" target=\"_blank\" rel=\"noopener noreferrer\">fiddle</a>:</p>\n<div class=\"wrapper_EBtA\" style=\"height:622.0500000000001px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">CREATE TABLE prod\n(\n  uid Int16,\n  name String,\n  age Int16\n)\nENGINE=MergeTree\nORDER BY ();\n\nCREATE TABLE staging\n(\n  uid Int16,\n  name String,\n  age Int16\n)\nENGINE=MergeTree\nORDER BY ();\n\n-- Initial data\nINSERT INTO prod VALUES (123, 'John', 33);\nINSERT INTO prod VALUES (456, 'Ksenia', 48);\n-- Load data\nINSERT INTO staging VALUES (8811, 'Alice', 50);\nINSERT INTO staging VALUES (8812, 'Bob', 23);\n\n-- Validate import\nSELECT 'Staging count:', COUNT() FROM staging;\n-- Move partition\nALTER TABLE staging MOVE PARTITION tuple() TO TABLE prod; -- atomic op\n\n-- Check data\nSELECT 'Prod count:', COUNT() FROM prod;\nSELECT * FROM prod;\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"references\">References<a href=\"https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts#references\" class=\"hash-link\" aria-label=\"Direct link to References\" title=\"Direct link to References\">​</a></h2>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><a href=\"https://clickhouse.com/docs/sql-reference/statements/alter/partition\"><strong>Manipulating Partitions and Parts</strong></a></li>\n<li class=\"custom-li\">To read more about this strategy, see the blog post <a href=\"https://clickhouse.com/blog/supercharge-your-clickhouse-data-loads-part3\" target=\"_blank\" rel=\"noopener noreferrer\"><strong>Supercharging your large ClickHouse data loads - Part 3: Making a large data load resilient</strong></a>.</li>\n</ul><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2026-06-01T00:00:00.000Z\">Jun 1, 2026</time> · <!-- -->4 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/achieving-atomic-inserts",
            "title": "Achieving atomic inserts and multi-table consistency in ClickHouse Cloud",
            "summary": "How to load data atomically and keep multiple tables consistent in ClickHouse Cloud without multi-statement transactions, using staging tables and partition-level operations.",
            "date_modified": "2026-06-01T00:00:00.000Z",
            "tags": [
                "Data Ingestion",
                "Best Practices"
            ]
        },
        {
            "id": "https://clickhouse.com/docs/knowledgebase/runbook-json",
            "content_html": "Choose the right schema approach for JSON data in ClickHouse — typed columns, hybrid, native JSON, or String storage<!-- -->\n<!-- -->\n<div class=\"theme-admonition theme-admonition-note alert alert--secondary admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Note</div><p>The JSON column type is production-ready from ClickHouse 25.3+. Earlier versions are not recommended for production use.</p></div></div>\n<p>Your data arrives as JSON. ClickHouse gives you several ways to store it, from fully typed columns to a raw String. The right choice depends on how predictable your schema is and whether you need field-level queries.</p>\n<p><strong>Scope:</strong> This page covers schema design decisions for storing JSON data. It does not cover <a href=\"https://clickhouse.com/docs/interfaces/formats/JSON\">JSON input/output formats</a>, <a href=\"https://clickhouse.com/docs/sql-reference/functions/json-functions\">JSON functions</a>, or query syntax. For background on the JSON column type itself, see <a href=\"https://clickhouse.com/docs/best-practices/use-json-where-appropriate\">Use JSON where appropriate</a>.</p>\n<p><strong>Assumes:</strong> Familiarity with <a href=\"https://clickhouse.com/docs/sql-reference/statements/create/table\">ClickHouse table creation</a>, <a href=\"https://clickhouse.com/docs/engines/table-engines/mergetree-family/mergetree\">MergeTree</a> basics, and column type syntax.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"quick-decision\">Quick decision<a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#quick-decision\" class=\"hash-link\" aria-label=\"Direct link to Quick decision\" title=\"Direct link to Quick decision\">​</a></h2>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><strong>If</strong> every field has a known, stable type and the schema rarely changes\n<strong>→</strong> <a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#typed-columns\">Typed columns</a></li>\n<li class=\"custom-li\"><strong>If</strong> most fields are stable but some section is dynamic or unpredictable\n<strong>→</strong> <a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#hybrid\">Hybrid (typed + JSON)</a></li>\n<li class=\"custom-li\"><strong>If</strong> the entire structure is dynamic, with keys that appear and disappear across records\n<strong>→</strong> <a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#native-json\">Native JSON column</a></li>\n<li class=\"custom-li\"><strong>If</strong> the dynamic fields are key-value pairs with a consistent value type (e.g., string tags, numeric metrics)\n<strong>→</strong> <a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#when-map-fits-better\"><code>Map</code></a> instead of JSON</li>\n<li class=\"custom-li\"><strong>If</strong> you only store and retrieve the JSON blob without field-level queries\n<strong>→</strong> <a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#opaque-storage\">Opaque String storage</a></li>\n</ul>\n<div class=\"theme-admonition theme-admonition-note alert alert--secondary admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Note</div><p>Don't confuse the JSON <em>format</em> with the JSON <em>column type</em>. You can insert JSON-formatted data (via <code>JSONEachRow</code>, etc.) into typed columns without using the <code>JSON</code> column type at all. The decision here is about column types, not input formats.</p></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"approach-details\">Approach details<a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#approach-details\" class=\"hash-link\" aria-label=\"Direct link to Approach details\" title=\"Direct link to Approach details\">​</a></h2>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"typed-columns\">Typed columns<a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#typed-columns\" class=\"hash-link\" aria-label=\"Direct link to Typed columns\" title=\"Direct link to Typed columns\">​</a></h3>\n<p><strong>When to use:</strong> The JSON structure is fully known at design time. Fields and types don't change between records. Even complex nested structures (arrays of objects, nested maps) can be expressed with <a href=\"https://clickhouse.com/docs/sql-reference/data-types/array\"><code>Array</code></a>, <a href=\"https://clickhouse.com/docs/sql-reference/data-types/tuple\"><code>Tuple</code></a>, and <a href=\"https://clickhouse.com/docs/sql-reference/data-types/nested-data-structures/nested\"><code>Nested</code></a> types.</p>\n<p><strong>Trade-offs:</strong> Schema changes require <code>ALTER TABLE</code>. Unexpected fields are silently dropped on insert unless the schema is updated.</p>\n<details class=\"details_lb9f alert alert--info details_r1OI\" data-collapsed=\"true\"><summary>Setup, verification, and gotchas</summary><div><div class=\"collapsibleContent_i85q\"><p><strong>Setup</strong></p><div class=\"wrapper_EBtA\" style=\"height:207.35000000000002px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">CREATE TABLE events\n(\n    `timestamp` DateTime,\n    `service`   LowCardinality(String),\n    `level`     Enum8('DEBUG' = 1, 'INFO' = 2, 'WARN' = 3, 'ERROR' = 4),\n    `message`   String,\n    `host`      LowCardinality(String),\n    `duration_ms` UInt32\n)\nENGINE = MergeTree\nORDER BY (service, timestamp)\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p><strong>Verification</strong></p><div class=\"wrapper_EBtA\" style=\"height:150.8px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">-- Confirm column types match expectations\nDESCRIBE TABLE events FORMAT Vertical\n\n-- Insert and query to validate the schema handles your data\nINSERT INTO events FORMAT JSONEachRow\n{\"timestamp\":\"2025-03-19 10:00:00\",\"service\":\"api\",\"level\":\"INFO\",\"message\":\"request handled\",\"host\":\"node-1\",\"duration_ms\":42}\n\nSELECT service, level, duration_ms FROM events WHERE service = 'api'\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p><strong>Watch out for</strong></p><ul class=\"custom-ul\">\n<li class=\"custom-li\">If you insert JSON data with <code>JSONEachRow</code> and the JSON contains fields not in the schema, ClickHouse drops them silently by default. Set <a href=\"https://clickhouse.com/docs/operations/settings/formats#input_format_skip_unknown_fields\"><code>input_format_skip_unknown_fields</code></a> to <code>0</code> if you want errors instead.</li>\n</ul></div></div></details>\n<hr>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"hybrid\">Hybrid (typed columns + JSON)<a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#hybrid\" class=\"hash-link\" aria-label=\"Direct link to Hybrid (typed columns + JSON)\" title=\"Direct link to Hybrid (typed columns + JSON)\">​</a></h3>\n<p><strong>When to use:</strong> A core set of fields is stable (timestamps, IDs, status codes), but part of the payload is dynamic. Think user-defined attributes, tags, metadata, or extension fields that vary across records.</p>\n<p><strong>Trade-offs:</strong> Full performance on typed columns, flexibility on the JSON column. The JSON column still carries insert overhead and storage cost for its dynamic portion.</p>\n<details class=\"details_lb9f alert alert--info details_r1OI\" data-collapsed=\"true\"><summary>Setup, verification, and gotchas</summary><div><div class=\"collapsibleContent_i85q\"><p><strong>Setup</strong></p><div class=\"wrapper_EBtA\" style=\"height:320.45000000000005px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">CREATE TABLE events\n(\n    `timestamp`  DateTime,\n    `service`    LowCardinality(String),\n    `level`      Enum8('DEBUG' = 1, 'INFO' = 2, 'WARN' = 3, 'ERROR' = 4),\n    `message`    String,\n    `host`       LowCardinality(String),\n    `duration_ms` UInt32,\n    `attributes` JSON(\n        max_dynamic_paths = 256,\n        `http.status_code` UInt16,\n        `http.method` LowCardinality(String),\n        SKIP REGEXP 'debug\\..*'\n    )\n)\nENGINE = MergeTree\nORDER BY (service, timestamp)\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p><strong>Verification</strong></p><div class=\"wrapper_EBtA\" style=\"height:131.95000000000002px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">-- Insert sample data and inspect inferred paths\nINSERT INTO events FORMAT JSONEachRow\n{\"timestamp\":\"2025-03-19 10:00:00\",\"service\":\"api\",\"level\":\"INFO\",\"message\":\"request handled\",\"host\":\"node-1\",\"duration_ms\":42,\"attributes\":{\"http.status_code\":200,\"http.method\":\"GET\",\"user.region\":\"eu-west\",\"custom_tag\":\"abc\"}}\n\nSELECT JSONAllPathsWithTypes(attributes)\nFROM events\nFORMAT PrettyJSONEachRow\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p><strong>Watch out for</strong></p><ul class=\"custom-ul\">\n<li class=\"custom-li\">Use <a href=\"https://clickhouse.com/docs/sql-reference/data-types/newjson\">type hints</a> on JSON paths you know ahead of time. Hints bypass the discriminator column and store the path like a regular typed column, with the same performance and no overhead.</li>\n<li class=\"custom-li\">Use <code>SKIP</code> or <code>SKIP REGEXP</code> for paths you never query (debug metadata, internal tracing IDs) to save storage and reduce subcolumn count.</li>\n<li class=\"custom-li\">Set <code>max_dynamic_paths</code> proportional to the number of distinct paths you actually query. The default (1024) works for most cases. Lower it if your dynamic section is narrow.</li>\n<li class=\"custom-li\">Don't set <code>max_dynamic_paths</code> above 10,000. High values increase resource consumption and reduce efficiency.</li>\n</ul><div class=\"theme-admonition theme-admonition-note alert alert--secondary admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Dotted keys</div><p>Keys with dots (e.g., <code>http.status_code</code>) are treated as nested paths by default, so <code>{\"http.status_code\": 200}</code> is stored the same as <code>{\"http\": {\"status_code\": 200}}</code>. This is common with OTel attributes. Use type hints to control how dotted paths are stored, or enable <code>json_type_escape_dots_in_keys</code> (25.8+).</p></div></div></div></div></details>\n<hr>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"native-json\">Native JSON column<a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#native-json\" class=\"hash-link\" aria-label=\"Direct link to Native JSON column\" title=\"Direct link to Native JSON column\">​</a></h3>\n<p><strong>When to use:</strong> The structure is genuinely unpredictable, with keys that appear and disappear across records. User-generated schemas, plugin systems, or data-lake ingestion where you don't control the upstream schema.</p>\n<p><strong>Trade-offs:</strong> Slower inserts than typed columns. Slower full-object reads than String. Storage overhead from subcolumn management. Works well for field-level queries on specific paths.</p>\n<details class=\"details_lb9f alert alert--info details_r1OI\" data-collapsed=\"true\"><summary>Setup, verification, and gotchas</summary><div><div class=\"collapsibleContent_i85q\"><p><strong>Setup</strong></p><div class=\"wrapper_EBtA\" style=\"height:226.20000000000002px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">CREATE TABLE dynamic_events\n(\n    `id`   UInt64,\n    `ts`   DateTime DEFAULT now(),\n    `data` JSON(\n        max_dynamic_paths = 512,\n        `event_type` LowCardinality(String),\n        `version` UInt8\n    )\n)\nENGINE = MergeTree\nORDER BY (data.event_type, ts)\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>Use <a href=\"https://clickhouse.com/docs/interfaces/formats/JSONAsObject\"><code>JSONAsObject</code></a> format when inserting whole JSON documents into a JSON column. It treats each input line as a complete JSON object mapped to the column.</p><p><strong>Verification</strong></p><div class=\"wrapper_EBtA\" style=\"height:169.65px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">INSERT INTO dynamic_events (id, data) FORMAT JSONEachRow\n{\"id\": 1, \"data\": {\"event_type\": \"click\", \"version\": 2, \"page\": \"/home\", \"button_id\": \"cta-1\"}}\n{\"id\": 2, \"data\": {\"event_type\": \"purchase\", \"version\": 1, \"item_id\": \"SKU-99\", \"amount\": 49.99, \"currency\": \"USD\"}}\n\n-- Check which paths ClickHouse detected and their types\nSELECT JSONAllPathsWithTypes(data) FROM dynamic_events FORMAT PrettyJSONEachRow\n\n-- Query a specific path\nSELECT data.page FROM dynamic_events WHERE data.event_type = 'click'\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p><strong>Watch out for</strong></p><ul class=\"custom-ul\">\n<li class=\"custom-li\">Without type hints, ClickHouse infers types per-path from the first values it sees. If <code>score</code> arrives as <code>\"10\"</code> (string) in one record and <code>10</code> (integer) in another, the path gets a discriminator column and queries become slower. Add hints for paths with known types.</li>\n<li class=\"custom-li\">When path count exceeds <code>max_dynamic_paths</code>, overflow values move to a <a href=\"https://clickhouse.com/docs/sql-reference/data-types/newjson#shared-data-structure\">shared data structure</a> with reduced query performance. Monitor with <a href=\"https://clickhouse.com/docs/sql-reference/data-types/newjson#introspection-functions\"><code>JSONDynamicPaths()</code></a> and keep the limit below 10,000.</li>\n<li class=\"custom-li\">Each dynamic path supports up to <code>max_dynamic_types</code> (default 32) distinct data types. If a single path exceeds this, extra types fall back to shared variant storage. This rarely matters unless your data has highly inconsistent types for the same field.</li>\n</ul></div></div></details>\n<hr>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"opaque-storage\">Opaque String storage<a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#opaque-storage\" class=\"hash-link\" aria-label=\"Direct link to Opaque String storage\" title=\"Direct link to Opaque String storage\">​</a></h3>\n<p><strong>When to use:</strong> JSON documents are stored and retrieved whole, then passed through to an application, archived, or forwarded downstream. No field-level filtering or aggregation inside ClickHouse.</p>\n<p><strong>Trade-offs:</strong> Fastest inserts and simplest schema. No field-level queries without runtime parsing (<code>JSONExtract</code> family), which is slow at scale.</p>\n<details class=\"details_lb9f alert alert--info details_r1OI\" data-collapsed=\"true\"><summary>Setup, verification, and gotchas</summary><div><div class=\"collapsibleContent_i85q\"><p><strong>Setup</strong></p><div class=\"wrapper_EBtA\" style=\"height:150.8px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">CREATE TABLE raw_events\n(\n    `id`        UInt64,\n    `received`  DateTime DEFAULT now(),\n    `payload`   String\n)\nENGINE = MergeTree\nORDER BY (received)\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p><strong>Verification</strong></p><div class=\"wrapper_EBtA\" style=\"height:169.65px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">INSERT INTO raw_events (id, payload) VALUES\n(1, '{\"type\":\"click\",\"page\":\"/home\"}'),\n(2, '{\"type\":\"purchase\",\"item\":\"SKU-99\",\"amount\":49.99}')\n\n-- Confirm data round-trips intact\nSELECT payload FROM raw_events WHERE id = 1\n\n-- Verify you can still parse fields ad-hoc when needed\nSELECT JSONExtractString(payload, 'type') AS event_type FROM raw_events\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p><strong>Watch out for</strong></p><ul class=\"custom-ul\">\n<li class=\"custom-li\">If requirements change and you later need field-level queries, you'll need to create a new table with typed or JSON columns and backfill the data. If there's any chance you'll query individual fields, start with the <a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#hybrid\">hybrid approach</a> instead.</li>\n<li class=\"custom-li\"><code>JSONExtract</code> functions parse the string on every query. Acceptable for ad-hoc exploration, not for production dashboards or high-QPS workloads.</li>\n<li class=\"custom-li\">Consider compression codecs (<code>ZSTD</code>) on the String column if the JSON payloads are large — it compresses well.</li>\n</ul></div></div></details>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"comparison\">Comparison<a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#comparison\" class=\"hash-link\" aria-label=\"Direct link to Comparison\" title=\"Direct link to Comparison\">​</a></h2>\n<table><thead><tr><th>Dimension</th><th>Typed columns</th><th>Hybrid</th><th>Native JSON</th><th>String</th></tr></thead><tbody><tr><td><strong>Insert throughput</strong></td><td>Fastest</td><td>Fast</td><td>Moderate</td><td>Fastest</td></tr><tr><td><strong>Field-level queries</strong></td><td>Fastest</td><td>Fast (typed); good (hinted JSON)</td><td>Good (hinted); slower (dynamic)</td><td>Slow (runtime parsing)</td></tr><tr><td><strong>Full-object reads</strong></td><td>Fast</td><td>Moderate</td><td>Slow</td><td>Fastest</td></tr><tr><td><strong>Storage efficiency</strong></td><td>Best</td><td>Good</td><td>Moderate</td><td>Good (compresses well)</td></tr><tr><td><strong>Schema flexibility</strong></td><td>None (<code>ALTER TABLE</code>)</td><td>Partial (rigid core, flexible tail)</td><td>Full</td><td>Full</td></tr><tr><td><strong>Complexity</strong></td><td>Low</td><td>Medium</td><td>Medium–High</td><td>Low</td></tr></tbody></table>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"when-map-fits-better\">When Map fits better<a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#when-map-fits-better\" class=\"hash-link\" aria-label=\"Direct link to When Map fits better\" title=\"Direct link to When Map fits better\">​</a></h2>\n<p>If your dynamic fields are homogeneous key-value pairs — all values share the same type — <a href=\"https://clickhouse.com/docs/sql-reference/data-types/map\"><code>Map(String, T)</code></a> is simpler and more efficient than a JSON column. Common examples: string tags (<code>Map(String, String)</code>), numeric metrics (<code>Map(String, Float64)</code>), or feature flags (<code>Map(String, Bool)</code>).</p>\n<div class=\"wrapper_EBtA\" style=\"height:150.8px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">CREATE TABLE tagged_events\n(\n    `timestamp` DateTime,\n    `service`   LowCardinality(String),\n    `tags`      Map(String, String)  -- e.g., {\"env\": \"prod\", \"region\": \"us-east-1\", \"team\": \"platform\"}\n)\nENGINE = MergeTree\nORDER BY (service, timestamp)\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p><code>Map</code> supports key-level filtering (<code>tags['env'] = 'prod'</code>), is cheaper to store than JSON, and avoids the subcolumn overhead of the JSON type. Note that key lookups scan the map linearly by default — fine for small tag sets, but for maps with 100+ keys consider <a href=\"https://clickhouse.com/docs/sql-reference/data-types/map#bucketed-map-serialization\"><code>with_buckets</code> serialization</a>. Use JSON when values have mixed types or the structure has nesting — use <code>Map</code> when it's flat key-value pairs with a uniform value type.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"related-resources\">Related resources<a href=\"https://clickhouse.com/docs/knowledgebase/runbook-json#related-resources\" class=\"hash-link\" aria-label=\"Direct link to Related resources\" title=\"Direct link to Related resources\">​</a></h2>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><a href=\"https://clickhouse.com/docs/best-practices/use-json-where-appropriate\">Use JSON where appropriate</a> — when to use the JSON column type vs alternatives</li>\n<li class=\"custom-li\"><a href=\"https://clickhouse.com/docs/sql-reference/data-types/newjson\">JSON data type reference</a> — full syntax for type hints, SKIP, max_dynamic_paths, and introspection functions</li>\n<li class=\"custom-li\"><a href=\"https://clickhouse.com/docs/best-practices/select-data-types\">Selecting data types</a> — general type selection guidance</li>\n<li class=\"custom-li\"><a href=\"https://clickhouse.com/blog/a-new-powerful-json-data-type-for-clickhouse\" target=\"_blank\" rel=\"noopener noreferrer\">A New Powerful JSON Data Type for ClickHouse</a> — deep dive on the JSON type's storage architecture</li>\n<li class=\"custom-li\"><a href=\"https://clickhouse.com/docs/interfaces/formats/JSON\">JSON formats reference</a> — input/output formats for JSON data (JSONEachRow, JSONAsObject, etc.)</li>\n</ul><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2026-03-20T00:00:00.000Z\">Mar 20, 2026</time> · <!-- -->8 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/runbook-json",
            "title": "Runbook: JSON schema",
            "summary": "Choose the right schema approach for JSON data in ClickHouse — typed columns, hybrid, native JSON, or String storage",
            "date_modified": "2026-03-20T00:00:00.000Z",
            "tags": [
                "Runbooks",
                "Data Modelling"
            ]
        },
        {
            "id": "https://clickhouse.com/docs/knowledgebase/opt-out-core-dump-collection",
            "content_html": "This article shows you how you can opt out of crash report collection on ClickHouse Cloud<!-- -->\n<!-- -->\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"overview\">Overview<a href=\"https://clickhouse.com/docs/knowledgebase/opt-out-core-dump-collection#overview\" class=\"hash-link\" aria-label=\"Direct link to Overview\" title=\"Direct link to Overview\">​</a></h2>\n<p>Starting <strong>February 27, 2026</strong>, ClickHouse Cloud will begin collecting crash reports when server crashes occur. This is part of our commitment to improving service reliability for all our users.</p>\n<p>Crash report collection helps us:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Identify and understand what caused a crash</li>\n<li class=\"custom-li\">Fix issues more quickly</li>\n<li class=\"custom-li\">Continuously improve your service experience</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"whats-collected\">What's collected<a href=\"https://clickhouse.com/docs/knowledgebase/opt-out-core-dump-collection#whats-collected\" class=\"hash-link\" aria-label=\"Direct link to What's collected\" title=\"Direct link to What's collected\">​</a></h2>\n<p>When a crash occurs, crash reports capture a snapshot of the server's state at that moment. We want to be transparent that this may include sensitive information that was in memory, such as:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Connection credentials</li>\n<li class=\"custom-li\">Encryption keys</li>\n<li class=\"custom-li\">Query strings</li>\n<li class=\"custom-li\">Other data in memory at the time of the crash</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-we-protect-your-data\">How we protect your data<a href=\"https://clickhouse.com/docs/knowledgebase/opt-out-core-dump-collection#how-we-protect-your-data\" class=\"hash-link\" aria-label=\"Direct link to How we protect your data\" title=\"Direct link to How we protect your data\">​</a></h3>\n<p>We take the security of this data seriously:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><strong>Regional Storage</strong>: All data stays in the same region as your service</li>\n<li class=\"custom-li\"><strong>Enhanced Security</strong>: We apply additional protections to all collected data</li>\n<li class=\"custom-li\"><strong>Limited Retention</strong>: Data is kept only as long as needed for debugging, then securely deleted</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"services-automatically-excluded\">Services automatically excluded<a href=\"https://clickhouse.com/docs/knowledgebase/opt-out-core-dump-collection#services-automatically-excluded\" class=\"hash-link\" aria-label=\"Direct link to Services automatically excluded\" title=\"Direct link to Services automatically excluded\">​</a></h2>\n<p>Some services are automatically excluded from crash report collection:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Services with <strong>HIPAA</strong> compliance requirements</li>\n<li class=\"custom-li\">Services with <strong>PCI</strong> compliance requirements</li>\n<li class=\"custom-li\"><strong>BYOC</strong> (Bring Your Own Cloud) services</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-to-opt-out\">How to opt out<a href=\"https://clickhouse.com/docs/knowledgebase/opt-out-core-dump-collection#how-to-opt-out\" class=\"hash-link\" aria-label=\"Direct link to How to opt out\" title=\"Direct link to How to opt out\">​</a></h2>\n<p>If you'd prefer not to participate in crash report collection, you can easily opt out:</p>\n<ol class=\"custom-ol\">\n<li class=\"custom-li\">Go to the <a href=\"https://clickhouse.cloud/\" target=\"_blank\" rel=\"noopener noreferrer\">ClickHouse Cloud Console</a></li>\n<li class=\"custom-li\">Select your service</li>\n<li class=\"custom-li\">Click on the <strong>Settings</strong> tab</li>\n<li class=\"custom-li\">Scroll to find \"Share crash reports with ClickHouse\"</li>\n<li class=\"custom-li\">Toggle the option off to opt out</li>\n</ol>\n<div style=\"position:relative;margin-bottom:16px;margin-top:16px\"><div style=\"cursor:default\"><div style=\"background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAYAAAALpr0TAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA60lEQVR4nC2P227EIAxE8//fWLXdJu1uCrmwgAFfmIp0H0a25GN7Zoox5VKqnWfQ4wxaa9OUSEupqqoMAL33z2nzXlrOUFUwC1Q7YsxoozfYAM2wTJtzEuYZKUbEmNBaAxFdMrMLBLBMOWcptV6DTARmhvce3m84zsN6f4ExJmmsr8XhB3C7w/f6hXVbbcxEdJnMTNCBUipCeF6vKwtybWDRf4/jIgCGWT/eP/qx/PTqXE/3Ry/OdzlORXzCQpgHKCMW3W6I8wxeV9T746rinMF76L5fYQIzl5KJ3OOXUojUqFLNZShRIlPRtz8lfFtizxsR0QAAAABJRU5ErkJggg==&quot;);width:600px;margin:0 auto;display:block;box-shadow:none\"><svg style=\"width:100%;height:auto;max-width:100%;margin-bottom:-4px\" width=\"1024\" height=\"945\"></svg><noscript><img style=width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0 src=/docs/assets/ideal-img/crash-reports.8fc1a6a.48.png srcset=\"/docs/assets/ideal-img/crash-reports.8fc1a6a.48.png 48w,/docs/assets/ideal-img/crash-reports.331833a.300.png 300w,/docs/assets/ideal-img/crash-reports.4b82b6d.600.png 600w,/docs/assets/ideal-img/crash-reports.740f738.1024.png 1024w\" alt=\"Where to find the opt out option in Cloud UI\" width=1024 height=945></noscript></div></div></div>\n<div class=\"theme-admonition theme-admonition-note alert alert--secondary admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Note</div><p>In addition to the service setting, we also support configuring preferences for the organization. If the setting is disabled, i.e., opt out of the crash report collection, all existing and new services will be opted out automatically. If you set the preference to enabled i.e. opt in, you can selectively disable at the service level to override and opt out.</p></div></div>\n<div style=\"position:relative;margin-bottom:16px;margin-top:16px\"><div style=\"cursor:default\"><div style=\"background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAZElEQVR4nB3MOw6FIBQAUdbhQ37XoCJComBn3P+q5iUU05xiVEortWaM+SHieJ422veI9zMiBudm1HkefN9L7xcxCq3f3O2i9ca2CaUIIh4Vgh2nEBzL4qm1UErmyGmY1hPWav6SUywuE9uS4gAAAABJRU5ErkJggg==&quot;);width:600px;margin:0 auto;display:block;box-shadow:none\"><svg style=\"width:100%;height:auto;max-width:100%;margin-bottom:-4px\" width=\"1024\" height=\"276\"></svg><noscript><img style=width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0 src=/docs/assets/ideal-img/crash-reports-collection.51c00b7.48.png srcset=\"/docs/assets/ideal-img/crash-reports-collection.51c00b7.48.png 48w,/docs/assets/ideal-img/crash-reports-collection.eb0b069.300.png 300w,/docs/assets/ideal-img/crash-reports-collection.dd0d033.600.png 600w,/docs/assets/ideal-img/crash-reports-collection.51bf8c8.1024.png 1024w\" alt=\"Crash reports collection\" width=1024 height=276></noscript></div></div></div><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2026-02-04T00:00:00.000Z\">Feb 4, 2026</time> · <!-- -->2 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/opt-out-core-dump-collection",
            "title": "How to opt out of crash report collection",
            "summary": "This article shows you how you can opt out of crash report collection on ClickHouse Cloud",
            "date_modified": "2026-02-04T00:00:00.000Z",
            "tags": [
                "Managing Cloud"
            ]
        },
        {
            "id": "https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats",
            "content_html": "Learn about the different part types (Wide vs Compact) and storage formats (Full vs Packed) in ClickHouse, and how they affect performance.<!-- -->\n<!-- -->\n<br>\n<br>\n<p>ClickHouse uses two independent concepts for organizing data within parts:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><strong>Part types</strong> (Wide vs Compact): How column data is stored within a part</li>\n<li class=\"custom-li\"><strong>Storage formats</strong> (Full vs Packed): How the part's files are stored on disk</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"part-types-wide-vs-compact\">Part types: Wide vs Compact<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#part-types-wide-vs-compact\" class=\"hash-link\" aria-label=\"Direct link to Part types: Wide vs Compact\" title=\"Direct link to Part types: Wide vs Compact\">​</a></h2>\n<p>Part types determine how column data is organized within a data part.</p>\n<table><thead><tr><th>Type</th><th>Description</th><th>Best for</th></tr></thead><tbody><tr><td><strong>Wide</strong></td><td>Each column is stored in separate file(s), each with its own <a href=\"https://clickhouse.com/docs/concepts/glossary#mark-file\">marks file</a></td><td>Queries selecting a subset of columns</td></tr><tr><td><strong>Compact</strong></td><td>All columns are stored in a single file with a single <a href=\"https://clickhouse.com/docs/concepts/glossary#mark-file\">marks file</a></td><td>Ingestion performance and queries needing all columns</td></tr></tbody></table>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"when-each-type-is-used\">When each type is used<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#when-each-type-is-used\" class=\"hash-link\" aria-label=\"Direct link to When each type is used\" title=\"Direct link to When each type is used\">​</a></h3>\n<p>The part type is controlled by the following table settings:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\"><a href=\"https://clickhouse.com/docs/operations/settings/merge-tree-settings#min_bytes_for_wide_part\"><code>min_bytes_for_wide_part</code></a>: Minimum bytes for a part to use Wide format</li>\n<li class=\"custom-li\"><a href=\"https://clickhouse.com/docs/operations/settings/merge-tree-settings#min_rows_for_wide_part\"><code>min_rows_for_wide_part</code></a>: Minimum rows for a part to use Wide format</li>\n</ul>\n<p>If either the number of bytes or rows in a data part is less than the corresponding setting value, the part is stored in <code>Compact</code> format; otherwise, it uses <code>Wide</code> format.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"performance-considerations\">Performance considerations<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#performance-considerations\" class=\"hash-link\" aria-label=\"Direct link to Performance considerations\" title=\"Direct link to Performance considerations\">​</a></h3>\n<p><strong>Compact parts:</strong></p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Better ingestion performance</li>\n<li class=\"custom-li\">Optimal when queries need all columns</li>\n<li class=\"custom-li\">More efficient for small parts</li>\n</ul>\n<p><strong>Wide parts:</strong></p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">More efficient for queries selecting only a subset of columns</li>\n<li class=\"custom-li\">Better for large datasets with selective column access</li>\n</ul>\n<p>Compact-to-wide merges are slower than wide-to-wide merges because ClickHouse uses <a href=\"https://clickhouse.com/docs/merges#memory-optimized-merges\">vertical merge</a> algorithms for compact-to-wide conversions, while using horizontal algorithms for compact-to-compact merges. If you want to force the use of wide parts regardless of size, you can set <code>min_bytes_for_wide_part=0</code>.</p>\n<p>This can be useful in the following scenarios:</p>\n<ol class=\"custom-ol\">\n<li class=\"custom-li\">When you have tables with many columns (e.g. over 600 columns) and are making large inserts, setting this to 0 can help with performance</li>\n<li class=\"custom-li\">For optimizing memory usage of system tables like <code>system.metric_log</code> and <code>system.text_log</code> that are consuming excessive memory during merges</li>\n<li class=\"custom-li\">When dealing with tables that have high insert volumes and you want to optimize storage and merge behavior</li>\n<li class=\"custom-li\">When you want consistent part format behavior and don't want the overhead of format transitions based on size thresholds</li>\n</ol>\n<p>It's worth noting that while setting this to 0 can improve certain performance characteristics, it may generate more GET requests to S3 storage due to the wide parts format.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"limitations\">Limitations<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#limitations\" class=\"hash-link\" aria-label=\"Direct link to Limitations\" title=\"Direct link to Limitations\">​</a></h3>\n<p>Column size statistics are not calculated for compact parts, which can affect monitoring and optimization efforts. When querying <code>system.parts_columns</code>, compact parts show <code>0</code> for <code>column_data_compressed_bytes</code> and <code>column_data_uncompressed_bytes</code>. For more information see <a href=\"https://clickhouse.com/docs/knowledgebase/count-parts-by-type\">\"Find counts and sizes of wide or compact parts\"</a>.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"storage-formats-full-vs-packed-clickhouse-cloud\">Storage formats: Full vs Packed (ClickHouse Cloud)<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#storage-formats-full-vs-packed-clickhouse-cloud\" class=\"hash-link\" aria-label=\"Direct link to Storage formats: Full vs Packed (ClickHouse Cloud)\" title=\"Direct link to Storage formats: Full vs Packed (ClickHouse Cloud)\">​</a></h2>\n<p>Storage formats determine how a part's files are physically stored on disk.</p>\n<table><thead><tr><th>Format</th><th>Description</th><th>Availability</th></tr></thead><tbody><tr><td><strong>Full</strong></td><td>Each file is stored individually in the part directory</td><td>Open source and Cloud</td></tr><tr><td><strong>Packed</strong></td><td>All files are bundled into a single archive file</td><td>ClickHouse Cloud only</td></tr></tbody></table>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"full-storage\">Full storage<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#full-storage\" class=\"hash-link\" aria-label=\"Direct link to Full storage\" title=\"Direct link to Full storage\">​</a></h3>\n<p>In Full storage, each part consists of multiple separate files stored individually on disk. This is the default and only option in open source ClickHouse.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"packed-storage-clickhouse-cloud-only\">Packed storage (ClickHouse Cloud only)<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#packed-storage-clickhouse-cloud-only\" class=\"hash-link\" aria-label=\"Direct link to Packed storage (ClickHouse Cloud only)\" title=\"Direct link to Packed storage (ClickHouse Cloud only)\">​</a></h3>\n<p>In Packed storage, all part files are bundled into a single archive. This significantly reduces the number of file operations, which is critical for remote storage like S3 where each request has associated latency and cost.</p>\n<p><strong>Benefits of Packed storage:</strong></p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Fewer S3/object storage API calls</li>\n<li class=\"custom-li\">Reduced metadata pressure on the coordination service</li>\n<li class=\"custom-li\">Lower storage costs (full storage can be significantly more expensive)</li>\n<li class=\"custom-li\">Better performance for small parts on remote storage</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"when-packed-storage-is-used\">When Packed storage is used<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#when-packed-storage-is-used\" class=\"hash-link\" aria-label=\"Direct link to When Packed storage is used\" title=\"Direct link to When Packed storage is used\">​</a></h3>\n<p>In ClickHouse Cloud, a part uses Packed storage if <strong>any</strong> of these conditions are true:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Uncompressed bytes &lt; <a href=\"https://clickhouse.com/docs/operations/settings/merge-tree-settings#min_bytes_for_full_part_storage\"><code>min_bytes_for_full_part_storage</code></a></li>\n<li class=\"custom-li\">Row count &lt; <a href=\"https://clickhouse.com/docs/operations/settings/merge-tree-settings#min_rows_for_full_part_storage\"><code>min_rows_for_full_part_storage</code></a></li>\n<li class=\"custom-li\">Merge level &lt; <a href=\"https://clickhouse.com/docs/operations/settings/merge-tree-settings#min_level_for_full_part_storage\"><code>min_level_for_full_part_storage</code></a></li>\n</ul>\n<div class=\"theme-admonition theme-admonition-note alert alert--secondary admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Open source</div><p>While the settings <code>min_bytes_for_full_part_storage</code>, <code>min_rows_for_full_part_storage</code>, and <code>min_level_for_full_part_storage</code> are defined in open source ClickHouse, they have no effect because the Packed storage implementation is only available in ClickHouse Cloud.</p></div></div>\n<p>The <code>min_level_for_full_part_storage</code> setting can be used to optimize both performance and costs in ClickHouse Cloud environments, particularly for tables that receive continuous inserts.\nThe setting is available starting in ClickHouse version 25.10 and works in conjunction with <code>min_level_for_wide_part</code> to provide comprehensive control over part storage strategies.</p>\n<p>Changing it from the default value (0) could be considered in the following use cases:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">When tables receive regular data ingestion, initial parts will be merged away quickly, making it wasteful to store them in full part format initially</li>\n<li class=\"custom-li\">Setting this parameter prevents expensive S3 PUT requests during inserts. For example, one analysis showed that inserts creating full parts averaged 31.3 PUT requests per insert, while those creating only packed parts averaged just 2.22 PUT requests per insert</li>\n<li class=\"custom-li\">Insert operations become faster, especially for tables with many columns, since packed storage writes all data into one file rather than creating separate files for each column.</li>\n</ul>\n<p>Recommended configuration:</p>\n<p>Set <code>min_level_for_full_part_storage = 2</code> for cloud deployments.\nThis ensures that:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Level 0 parts (initial inserts) use packed storage</li>\n<li class=\"custom-li\">Level 1 parts continue using packed storage</li>\n<li class=\"custom-li\">Only parts at level 2 and above use full storage format</li>\n</ul>\n<div class=\"theme-admonition theme-admonition-tip alert alert--success admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Tip</div><p>Avoid this setting for tables that receive very large but infrequent writes where insufficient merges occur, as large initial writes may benefit from full part storage immediately.</p></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"combining-part-types-and-storage-formats\">Combining part types and storage formats<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#combining-part-types-and-storage-formats\" class=\"hash-link\" aria-label=\"Direct link to Combining part types and storage formats\" title=\"Direct link to Combining part types and storage formats\">​</a></h2>\n<p>These two concepts are orthogonal and you can have any combination, depending on if you are using ClickHouse Cloud or Open Source ClickHouse:</p>\n<table><thead><tr><th>Combination</th><th>Use case</th></tr></thead><tbody><tr><td>Wide + Full</td><td>Large parts on local storage (default in open source)</td></tr><tr><td>Wide + Packed</td><td>Large parts on cloud storage</td></tr><tr><td>Compact + Full</td><td>Small parts on local storage</td></tr><tr><td>Compact + Packed</td><td>Small parts on cloud storage</td></tr></tbody></table>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"querying-part-information\">Querying part information<a href=\"https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats#querying-part-information\" class=\"hash-link\" aria-label=\"Direct link to Querying part information\" title=\"Direct link to Querying part information\">​</a></h2>\n<p>You can inspect the part type and part storage type of existing parts using the <a href=\"https://clickhouse.com/docs/operations/system-tables/parts\"><code>system.parts</code></a> table:</p>\n<div class=\"wrapper_EBtA\" style=\"height:282.75px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">SELECT\n    part_type,\n    part_storage_type,\n    max(level),\n    count(),\n    formatReadableSize(max(data_uncompressed_bytes)),\n    formatReadableSize(min(data_uncompressed_bytes))\nFROM system.parts\nWHERE (database != 'system') AND active\nGROUP BY\n    1,\n    2\nORDER BY\n    1 ASC,\n    2 ASC\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>You will see something like this:</p>\n<div class=\"wrapper_EBtA\" style=\"height:113.10000000000001px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-response\"><code class=\"language-response\">   ┌─part_type─┬─part_storage_type─┬─max(level)─┬─count()─┬─formatReadab⋯sed_bytes))─┬─formatReadab⋯sed_bytes))─┐\n1. │ Compact   │ Full              │      12688 │    2456 │ 1023.87 MiB              │ 23.78 MiB                │\n2. │ Compact   │ Packed            │      97383 │   13748 │ 127.77 MiB               │ 1.00 B                   │\n3. │ Wide      │ Full              │       7642 │    2000 │ 1.38 TiB                 │ 30.18 MiB                │\n4. │ Wide      │ Packed            │         10 │     187 │ 110.01 MiB               │ 1.30 KiB                 │\n   └───────────┴───────────────────┴────────────┴─────────┴──────────────────────────┴──────────────────────────┘\n</code></pre></div><div class=\"activity_PoTP\"></div></div><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2026-02-03T00:00:00.000Z\">Feb 3, 2026</time> · <!-- -->6 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/understanding-part-types-and-storage-formats",
            "title": "Understanding part types and storage formats",
            "summary": "Learn about the different part types (Wide vs Compact) and storage formats (Full vs Packed) in ClickHouse, and how they affect performance.",
            "date_modified": "2026-02-03T00:00:00.000Z",
            "tags": [
                "Core Data Concepts"
            ]
        },
        {
            "id": "https://clickhouse.com/docs/knowledgebase/count-parts-by-type",
            "content_html": "This knowledgebase article shows you how to find part counts by the type of part - wide or compact.<!-- -->\n<!-- -->\n<br>\n<p>The following query can be used to count the number of parts by type:</p>\n<div class=\"wrapper_EBtA\" style=\"height:226.20000000000002px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">SELECT\n    table,\n    part_type,\n    count(*)\nFROM system.parts\nWHERE active\nGROUP BY\n    table,\n    part_type\nORDER BY\n    table ASC,\n    part_type ASC\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>An example response is shown below:</p>\n<div class=\"wrapper_EBtA\" style=\"height:207.35000000000002px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-response\"><code class=\"language-response\">┌─table───────────────────┬─part_type─┬─count()─┐\n│ asynchronous_metric_log │ Compact   │       6 │\n│ metric_log              │ Compact   │       1 │\n│ otel_logs               │ Compact   │       5 │\n│ otel_logs               │ Wide      │       2 │\n│ part_log                │ Compact   │       2 │\n│ query_log               │ Compact   │       5 │\n│ session_log             │ Compact   │       2 │\n│ text_log                │ Compact   │       7 │\n│ trace_log               │ Compact   │       6 │\n└─────────────────────────┴───────────┴─────────┘\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>Run the query below to query part size for wide and compact parts:</p>\n<div class=\"wrapper_EBtA\" style=\"height:320.45000000000005px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">SELECT\n    table,\n    column,\n    part_type,\n    sum(rows),\n    sum(column_data_compressed_bytes),\n    sum(column_data_uncompressed_bytes)\nFROM system.parts_columns\nWHERE active\nGROUP BY\n    table,\n    column,\n    part_type\nORDER BY\n    table ASC,\n    column ASC,\n    part_type ASC\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<p>An example response is shown below:</p>\n<div class=\"wrapper_EBtA\" style=\"height:414.70000000000005px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-response\"><code class=\"language-response\">┌─table─────┬─column─────────────┬─part_type─┬─sum(rows)─┬─sum(column_data_compressed_bytes)─┬─sum(column_data_uncompressed_bytes)─┐\n│ otel_logs │ Body               │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ Body               │ Wide      │  18900157 │                         316784170 │                          2807508947 │\n│ otel_logs │ LogAttributes      │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ LogAttributes      │ Wide      │  18900157 │                         215812392 │                          3173566494 │\n│ otel_logs │ ResourceAttributes │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ ResourceAttributes │ Wide      │  18900157 │                          94428129 │                          2258154988 │\n│ otel_logs │ ServiceName        │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ ServiceName        │ Wide      │  18900157 │                             24726 │                            18973727 │\n│ otel_logs │ SeverityNumber     │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ SeverityNumber     │ Wide      │  18900157 │                             51973 │                            75600628 │\n│ otel_logs │ SeverityText       │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ SeverityText       │ Wide      │  18900157 │                             24726 │                            18973727 │\n│ otel_logs │ SpanId             │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ SpanId             │ Wide      │  18900157 │                             13048 │                            18900157 │\n│ otel_logs │ Timestamp          │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ Timestamp          │ Wide      │  18900157 │                          61225801 │                           151201256 │\n│ otel_logs │ TraceFlags         │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ TraceFlags         │ Wide      │  18900157 │                             51973 │                            75600628 │\n│ otel_logs │ TraceId            │ Compact   │   1564357 │                                 0 │                                   0 │\n│ otel_logs │ TraceId            │ Wide      │  18900157 │                             13048 │                            18900157 │\n└───────────┴────────────────────┴───────────┴───────────┴───────────────────────────────────┴─────────────────────────────────────┘\n</code></pre></div><div class=\"activity_PoTP\"></div></div>\n<div class=\"theme-admonition theme-admonition-note alert alert--secondary admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Compact part size shows as zero</div><p>Note that columns sizes are not calculated in compact parts and they therefore show as <code>0</code> above.</p></div></div><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2026-01-12T00:00:00.000Z\">Jan 12, 2026</time> · <!-- -->3 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/count-parts-by-type",
            "title": "Find counts and sizes of wide or compact parts",
            "summary": "This knowledgebase article shows you how to find part counts by the type of part - wide or compact.",
            "date_modified": "2026-01-12T00:00:00.000Z",
            "tags": [
                "Troubleshooting"
            ]
        },
        {
            "id": "https://clickhouse.com/docs/knowledgebase/collect-and-draw-traces",
            "content_html": "This guide shows you how to collect and draw query traces with self-managed ClickHouse using either built-in methods or using Grafana. This is particularly useful when you're working with complex queries and need to understand the internal execution mechanics beyond what EXPLAIN provides.<!-- -->\n<!-- -->\n<br>\n<br>\n<p><strong>Prerequisites</strong>:</p>\n<ul class=\"custom-ul\">\n<li class=\"custom-li\">Familiarity with ClickHouse <a href=\"https://clickhouse.com/docs/operations/configuration-files\">config files</a></li>\n<li class=\"custom-li\">A running instance of <a href=\"https://clickhouse.com/docs/install\">ClickHouse server</a></li>\n<li class=\"custom-li\">(Optional) A running local <a href=\"https://grafana.com/docs/grafana/latest/fundamentals/getting-started/\" target=\"_blank\" rel=\"noopener noreferrer\">Grafana instance</a></li>\n</ul>\n<div class=\"sc-xro1ly-0 QlHdI vertical-stepper \"><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-0\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Check that  system table is enabled</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"enable-system-table\">Check that <code>opentelemetry_span_log</code> system table is enabled<a href=\"https://clickhouse.com/docs/knowledgebase/collect-and-draw-traces#enable-system-table\" class=\"hash-link\" aria-label=\"Direct link to enable-system-table\" title=\"Direct link to enable-system-table\">​</a></h2><p>If you have not made any modifications to the <code>opentelemetry_span_log</code> section of <code>config.xml</code> you can skip this step.</p><p>Open your default ClickHouse <code>config.xml</code> file and find the following section:</p><div class=\"wrapper_EBtA\" style=\"height:546.6500000000001px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-yaml\"><code class=\"language-yaml\">&lt;!--\n    OpenTelemetry log contains OpenTelemetry trace spans.\n\n    NOTE: this table does not use standard schema with event_date and event_time!\n--&gt;\n&lt;opentelemetry_span_log&gt;\n    &lt;!--\n        The default table creation code is insufficient, this &lt;engine&gt; spec\n        is a workaround. There is no 'event_time' for this log, but two times,\n        start and finish. It is sorted by finish time, to avoid inserting\n        data too far away in the past (probably we can sometimes insert a span\n        that is seconds earlier than the last span in the table, due to a race\n        between several spans inserted in parallel). This gives the spans a\n        global order that we can use to e.g. retry insertion into some external\n        system.\n    --&gt;\n    &lt;engine&gt;\n        engine MergeTree\n        partition by toYYYYMM(finish_date)\n        order by (finish_date, finish_time_us, trace_id)\n    &lt;/engine&gt;\n    &lt;database&gt;system&lt;/database&gt;\n    &lt;table&gt;opentelemetry_span_log&lt;/table&gt;\n    &lt;flush_interval_milliseconds&gt;7500&lt;/flush_interval_milliseconds&gt;\n    &lt;max_size_rows&gt;1048576&lt;/max_size_rows&gt;\n    &lt;reserved_size_rows&gt;8192&lt;/reserved_size_rows&gt;\n    &lt;buffer_size_rows_flush_threshold&gt;524288&lt;/buffer_size_rows_flush_threshold&gt;\n    &lt;flush_on_crash&gt;false&lt;/flush_on_crash&gt;\n&lt;/opentelemetry_span_log&gt;\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>Make sure that it is not commented out, or else you won't be able to see <code>system.opentelemetry_span_log</code> in the following steps.\nThis can also be the case if your ClickHouse server is not using the default configuration file.</p><p>Check your server logs for something like:</p><div class=\"wrapper_EBtA\" style=\"height:37.7px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">Processing configuration file 'config.xml'.\nThere is no file 'config.xml', will use embedded config.\n</code></pre></div><div class=\"activity_PoTP\"></div></div><div class=\"theme-admonition theme-admonition-tip alert alert--success admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Tip</div><p>In standard installations this file is located at <code>/etc/clickhouse-server/config.xml</code></p></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-1\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Enable OpenTelemetry tracing</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"enable-otel-tracing\">Enable OpenTelemetry tracing<a href=\"https://clickhouse.com/docs/knowledgebase/collect-and-draw-traces#enable-otel-tracing\" class=\"hash-link\" aria-label=\"Direct link to Enable OpenTelemetry tracing\" title=\"Direct link to Enable OpenTelemetry tracing\">​</a></h2><p>With your ClickHouse server running, open ClickHouse client and enable trace collection using the following query:</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-bash\"><code class=\"language-bash\">SET opentelemetry_trace_processors=1;\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>You should now see the <code>opentelemetry_span_log</code> system table if you run:</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">SHOW TABLES IN system\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>Next run:</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-txt\"><code class=\"language-txt\">SET opentelemetry_start_trace_probability=1;\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>This sets the probability that the ClickHouse can start a trace for executed queries, where\n<code>1</code> means that the trace is enabled for all executed queries.</p></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-2\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Obtain a query ID</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"obtain-query-id\">Obtain a query ID<a href=\"https://clickhouse.com/docs/knowledgebase/collect-and-draw-traces#obtain-query-id\" class=\"hash-link\" aria-label=\"Direct link to Obtain a query ID\" title=\"Direct link to Obtain a query ID\">​</a></h2><p>Run the following dummy query, or the query you are interested in tracing:</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">SELECT pow(number, 2) FROM numbers(10E4);\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>Copy the query id:</p><div class=\"wrapper_EBtA\" style=\"height:131.95000000000002px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">:) SELECT pow(number, 2) FROM numbers(10E4);\n\nSELECT pow(number, 2)\nFROM numbers(100000.)\n\n--highlight-next-line\nQuery id: a9241258-a0c4-4776-a00b-e6a1d9bec4a1\n</code></pre></div><div class=\"activity_PoTP\"></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-3\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Generate a trace file</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"generate-trace-file\">Generate a trace file<a href=\"https://clickhouse.com/docs/knowledgebase/collect-and-draw-traces#generate-trace-file\" class=\"hash-link\" aria-label=\"Direct link to Generate a trace file\" title=\"Direct link to Generate a trace file\">​</a></h2><p>Run the following query, substituting in the query ID you obtained in the previous step:</p><div class=\"wrapper_EBtA\" style=\"height:414.70000000000005px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">WITH 'a9241258-a0c4-4776-a00b-e6a1d9bec4a1' AS my_query_id\nSELECT\n    concat(substring(hostName(), length(hostName()), 1), leftPad(greatest(attribute['clickhouse.thread_id'], attribute['thread_number']), 5, '0')) AS group,\n    operation_name,\n    start_time_us,\n    finish_time_us,\n    sipHash64(operation_name) AS color,\n    attribute\nFROM system.opentelemetry_span_log\nWHERE (trace_id IN (\n    SELECT trace_id\n    FROM system.opentelemetry_span_log\n    WHERE (attribute['clickhouse.query_id']) = my_query_id\n)) AND (operation_name != 'query') AND (operation_name NOT LIKE 'Query%')\nORDER BY\n    hostName() ASC,\n    group ASC,\n    parent_span_id ASC,\n    start_time_us ASC\nINTO OUTFILE 'trace.json'\nFORMAT JSON\nSETTINGS output_format_json_named_tuples_as_objects = 1\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>This will write the trace to a file named <code>trace.json</code>.\nBy default, this file is created in the current working directory from which you are running the clickhouse-client or clickhouse-local tool.</p></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-4\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Visualize trace using built-in tooling</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"visualize-trace-using-built-in-tooling\">Visualize trace using built-in tooling<a href=\"https://clickhouse.com/docs/knowledgebase/collect-and-draw-traces#visualize-trace-using-built-in-tooling\" class=\"hash-link\" aria-label=\"Direct link to Visualize trace using built-in tooling\" title=\"Direct link to Visualize trace using built-in tooling\">​</a></h2><p>Use the hosted trace visualizer at <a href=\"https://trace-visualizer.clickhouse.com/\" target=\"_blank\" rel=\"noopener noreferrer\">https://trace-visualizer.clickhouse.com/</a>.\nLoad the <code>trace.json</code> file from the previous step to visualize the trace.</p><div style=\"position:relative;margin-bottom:16px;margin-top:16px\"><div style=\"cursor:default\"><div style=\"background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAFCAYAAAB8ZH1oAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAu0lEQVR4nC3MPwsBcRzH8XtQJiUMHoLFAyDLFeUJWJTdYLRQIvmzkM1EKXWUhOGU5E4G59fdz/f7/eic5bN8Xr0NIq1P5wdP+jtezQ68nJ14s7DZ9wPWWpOIQGt9MwDwdnGFGR+glBwhH+vATHUR+AQAEg4ROSEka32UbLosuUxFaoWhzNt7IeIQ8R/ef0XXdTGe9lCvNlBMtHC3vfAHRzgqMvNLBAqAci++GjUt9XQ8BYgiojczf4jI/gLtw6+Frs+wuwAAAABJRU5ErkJggg==&quot;);width:600px;margin:0 auto;display:block;box-shadow:none\"><svg style=\"width:100%;height:auto;max-width:100%;margin-bottom:-4px\" width=\"1024\" height=\"521\"></svg><noscript><img style=width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0 src=/docs/assets/ideal-img/trace-visualizer-example.3edbe1f.48.png srcset=\"/docs/assets/ideal-img/trace-visualizer-example.3edbe1f.48.png 48w,/docs/assets/ideal-img/trace-visualizer-example.2cb0386.300.png 300w,/docs/assets/ideal-img/trace-visualizer-example.6b0a177.600.png 600w,/docs/assets/ideal-img/trace-visualizer-example.b6923eb.1024.png 1024w\" alt=\"ClickHouse trace visualizer example\" width=1024 height=521></noscript></div></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-5\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Using Grafana to visualize traces</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"using-grafana\">Using Grafana to visualize traces<a href=\"https://clickhouse.com/docs/knowledgebase/collect-and-draw-traces#using-grafana\" class=\"hash-link\" aria-label=\"Direct link to Using Grafana to visualize traces\" title=\"Direct link to Using Grafana to visualize traces\">​</a></h2><p>We recommend Grafana for visualizing and exploring trace data using the official ClickHouse plugin.\nThe plugin has been enhanced to allow visualization of traces using the Trace Panel.\nThis is supported as both a visualization and as a component in Explore.</p><p>Follow the steps described in <a href=\"https://clickhouse.com/docs/observability/grafana\" target=\"_blank\" rel=\"noopener noreferrer\">\"Using Grafana and ClickHouse for Observability\"</a>\nto set up Grafana with the ClickHouse plugin.</p><p>From the Explore tab you can then run the following query, replacing the <code>trace_id</code> with your own:</p><div class=\"wrapper_EBtA\" style=\"height:207.35000000000002px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-sql\"><code class=\"language-sql\">SELECT\n    toString(trace_id) AS traceID,\n    toString(span_id) AS spanID,\n    if(toString(parent_span_id)='0', '', toString(parent_span_id)) AS parentSpanID,\n    'ClickHouse' AS serviceName,\n    operation_name AS operationName,\n    start_time_us/1000000 AS startTime,\n    (finish_time_us - start_time_us)/1000 AS duration,\n    arrayMap(key -&gt; map('key', key, 'value', attribute[key]), mapKeys(attribute)) AS serviceTags\nFROM system.opentelemetry_span_log\nWHERE trace_id = '68a14b27-a61f-596d-3746-2b03d2530e42' ORDER BY startTime ASC\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>Make sure to set <code>Query type</code> to <code>Traces</code>:</p><div style=\"position:relative;margin-bottom:16px;margin-top:16px\"><div style=\"cursor:default\"><div style=\"background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAADCAYAAACqPZ51AAAACXBIWXMAABYlAAAWJQFJUiTwAAAAb0lEQVR4nAXBixHCIBAFwNQgI4bj+BwhIaijHdh/K0kLGd65O9W6XxwEsjSk0kAsIM5wlOBDGSKb7t/fMfX2HikWdZxVylPX7aWRRT1FNZZAc9CQP+dEnK+ZIh4uYqkdbe3wFOF8hrV+mLvTm7HHH7gpKHvqJdOAAAAAAElFTkSuQmCC&quot;);width:600px;margin:0 auto;display:block;box-shadow:none\"><svg style=\"width:100%;height:auto;max-width:100%;margin-bottom:-4px\" width=\"1024\" height=\"338\"></svg><noscript><img style=width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0 src=/docs/assets/ideal-img/trace-visualization-grafana.c47436e.48.png srcset=\"/docs/assets/ideal-img/trace-visualization-grafana.c47436e.48.png 48w,/docs/assets/ideal-img/trace-visualization-grafana.0844f47.300.png 300w,/docs/assets/ideal-img/trace-visualization-grafana.2c50357.600.png 600w,/docs/assets/ideal-img/trace-visualization-grafana.ea5a411.1024.png 1024w\" alt=\"ClickHouse trace visualization in Grafana\" width=1024 height=338></noscript></div></div></div><p>Click \"Run Query\" and inspect the trace diagram:</p><div style=\"position:relative;margin-bottom:16px;margin-top:16px\"><div style=\"cursor:default\"><div style=\"background-size:cover;background-repeat:no-repeat;position:relative;background-image:url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAECAYAAAC3OK7NAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAl0lEQVR4nB3FWwqCQBQA0FlFOjLO25HJKV+llCREVBDtfwniGsrrDTo/hyidfZzfgQ81SO2AcQMJU5AwCYzrhQuLKqsnElEOPuwxlCUqY1Bq/d9Yi9qa1TqHQoiZRDT95j5A2/XgtgGavoP7+wnDdYTq2C7locG8KCYS03QxzuPpMmJR1Xgcznh7PVAah5s4XeNEYET5/AMjXDoZcP+PtAAAAABJRU5ErkJggg==&quot;);width:600px;margin:0 auto;display:block;box-shadow:none\"><svg style=\"width:100%;height:auto;max-width:100%;margin-bottom:-4px\" width=\"1024\" height=\"448\"></svg><noscript><img style=width:100%;height:auto;max-width:100%;margin-bottom:-4px;position:absolute;top:0;left:0 src=/docs/assets/ideal-img/trace-visualization-diagram.7afc8ee.48.png srcset=\"/docs/assets/ideal-img/trace-visualization-diagram.7afc8ee.48.png 48w,/docs/assets/ideal-img/trace-visualization-diagram.c678fc4.300.png 300w,/docs/assets/ideal-img/trace-visualization-diagram.25d242b.600.png 600w,/docs/assets/ideal-img/trace-visualization-diagram.4472323.1024.png 1024w\" alt=\"ClickHouse trace visualization in Grafana\" width=1024 height=448></noscript></div></div></div></div></div></div><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2025-12-30T00:00:00.000Z\">Dec 30, 2025</time> · <!-- -->4 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/collect-and-draw-traces",
            "title": "How to collect and draw a query trace",
            "summary": "This guide shows you how to collect and draw query traces with self-managed ClickHouse using either built-in methods or using Grafana. This is particularly useful when you're working with complex queries and need to understand the internal execution mechanics beyond what EXPLAIN provides.",
            "date_modified": "2025-12-30T00:00:00.000Z",
            "tags": [
                "Tools and Utilities"
            ]
        },
        {
            "id": "https://clickhouse.com/docs/knowledgebase/restore-replica-after-storage-failure",
            "content_html": "This article explains how to recover data when using replicated tables in atomic databases in ClickHouse and disks/storage on one of the replica is lost/currupted.<!-- -->\n<!-- -->\n<br>\n<br>\n<div class=\"theme-admonition theme-admonition-note alert alert--secondary admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Note</div><p>This guide assumes that the <code>&lt;path&gt;</code> parameter in your config.xml file is set to:</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">&lt;path&gt;/var/lib/clickhouse/&lt;/path&gt;\n</code></pre></div><div class=\"activity_PoTP\"></div></div><p>If you have configured a different data path, replace all instances of <code>/var/lib/clickhouse</code> in the below commands with the actual value of your <code>&lt;path&gt;</code> setting.</p></div></div>\n<div class=\"sc-xro1ly-0 QlHdI vertical-stepper \"><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-0\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Copy access configuration from the healthy replica</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"copy-access-config\">Copy access configuration from the healthy replica<a href=\"https://clickhouse.com/docs/knowledgebase/restore-replica-after-storage-failure#copy-access-config\" class=\"hash-link\" aria-label=\"Direct link to Copy access configuration from the healthy replica\" title=\"Direct link to Copy access configuration from the healthy replica\">​</a></h2><p>Copy the contents of the <code>access</code> folder which contains local users from the healthy replica:</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">/var/lib/clickhouse/access\n</code></pre></div><div class=\"activity_PoTP\"></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-1\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Back up the metadata folder from the healthy replica</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"back-up-the-metadata-folder-from-the-healthy-replica\">Back up the metadata folder from the healthy replica<a href=\"https://clickhouse.com/docs/knowledgebase/restore-replica-after-storage-failure#back-up-the-metadata-folder-from-the-healthy-replica\" class=\"hash-link\" aria-label=\"Direct link to Back up the metadata folder from the healthy replica\" title=\"Direct link to Back up the metadata folder from the healthy replica\">​</a></h2><ol class=\"custom-ol\">\n<li class=\"custom-li\">Navigate to the ClickHouse data directory:</li>\n</ol><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">cd /var/lib/clickhouse\n</code></pre></div><div class=\"activity_PoTP\"></div></div><ol class=\"custom-ol\" start=\"2\">\n<li class=\"custom-li\">Create a backup of the metadata folder (including symbolic links): The metadata directory contains DDLs for databases and tables.\nThe database directory has symlinks to <code>/var/lib/clickhouse/store/..</code> which contains all the table DDLs.</li>\n</ol><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-bash\"><code class=\"language-bash\">{ find metadata -type f; find metadata -type l; find metadata -type l | xargs readlink -f; } | tar -cPf backup.tar --files-from=-\n</code></pre></div><div class=\"activity_PoTP\"></div></div><div class=\"theme-admonition theme-admonition-note alert alert--secondary admonition_WoCw\"><div class=\"alert-icon admonitionIcon_Ibzs\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></div><div class=\"alert-content admonitionContent_vXIg\"><div class=\"admonitionHeading_TMsN\">Note</div><p>This command ensures that both the <strong>metadata files</strong>, and the symlink architecture are preserved in the backup.</p></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-2\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Restore the metadata on the faulty replica</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"restore-the-metadata-on-the-faulty-replica\">Restore the metadata on the faulty replica<a href=\"https://clickhouse.com/docs/knowledgebase/restore-replica-after-storage-failure#restore-the-metadata-on-the-faulty-replica\" class=\"hash-link\" aria-label=\"Direct link to Restore the metadata on the faulty replica\" title=\"Direct link to Restore the metadata on the faulty replica\">​</a></h2><ol class=\"custom-ol\">\n<li class=\"custom-li\">Copy the generated <code>backup.tar</code> file to the faulty replica.</li>\n<li class=\"custom-li\">Extract it to the ClickHouse data directory:</li>\n</ol><div class=\"wrapper_EBtA\" style=\"height:37.7px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">cd /var/lib/clickhouse/\ntar -xvPf backup.tar\n</code></pre></div><div class=\"activity_PoTP\"></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-3\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Create the force restore flag</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-force-restore-flag\">Create the force restore flag<a href=\"https://clickhouse.com/docs/knowledgebase/restore-replica-after-storage-failure#create-force-restore-flag\" class=\"hash-link\" aria-label=\"Direct link to Create the force restore flag\" title=\"Direct link to Create the force restore flag\">​</a></h2><p>To trigger automatic data synchronization from other replicas, create the following flag:</p><div class=\"wrapper_EBtA\" style=\"height:18.85px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-text\"><code class=\"language-text\">sudo -u clickhouse touch /var/lib/clickhouse/flags/force_restore_data\n</code></pre></div><div class=\"activity_PoTP\"></div></div></div></div><div class=\"sc-xro1ly-1 bHdCRJ\"><button id=\"step-4\" class=\"sc-xro1ly-2 dOmWto\"><div class=\"sc-xro1ly-3 buAqWx\"></div><div class=\"sc-xro1ly-5 kxqSvA\">Restart the faulty replica</div></button><div class=\"sc-xro1ly-6 EHzUo\"><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"restart-faulty-replica\">Restart the faulty replica<a href=\"https://clickhouse.com/docs/knowledgebase/restore-replica-after-storage-failure#restart-faulty-replica\" class=\"hash-link\" aria-label=\"Direct link to Restart the faulty replica\" title=\"Direct link to Restart the faulty replica\">​</a></h2><ol class=\"custom-ol\">\n<li class=\"custom-li\">Restart the ClickHouse server on the faulty node.</li>\n<li class=\"custom-li\">Check the server logs, you should observe parts being downloaded from the healthy replicas:</li>\n</ol><div class=\"wrapper_EBtA\" style=\"height:131.95000000000002px\"><div style=\"position:absolute;left:-9999px;top:-9999px;opacity:0;pointer-events:none;width:1px;height:1px;overflow:hidden\"><pre class=\"language-bash\"><code class=\"language-bash\">2025.11.02 00:00:04.047097 [ 682 ] {} &lt;Debug&gt; analytics.events_local (...) (Fetcher): Downloading files 23\n2025.11.02 00:00:04.055542 [ 682 ] {} &lt;Debug&gt; analytics.events_local (...) (Fetcher): Download of part 202511_0_0_0 onto disk disk2 finished.\n2025.11.02 00:00:04.101888 [ 687 ] {} &lt;Debug&gt; warehouse.customers_local (...) (Fetcher): Downloading part 2025_0_0_1 onto disk default.\n2025.11.02 00:00:04.102005 [ 687 ] {} &lt;Debug&gt; warehouse.customers_local (...) (Fetcher): Downloading files 11\n2025.11.02 00:00:04.102210 [ 690 ] {} &lt;Debug&gt; warehouse.customers_local (...) (Fetcher): Downloading part 2022_0_0_1 onto disk disk1.\n2025.11.02 00:00:04.102247 [ 688 ] {} &lt;Debug&gt; warehouse.customers_local (...) (Fetcher): Downloading part 2021_0_0_1 onto disk disk2.\n2025.11.02 00:00:04.102331 [ 690 ] {} &lt;Debug&gt; warehouse.customers_local (...) (Fetcher): Downloading files 11\n</code></pre></div><div class=\"activity_PoTP\"></div></div></div></div></div><div class=\"container_iJTo margin-vert--md\"><time datetime=\"2025-11-19T00:00:00.000Z\">Nov 19, 2025</time> · <!-- -->2 min read</div>",
            "url": "https://clickhouse.com/docs/knowledgebase/restore-replica-after-storage-failure",
            "title": "How to restore a replica after storage failure",
            "summary": "This article explains how to recover data when using replicated tables in atomic databases in ClickHouse and disks/storage on one of the replica is lost/currupted.",
            "date_modified": "2025-11-19T00:00:00.000Z",
            "tags": [
                "Deployments and Scaling"
            ]
        }
    ]
}