Skip to main content
A Resource is immutable metadata that describes the entity producing telemetry. It’s the answer to one question: who is sending this data? Every span, metric, and log your application emits carries the same Resource. It’s set once, when the Tracer Provider is created, and it never changes for the lifetime of the process. Picking the right Resource attributes is what makes your traces routable and queryable downstream.

Anatomy of a Resource

TermWhat it means
DescribesAnswers the question “who is sending this telemetry?”
EntityService name, environment, host, pod, container — anything that identifies the producer.
ImmutableDoes not change during the process lifetime. The same Resource is attached to every span, metric, and log.
MetadataKey-value pairs.
Resource attributes should generally follow OpenTelemetry resource semantic conventions so any OTel-compatible backend can interpret them.

Configuring a Resource

from opentelemetry.sdk.resources import Resource

resource = Resource.create(
    {
        # Service attributes
        "service.name": "checkout",
        "service.namespace": "acme-webstore",
        "service.version": "v1.2.3",
        "service.instance.id": "instance-12345",

        # Environment / infrastructure attributes
        "deployment.environment.name": "production",
        "cloud.region": "us-east-1",

        # Phoenix project routing
        "openinference.project.name": "webstore-prod",
    }
)
For the full set of supported parameters, see the Resource API reference. Always use Resource.create(...) rather than the class constructor Resource(attributes=...). create() merges your attributes with the defaults from the built-in resource detectors.

Phoenix–Specific Project Routing

Phoenix resolves the project for an incoming OTLP request in this order of precedence:
SourceStatusNotes
x-project-name HTTP headerHighest precedenceSet per request to override the resource attribute. Useful when one application sends to multiple projects without re-creating a Tracer Provider. Pass via register(headers={"x-project-name": "..."}) or set on the OTLP exporter directly.
openinference.project.name resource attributeCanonicalThe name of the project in Phoenix. Defined by the OpenInference resource semconv — exposed as ResourceAttributes.PROJECT_NAME in the openinference.semconv.resource Python package, and as SEMRESATTRS_PROJECT_NAME in @arizeai/openinference-semantic-conventions for JS/TS.
Server default projectFallbackIf neither of the above is set, spans land in the default project.
For the common case, set openinference.project.name on the Resource and forget about it.

Environment Variables

The OpenTelemetry SDK can auto-detect Resource attributes from the environment, so you can avoid hardcoding them:
Environment variablePurpose
OTEL_SERVICE_NAMESets service.name.
OTEL_RESOURCE_ATTRIBUTESComma-separated key=value pairs. Sets arbitrary resource attributes.
OTEL_EXPERIMENTAL_RESOURCE_DETECTORSComma-separated list of built-in detectors to enable or disable.
Code wins over environment variables. If you set the same key in both, the value passed to Resource.create(...) takes precedence. The SDK also runs built-in resource detectors by default — they automatically fill in host, OS, process, and other infrastructure attributes. Disable them with OTEL_EXPERIMENTAL_RESOURCE_DETECTORS="" if you don’t want them. For the full list, see the OpenTelemetry SDK environment variables reference.

Common Pitfalls

Don’t try to change the Resource per request or per span. Only one Resource is allowed per Tracer Provider, and it’s intentionally immutable. If you need per-request context (a user ID, a session ID, a customer tier), set it as a span attribute instead — see Customize your traces and the OpenInference context managers.
A few more things that trip people up:
  • Not following semantic conventions — using your own attribute names instead of service.name, deployment.environment.name, etc. means other tools (and Phoenix) can’t interpret them. Stick with the resource semconv whenever there’s a standard key.
  • Using the class constructor Resource(attributes=...) instead of Resource.create(...) — bypasses built-in detectors, so you lose the auto-populated host/process attributes.
  • Forgetting to set a project name — without openinference.project.name on the Resource or an x-project-name request header, spans land in the default project and are hard to organize.

Where Resource Fits in the Pipeline

The Resource is held by the Tracer Provider alongside the Span Processor pipeline (which in turn owns the Exporter). Every span the provider hands out gets the same Resource stamped on it before export.

Next step

The Resource describes who is sending data. The Exporter sends it:

Next: Exporter and OTLP