Use Grail buckets to partition data and help your teams work efficiently, even when they have different data perspectives and requirements. A successful partitioning strategy ensures accurate ingestion, efficient queries, cost-effective retention, and secure access.
This page provides an overview of how to select the best partitioning strategy for your organization. It describes how Grail buckets are used to partition data, and includes use cases to illustrate how to implement this in practice.
In Dynatrace Grail, table data is physically organized into buckets. Each table is specific to a single record type.
Buckets are the logical storage units where records are stored. Buckets are always associated with a specific record type, such as logs, events, or spans, as shown in the figure below. Dynatrace provides built-in buckets, and you can create custom buckets.

By partitioning data into separate buckets, you'll implement the technical foundation that allow advanced data management features:
Through experience, we've learned that many of our enterprise customers have central monitoring teams that manage how Dynatrace (and other software) is used across the rest of the teams. Because of this, we recommend that a centralized team also define the overall partitioning strategy. It is vital that this centralized team can evaluate strategy efficiency and adjust as needed.
Many teams produce data, but not all of them own the lifecycle, quality, or retention requirements of that data. In some organizations, the teams that send data into Dynatrace are not the same teams that pay for retention or define retention periods. Make sure these responsibilities are clearly assigned. Clear ownership helps prevent routing issues, avoids unexpected storage growth, and ensures that access privileges stay aligned with actual responsibility.
When designing your partitioning strategy, identify:
For example, you might want restrict default bucket access to the central monitoring team. By doing this, you can help prevent exposing confidential data to unintended audiences.
Read more about how to use buckets to partition data for these use cases:
As a first step, we recommend creating a dedicated set of buckets per record type.
You can create custom buckets via either
Storage Management or the API, see Manage custom Grail buckets.
When you create a custom bucket, you can choose a name that best fits your purpose. We recommend:
default anywhere in the name, because you might confuse the built-in default_logs bucket with a custom logs_default bucket.
The figure below shows how you could name custom buckets with _shared as a suffix.If you haven't specified bucket assignment in the pipeline configuration, Dynatrace will send all ingested data to the default buckets.
That means any ingested log lines, for example, will go into default_logs unless the ingest pipeline configuration specifies a different bucket.
While this ensures no data is lost, it doesn't make it easy to tell whether the incoming data is from an already existing monitored component or a service that's just started sending data.
If you configure OpenPipeline so that all known components send their data to your custom buckets, the default buckets should remain empty. If data still appears in a default bucket, it means either that routing was incorrectly configured or that the newly arriving data isn't yet part of the routing setup.
That makes it easy to spot where the data came from, so you can fix the routing, adjust permissions, or drop the data.

Cost Allocation is the process of attributing data storage and query costs to different products or cost centers. You can use buckets to help allocate retention costs, so that the appropriate cost center is billed for their retained telemetry data.
When allocating log retention, Dynatrace can automatically derive cost allocation data from the ingested data, see Allocate your DPS costs. This use case is relevant for all telemetry data.
We'll start with the *_shared buckets that we already created, and create additional custom buckets to allocate specific cost centers.

Retention is the length of time that you store data until it is deleted. Different types of data often have different legal, compliance, or business requirements for retention. In Grail, you can configure retention time on a per-bucket basis.
Here's an example of how to use buckets to configure retention time for specific Grail data.
revenue will need to be retained for a total of 90 days.
By default, these will all be routed to the logs_shared custom bucket that you created previously.logs_delivery_90d, which clearly indicates the record type, the purpose of the bucket, and the retention time.
The figure below shows one logs_delivery custom bucket and one logs_delivery_90d custom bucket.
As your data volume grows, efficient querying becomes increasingly essential. The amount of data you ingest per day directly impacts how quickly and cost-effectively you can search and analyze your data.
A successful partitioning strategy does two things:
With the proper strategy in place, a bucket filter is among the most powerful yet simple tools to tune query performance. This how-to guide shows you how to find the buckets that are good candidates for partitioning.
How do you find out which tables in your environment face heavy ingest volume?
Query the dt.system.bucket metric to get the currently stored volume and the retention time for all buckets.
Use the query below to get a rough estimate of your average daily ingest.
fetch dt.system.buckets| filter dt.system.table == 'logs'| fieldsAdd est_avgDailyIngest = estimated_uncompressed_bytes / retention_days| fieldsRemove display_name, dt.system.table| sort avgDailyIngest desc
This query only helps you to estimate your ingest usage. It does not directly relate to your billable ingestion.

The table below shows an example output from that query.
| Name | Retention (days) | Number of records | Uncompressed ingest (bytes) | Daily ingest (GB) |
|---|---|---|---|---|
| 90 | 282,678,654,439 | 797,861,751,782,148 | 8,865 |
| 35 | 65,637,645,806 | 160,968,442,656,818 | 4,599 |
| 90 | 43,801,831,467 | 140,559,181,836,772 | 1,562 |
| 365 | 56,514,903,838 | 217,887,824,257,262 | 597 |
| 30 | 6,822,622,009 | 12,557,657,119,475 | 419 |
| 90 | 18,657,828,347 | 36,640,016,326,373 | 407 |
We recommend splitting up buckets that receive more than 2 TB of daily ingest.

It is up to you to decide which buckets are worth investigating and which criteria make sense for your environment when splitting.
For a broader view over a longer timeframe, feel free to adapt the DQL query. For example, expand your search timeframe to get deeper insights into how the metric has developed over time.
For more advanced analysis and more complex use cases, visit the Dynatrace Community.
We recommend that you avoid relying on any single query or record. If you find a query that has potential for optimization, make sure it has an impact on large sets of data before changing your partitioning strategy.
Fine-tune your DQL queries by following the information at DQL best practices.
Check whether extracting commonly queried log, event, and span content into dedicated metrics makes sense. Querying metrics requires fewer resources and is easier to adapt to than creating buckets.
We recommend periodically reviewing bucket usage and updating your partitioning strategy as query needs evolve.
Buckets are best used for organizing data by retention time, cost allocation, or query optimization. They are not intended as the primary mechanism for securing or segmenting data access.
While it is technically possible to use buckets to restrict access to specific data, bucket partitioning is not the primary or most flexible method for managing access control in Grail.
Depending on your organization’s structure, access control and Cost Allocation will likely follow similar structural boundaries.
For flexible access control, we recommend to start with record-level permissions, as these will be the best tool for most use cases. In most scenarios, Identity and access management (IAM) provides all the features you need to define who can access which data.
When compared to bucket-based partitioning, permissions offer even greater granularity, finer control, and flexibility. You can easily adapt to changing organizational structures or access requirements without redesigning your data storage strategy. Grail offers record-level and field-level permissions, see Permissions in Grail. For day-to-day access management, we recommend to use Grail record-level permissions.
If your use case requires strict separation of access, start with IAM policies to enforce these boundaries and then use buckets to play a supporting role for very high-level separation (such as isolating highly sensitive data).
If we want to make sure that users of the infrastructure group don’t have access to delivery or accounting data, we can use the *_delivery and *_accounting custom buckets that we've already created.
We can route the relevant sensitive data to these buckets, and use IAM policies to control bucket access.
By restricting access at the bucket level, you can ensure that Grail data isn't included in any queries these users execute, including full table scans, as shown in the figure below.
Spans from a single trace can be stored in different buckets. If the user doesn’t have permission for all relevant buckets, the user won't be able to view that trace.

Such access control is only possible with buckets.
This is because you can apply a DQL DENY statement to buckets, but not to record-level permissions.
DENY storage:buckets:read WHERE storage:bucket-name="logs_delivery" is valid and denies access to logs in the logs_delivery bucket.DENY storage:logs:read WHERE storage:dt.security_context="sensitive", however, denies all access to logs, not just those with the sensitive security context.As a side effect, reducing the number of scanned buckets also improves query performance and reduces query cost. Even if the user didn't have access to see the record, the query would still scan all data in the bucket.
Nevertheless, try to avoid over-complicating your partitioning strategy just to apply specific access controls.
Now that you've seen how you can create custom buckets to achieve certain use cases, here are some more things to think about.
Migration is a good opportunity to rethink your partitioning strategy. It might be tempting to replicate your previous tool's structure (like Splunk indexes), but keep in mind that Grail buckets have different constraints and capabilities. In Grail, each record can only reside in one bucket, and you can't move data between buckets once it's been ingested.
You'll always have certain fields that have an extra high number of scanned bytes, when compared to other fields. High-volume fields could include, for example, Kubernetes clusters, cloud account IDs, or applications. These are often natural categories for partitioning data, but not always.
Making one bucket for each Kubernetes cluster (1:1 mapping) sounds simple but it could result in many underutilized buckets, which increases your maintenance workload and the number of scanned buckets per query.
Instead, consider a 1:1 mapping for only the most significant Kubernetes clusters. You can then group the majority of the smaller Kubernetes clusters into a few buckets, or even just one single bucket. This approach is still compatible with strict access control requirements, as it combines bucket and record-level permissions, see Using Grail buckets for access control.
Just because you can add a new custom bucket doesn't mean you should.
For low data volume environments, the default bucket setup is often sufficient.
The most relevant information in deciding when to create a new custom bucket is each bucket's fill rate. For example, a bucket with ingest of 12 TB per day over a one-year period can cause queries and dashboards to time out. While that amount of data in a single bucket can be an intentional decision, it is often a clear indicator of a missing or inappropriate partitioning strategy. You might want to consider routing some data to a different bucket or creating a new custom bucket.
Query performance is dependent on the timeframe and amount of data in the bucket. If you know that you'll only query within narrow timeframes, it's possible ingest ingest well over 15 TB per day into a single bucket without issues. However, longer timeframes in high-volume buckets are more likely to result in slow queries and will yield larger data volumes.
For queries started within apps (such as
Dashboards and
Notebooks) and workflows, the timeout is 5 minutes.
Even if you're fine with slower queries, make sure that the bucket size still allows the query to return data within the timeout limit.
The more frequently data is queried, the more it might be worth storing in a custom bucket. Even if the data volume per query is lower, the higher number of queries will still add up to a higher total cost.
For some use cases there is no alternative to buckets, while for others there is an alternative. Using buckets to achieve each use case (especially if buckets aren't the best solution) has the potential to drastically increase your number of custom buckets, which increases maintenance workload.
While there's a lot of flexibility in setting up your bucket strategy, changing an existing setup can be challenging. It's better to start small and to adjust whenever you see proof that it is needed.
Over-engineering the setup early on might lead to rethinking large parts of it later, because expectations didn't align with actual development.
Creating hundreds of buckets manually is tedious and error-prone. We highly recommend using APIs and automation to configure those buckets, in conjunction with a well-defined naming convention.
Short retention times make it easier to adjust your bucket strategy. As data ages out, the legacy structure ages out as well.
Here's an example model for partitioning that we've seen successfully used by several large customers. Don't take this as a blueprint to copy 1:1, but rather as a starting point for your specific organizational needs.
One Dynatrace customer observes thousands of applications with Dynatrace and has an ingest volume of 250 TB per day. The customer considered creating dedicated buckets for each application, but realized that many would have remained underutilized. Therefore, this wasn't the right choice.
The customer then looked at how these applications are grouped at a higher level. In this case, about 50 different business units were responsible for the applications.
Looking at the numbers, the customer decided to create:
This helped them minimize the number of buckets, reducing complexity, while still allowing custom buckets to be created for applications that really needed them.
This example is also relevant when deciding how to partition data with natural query boundaries.
Once you've got a partitioning strategy, we recommend familiarizing yourself with OpenPipeline routing and bucket assignments. On their own, bucket definitions are just empty shells. You need to use correct routing to ensure that these buckets are filled with the data that they're intended for.
For more information, see: