Tags and Unified Service Tagging
The idea in one sentence
Section titled “The idea in one sentence”A tag is just key:value, but three specific tags — env, service, and version — applied the same way across metrics, logs, and traces are what let you pivot from one signal to another for the same deployable unit, which is the foundation the whole “correlating signals” part of this course is built on.
Tagging fundamentals
Section titled “Tagging fundamentals”Every metric, log, and trace in Datadog can carry tags, written as key:value — env:prod, region:ap-southeast-1, team:payments. Tags come from a few places:
- Host tags — attached automatically by the Agent (hostname, OS, cloud provider/instance metadata) or configured once in
datadog.yaml. - Integration tags — added by a check’s config, like the
service:orders-dbtag in the Postgres example from the previous lesson. - Custom tags — anything you add yourself, on a host, a container, or directly on a metric submission.
Tags are what make Datadog’s UI usable at scale: instead of hunting through a list of 4,000 hosts, you filter by env:prod AND team:payments and get exactly the slice you need. This works identically whether you’re looking at Infrastructure, Logs, or APM — same tags, same filtering syntax, everywhere.
Cardinality: the tag you should not add
Section titled “Cardinality: the tag you should not add”Not every piece of data should become a tag value. Datadog tracks time series (and indexed log facets) per unique combination of tag values, so a tag whose value is different for basically every event — a raw user ID, a request ID, a full URL with query parameters — creates a new time series or facet value for almost every data point. This is called high cardinality, and it’s expensive in two ways: it inflates your custom-metrics bill (each unique tag combination on a metric is its own billed series), and it makes the UI less useful, because a tag filter dropdown with a million distinct values stops being something a human can actually use to narrow things down.
The rule of thumb: tag by things you’d actually want to filter or group by — env, service, version, region, team, endpoint (as a normalized route pattern, not a raw path) — and put genuinely unique identifiers (user ID, request ID, order ID) in the log message or a trace attribute instead of a tag.
Unified service tagging
Section titled “Unified service tagging”This is the tagging convention the rest of this course depends on. Three tag keys are reserved and treated specially across every Datadog product:
env— which environment this is running in (prod,staging,dev).service— the name of the deployable unit (orders-api,checkout-worker).version— the specific build/release of that service (1.4.2, a git SHA, whatever your release process produces).
The reason these three exist as a dedicated convention rather than just being “tags you could add yourself” is correlation: when a host’s Agent, an application’s tracer, and a log shipper all apply the exact same env, service, and version values to their respective telemetry, Datadog can join them. You look at a spike in orders-api’s error-rate metric for env:prod, version:1.4.2, jump straight to the traces carrying those same three tags, and from there to the exact log lines emitted during those requests — without ever hand-correlating timestamps or hostnames yourself. That pivot only works because the tag values, not just the keys, match exactly across all three telemetry types — a typo like Prod vs prod silently breaks the join.
flowchart LR
subgraph Tags["env:prod service:orders-api version:1.4.2"]
M["Metrics"]
T["Traces"]
L["Logs"]
end
M -.same 3 tags.-> T
T -.same 3 tags.-> L
L -.same 3 tags.-> M
Setting it consistently: env vars and Kubernetes labels
Section titled “Setting it consistently: env vars and Kubernetes labels”On a plain host or a single container, unified service tagging is set through three environment variables that the Agent and tracer both read:
export DD_ENV="prod"export DD_SERVICE="orders-api"export DD_VERSION="1.4.2"In Kubernetes, the equivalent is a set of labels on the Deployment and Pod template — tags.datadoghq.com/env, tags.datadoghq.com/service, tags.datadoghq.com/version — and those same values are then re-injected as DD_ENV/DD_SERVICE/DD_VERSION environment variables using the Kubernetes downward API, so the tracer inside the container sees them too:
apiVersion: apps/v1kind: Deploymentmetadata: labels: tags.datadoghq.com/env: "prod" tags.datadoghq.com/service: "orders-api" tags.datadoghq.com/version: "1.4.2"spec: template: metadata: labels: tags.datadoghq.com/env: "prod" tags.datadoghq.com/service: "orders-api" tags.datadoghq.com/version: "1.4.2" spec: containers: - name: my-app env: - name: DD_ENV valueFrom: { fieldRef: { fieldPath: "metadata.labels['tags.datadoghq.com/env']" } } - name: DD_SERVICE valueFrom: { fieldRef: { fieldPath: "metadata.labels['tags.datadoghq.com/service']" } } - name: DD_VERSION valueFrom: { fieldRef: { fieldPath: "metadata.labels['tags.datadoghq.com/version']" } }The label lives in two places (Deployment metadata and Pod template metadata) because the Agent’s Autodiscovery reads pod labels directly for host/container-level tagging, while the downward API projection is what gets the same values into the process’s own environment for the tracer to pick up — one source of truth, two consumers.