{"id":12648,"date":"2026-07-06T06:37:33","date_gmt":"2026-07-06T13:37:33","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cosmosdb\/?p=12648"},"modified":"2026-07-06T06:37:33","modified_gmt":"2026-07-06T13:37:33","slug":"need-a-different-partition-key-in-azure-cosmos-db-pick-the-right-approach","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cosmosdb\/need-a-different-partition-key-in-azure-cosmos-db-pick-the-right-approach\/","title":{"rendered":"Need a different partition key in Azure Cosmos DB? Pick the right approach"},"content":{"rendered":"<p>Once you create a container, its <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/partitioning\">partition key<\/a> is fixed at creation, and you can\u2019t change it in place.\u00a0However, if your original key starts causing problems like <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/how-to-query-container#avoid-cross-partition-queries\">cross-partition queries<\/a> or <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/troubleshoot-request-rate-too-large#how-to-identify-the-hot-partition\">hot partitions<\/a>, you need to consider your options for changing it. This post explains the mechanics of changing a partition key, and the tradeoffs between the options.<\/p>\n<p>First, think about the <strong>intent<\/strong> of your change:<\/p>\n<ul>\n<li><strong>Re-partition the data.<\/strong> You want your items (and future <em>writes<\/em>) to live under a different key. This means moving to a new container and there are three ways, from least to most effort:\u00a0<a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/change-partition-key\">Change partition key<\/a>\u00a0feature, <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/container-copy\">container copy jobs<\/a>, or a do-it-yourself migration.<\/li>\n<li><strong>Query by a different key without moving.<\/strong> Your writes are fine where they are, and you only have <em>read<\/em> patterns that fan out across partitions. A Global Secondary Index adds a separate, read-only, automatically-synchronized container with a different partition key and its own data model, a projection of your items (all properties, or just a subset). The source container stays unchanged.<\/li>\n<\/ul>\n<p>Note that every \u201cchange the partition key\u201d option is actually a <strong>move<\/strong>: get your data into a <em>new<\/em> container that has the new key, then point your app at it. The exception is a <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/global-secondary-indexes\">Global Secondary Index<\/a>, which adds a different key for read patterns while the source container stays unchanged.<\/p>\n<p>Let&#8217;s walk through each path and its tradeoffs.<\/p>\n<h2>Re-partition the data<\/h2>\n<p>The three options below all land your data in a <em>new<\/em> container with the new key. But, they differ in how much Azure Cosmos DB does for you, whether you can keep writing to the source while the copy runs, and how much control you have over the cutover.<\/p>\n<h3>Option 1: the portal Change partition key feature (least effort)<\/h3>\n<p>The portal feature is the managed path. In the Azure portal, open <strong>Data Explorer<\/strong>, pick your container, go to <strong>Scale &amp; Settings \u2192 Partition Keys<\/strong>, and select <strong>Change<\/strong>.<\/p>\n<p>The portal creates or selects a destination container in the same database, then copies your data into it using <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/container-copy\">container copy jobs<\/a> (Option 2 covers those, since they\u2019re the same engine). If you let the portal create the destination container for you, all configurations except the partition key and unique keys are replicated to the destination, so you only restate those two. You can run the copy <strong>online<\/strong> or <strong>offline<\/strong> (the next section explains what that means). In online mode you finish the job by selecting <strong>Complete<\/strong>, and once the copy is done you start using the new container with the new key and optionally delete the old one.<\/p>\n<p><div class=\"alert alert-success\">Read more in the Change partition key <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/change-partition-key#limitations\">limitations<\/a>.<\/div><\/p>\n<h3>Option 2: container copy jobs directly (same engine, scriptable)<\/h3>\n<p><a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/container-copy\">Container copy jobs<\/a> us the engine behind the portal capability. You can also create and manage them yourself with <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/how-to-container-copy\">Azure CLI<\/a> (via the <code>cosmosdb-preview<\/code> extension), which is what you want for scripting, automation, or finer control. The workflow is: create the target container with the partition key (and throughput, unique keys, etc.) you want, create the copy job, monitor it, and cut over. When the source and destination are in <em>different<\/em> accounts, you first give the destination account\u2019s identity read access to the source container. Same-account copies don\u2019t need this step.<\/p>\n<p>Container copy has two modes: online, where writes continue during the copy, and offline, where you stop writes before the job starts.<\/p>\n<p>Online mode keeps writes flowing during the copy and ends with a short Complete-then-cutover, but every source write is charged double RUs while online copy is enabled, and it needs continuous backup, <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/change-feed-modes?tabs=latest-version#all-versions-and-deletes-change-feed-mode\">all-versions-and-deletes change feed<\/a>, and the account-level capability switched on first. Offline skips those prerequisites at the cost of a write freeze on the source until you cut over. The <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/container-copy\">Container copy jobs<\/a> documentation walks through the prerequisites and cutover for both modes.<\/p>\n<p>Both modes run on a best-effort basis (there&#8217;s no guaranteed completion time), and jobs run one at a time in the write region. Two things are worth planning for: a copy doesn&#8217;t carry TTL across, so an item that hasn&#8217;t expired restarts its countdown in the destination, and you&#8217;ll want the target provisioned at roughly twice the source throughput to keep up.<\/p>\n<p><div class=\"alert alert-info\"><strong>Before you cut over:<\/strong> A new container is also your one window to turn on features that can only be set at creation, the clearest being hierarchical partition keys, so weigh them now if they fit your workload. And since you\u2019re re-keying everything, confirm the new partition key value plus id is unique across the destination, or two formerly distinct items can collide.<\/div><\/p>\n<h3>Option 3: roll your own migration (most control, most work)<\/h3>\n<p>If you need transforms during the move, your container exceeds the container-copy limits, or you just want full ownership of the process, create the new container yourself and move the data with your own pipeline. The <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/migrate\">large-scale migration guidance<\/a> covers the building blocks:<\/p>\n<ul>\n<li><strong>Bulk ingestion<\/strong> to load the new container fast: the .NET v3 SDK has it built in, and the <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/bulk-executor-overview\">bulk executor library<\/a> covers apps still on .NET SDK 2.x.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"40\"><strong>Azure Data Factory<\/strong>\u00a0for smaller datasets (configure source and sink, no code), or the\u00a0<strong>Spark connector<\/strong>\u00a0if you already work in Spark.<\/li>\n<li class=\"code-line\" dir=\"auto\" data-line=\"41\"><strong><a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/change-feed\" data-href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/change-feed\">Change feed<\/a><\/strong>\u00a0to catch writes that land on the source after your bulk load and replay them into the new container until you cut over. Its default\u00a0<a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/change-feed-modes\" data-href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/change-feed-modes\">latest-version mode<\/a> skips deletes and TTL expirations, so use all-versions-and-deletes (which needs continuous backup) to handle delete operations.<\/li>\n<\/ul>\n<p>Pre-create the destination with enough RU\/s and turn off indexing during the load to hold down write cost. You own incremental sync, error handling, cutover, and any reshaping along the way.<\/p>\n<h2>Query by a different key, without moving (global secondary index)<\/h2>\n<p>If your only pain is cross-partition queries (reads that fan out because your filter property isn\u2019t the partition key), you may not need to move at all. For that case, use <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/global-secondary-indexes\">global secondary indexes<\/a>.<\/p>\n<p>A global secondary index (GSI) is a separate, read-only container keyed by a <em>different<\/em> partition key, with its own data model. That model is a projection query over your items (<code>SELECT *<\/code> for all properties, or just the subset you query), and it stays automatically synchronized with the source container. You keep writing to your existing container with its existing key, and queries whose filter matches the GSI\u2019s partition key (the specific patterns you built the GSI for) can become single-partition queries against the GSI instead of cross-partition queries on the source. A GSI helps the patterns it\u2019s keyed for, not every cross-partition query.<\/p>\n<p>GSIs fit when changing your existing partition key would be disruptive, and you have multiple query patterns that no single key can satisfy. A GSI needs continuous backups on the account, stays eventually consistent regardless of your consistency level, and runs on autoscale with its own storage and RU costs. Once one exists, replace and delete on the source cost 50\u2013100% more RU (creates aren\u2019t affected), and <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/global-secondary-indexes\">global secondary indexes<\/a> cover the rest.<\/p>\n<p>A GSI fixes reads. It doesn\u2019t change where your <em>writes<\/em> land, so it won\u2019t relieve a write-side hot partition on the source. If your problem is write distribution, or you need a different primary key for the data, you\u2019re back to the data re-partitioning option above and you have to move.<\/p>\n<h2 class=\"code-line\" dir=\"auto\" data-line=\"59\">To wrap up<\/h2>\n<p class=\"code-line\" dir=\"auto\" data-line=\"59\">If your writes need a different key, you end up moving the data, and the only question left is how much of the copy you hand to Azure Cosmos DB. If only your reads fan out, a GSI fixes that without changing where your writes land. The table below puts all four side by side.<\/p>\n<h3>Decision table<\/h3>\n<table style=\"width: 100%;\">\n<thead>\n<tr>\n<th style=\"width: 24.3133%;\"><strong>Option<\/strong><\/th>\n<th style=\"width: 20.5493%;\"><strong>Keep writing to the source during the move?<\/strong><\/th>\n<th style=\"width: 8.34181%;\"><strong>Effort<\/strong><\/th>\n<th style=\"width: 36.826%;\"><strong>Use it when<\/strong><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"width: 24.3133%;\"><strong>Portal &#8220;Change partition key&#8221;<\/strong><\/td>\n<td style=\"width: 20.5493%;\">Yes (online mode) or no (offline)<\/td>\n<td style=\"width: 8.34181%;\">Lowest<\/td>\n<td style=\"width: 36.826%;\">You want point-and-click, container &lt; 1,000,000 RU\/s, &lt; 4 TB, supported region<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 24.3133%;\"><strong>Container copy jobs (CLI)<\/strong><\/td>\n<td style=\"width: 20.5493%;\">Yes (online) or no (offline)<\/td>\n<td style=\"width: 8.34181%;\">Low\u2013medium<\/td>\n<td style=\"width: 36.826%;\">You want the managed engine but scripted, with explicit online\/offline control<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 24.3133%;\"><strong>Roll your own (bulk \/ ADF \/ Spark + change feed)<\/strong><\/td>\n<td style=\"width: 20.5493%;\">Yes, if you build incremental sync yourself<\/td>\n<td style=\"width: 8.34181%;\">Highest<\/td>\n<td style=\"width: 36.826%;\">You need transforms, are above the copy limits, or want full control of cutover<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 24.3133%;\"><strong>Global secondary index<\/strong><\/td>\n<td style=\"width: 20.5493%;\">N\/A, source stays as-is, GSI auto-synced<\/td>\n<td style=\"width: 8.34181%;\">Medium<\/td>\n<td style=\"width: 36.826%;\">The pain is cross-partition\u00a0<em>reads<\/em>, not write hot partitions, and you can keep the source key<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Learn more<\/h2>\n<ul>\n<li><a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/partitioning\">Partitioning in Azure Cosmos DB<\/a>, <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/change-partition-key\">Change the partition key (portal feature)<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/container-copy\">Container copy jobs<\/a>, <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/how-to-container-copy\">Create and manage container copy jobs (CLI)<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/global-secondary-indexes\">Global secondary indexes<\/a>, <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/hierarchical-partition-keys\">Hierarchical partition keys<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/migrate\">Migrate large amounts of data<\/a>, <a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/bulk-executor-overview\">Bulk executor library overview<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/azure\/cosmos-db\/change-feed-modes\">Change feed modes<\/a><\/li>\n<\/ul>\n<div class=\"markdown-heading\" dir=\"auto\">\n<h2 class=\"heading-element\" dir=\"auto\" tabindex=\"-1\">About Azure Cosmos DB<\/h2>\n<\/div>\n<p dir=\"auto\">Azure Cosmos DB is a fully managed and serverless NoSQL and vector database for modern app development, including AI applications. With its SLA-backed speed and availability as well as instant dynamic scalability, it is ideal for real-time NoSQL and MongoDB applications that require high performance and distributed computing over massive volumes of NoSQL and vector data.<\/p>\n<p dir=\"auto\">To stay in the loop on Azure Cosmos DB updates, follow us on\u00a0<a href=\"https:\/\/twitter.com\/AzureCosmosDB\" rel=\"nofollow\">X<\/a>,\u00a0<a href=\"https:\/\/aka.ms\/AzureCosmosDBYouTube\" rel=\"nofollow\">YouTube<\/a>, and\u00a0<a href=\"https:\/\/www.linkedin.com\/company\/azure-cosmos-db\/\" rel=\"nofollow\">LinkedIn<\/a>. Join the discussion with other developers on the\u00a0<a href=\"https:\/\/discord.gg\/pczdC2SU\" rel=\"nofollow\">#nosql channel on the Microsoft Open Source Discord<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Once you create a container, its partition key is fixed at creation, and you can\u2019t change it in place.\u00a0However, if your original key starts causing problems like cross-partition queries or hot partitions, you need to consider your options for changing it. This post explains the mechanics of changing a partition key, and the tradeoffs between [&hellip;]<\/p>\n","protected":false},"author":181737,"featured_media":12667,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1980,644,1828],"tags":[499,1737],"class_list":["post-12648","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-cosmos-db","category-change-feed","category-data-modeling","tag-azure-cosmos-db","tag-partitioning"],"acf":[],"blog_post_summary":"<p>Once you create a container, its partition key is fixed at creation, and you can\u2019t change it in place.\u00a0However, if your original key starts causing problems like cross-partition queries or hot partitions, you need to consider your options for changing it. This post explains the mechanics of changing a partition key, and the tradeoffs between [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/12648","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/users\/181737"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/comments?post=12648"}],"version-history":[{"count":2,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/12648\/revisions"}],"predecessor-version":[{"id":12669,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/posts\/12648\/revisions\/12669"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media\/12667"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/media?parent=12648"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/categories?post=12648"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cosmosdb\/wp-json\/wp\/v2\/tags?post=12648"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}