Configuring your Apicurio Registry deployment
You can configure your Apicurio Registry deployment, including file-based secrets, HTTP caching, the web console, logging, health checks, observability, and the storage content cache.
| For a list of all available configuration options, see Apicurio Registry configuration reference. |
Loading configuration values from files
You can load a Apicurio Registry configuration value from a file, such as a mounted secret, to keep the value out of the deployment’s environment variables.
-
You can update and restart the Apicurio Registry deployment.
-
The file contains the required configuration value and is readable by the Apicurio Registry process.
-
Mount the file in the Apicurio Registry container, for example, at
/run/secrets/db_password. Use file permissions that restrict access to the processes that need the value. -
Set the environment variable for the configuration property with an additional
_FILEsuffix:APICURIO_DATASOURCE_PASSWORD_FILE=/run/secrets/db_passwordAlternatively, configure the property with a
.filesuffix in the server configuration:apicurio.datasource.password.file=/run/secrets/db_passwordThe file value takes precedence over a directly configured value for
apicurio.datasource.password. Apicurio Registry removes leading and trailing whitespace from the file contents. This mechanism applies to configuration properties resolved by the server, including properties other than passwords. -
Restart the deployment to load the value.
Apicurio Registry caches the value after reading the file. Restart the deployment after changing the file or its configured path.
-
Check the server log for a message that confirms the file was loaded for
apicurio.datasource.password. The message identifies the property without displaying the secret value.
-
If the server reports that the file is missing or unreadable, check the mounted path and file permissions. A configured file that cannot be read causes configuration resolution to fail; the server does not fall back to the directly configured value.
Enabling HTTP caching for artifact content
You can enable HTTP cache headers for supported v3 content endpoints so that a reverse proxy can serve repeated requests from its cache.
-
You can update and restart the Apicurio Registry deployment.
-
A reverse proxy cache is available in front of Apicurio Registry.
-
An artifact version is available for testing content requests.
-
Enable HTTP caching in the server configuration:
apicurio.http-caching.enabled=trueThe equivalent environment variable is
APICURIO_HTTP_CACHING_ENABLED=true. -
Set expiration times that match how long your clients can tolerate stale content. For example, the following configuration caches highly cacheable content for one hour and uses shorter periods for content that can change:
apicurio.http-caching.high-cacheability.max-age-seconds=3600 apicurio.http-caching.moderate-cacheability.max-age-seconds=30 apicurio.http-caching.low-cacheability.max-age-seconds=10 -
Configure the reverse proxy to use
Surrogate-Control,ETag, andVaryresponse headers.The default
Varyheaders areAccept,Accept-Encoding, andAuthorization. Configure the proxy to keep responses for different credentials separate and to enforce your access policy when serving cached responses. Apicurio Registry does not invalidate proxy entries when content access or version state changes. -
Restart the Apicurio Registry deployment to apply the configuration.
-
Request an existing artifact version through the proxy and inspect its response headers:
curl --include \ "https://registry.example.com/apis/registry/v3/groups/my-group/artifacts/my-schema/versions/1.0.0/content"Replace the URL and artifact coordinates with values from your deployment, and supply authentication if required. For a successful cacheable response, check for
ETag,Surrogate-Control, andVaryheaders. With the default diagnostic headers enabled,X-Cache-Cacheabilityidentifies the selected cacheability level. Use the proxy’s cache-hit statistics or logs to verify that repeated requests use its cache.
HTTP cache behavior
Apicurio Registry assigns a cacheability level to supported v3 content responses based on the request, version state, and reference handling.
| Level | Default expiration | Typical behavior |
|---|---|---|
|
864,000 seconds (10 days) |
Content identified by an immutable identifier or a specific immutable version can use the longest expiration. |
|
30 seconds |
Requests that use version expressions, such as a branch, or retrieve mutable draft content need a shorter expiration. |
|
10 seconds |
Responses whose dependencies can change or whose content identity cannot be determined use the shortest expiration. |
|
Not applicable |
The response does not receive a |
- Activation
-
HTTP caching is disabled by default. Set
apicurio.http-caching.enabled=trueand keep at least one cacheability expiration greater than zero to activate it. Setting an individual expiration to zero or a negative value suppresses the caching policy for that level. - Scope
-
Cache headers apply to supported v3 content operations, including content retrieved by global ID and by group, artifact, and version. The server evaluates the request and can reduce its cacheability because of reference handling or version mutability.
- Response headers
-
Surrogate-Controlspecifies the proxy expiration policy.ETagidentifies the response representation, andVaryidentifies request headers that distinguish cached representations. These headers do not enable browser caching; the API’sCache-Controlpolicy remainsno-cache, no-store. - Expiration and invalidation
-
Cached responses can remain available until the proxy refreshes or expires them. Automatic invalidation of reverse proxy entries is not available. Choose shorter expiration times when content changes or access changes must reach clients sooner.
Configuring the Apicurio Registry web console
You can set optional environment variables to configure the Apicurio Registry web console specifically for your deployment environment or to customize its behavior.
-
You have already installed Apicurio Registry.
-
Set the
REGISTRY_API_URLenvironment variable to override the default REST API URL. For example,https://registry-api.my-domain.com/apis/registry/v3.Typically the Apicurio Registry Operator automatically configures the UI component with the REST API v3 URL. Override this value only if your deployment requires a custom URL.
-
Set the
REGISTRY_FEATURE_READ_ONLYenvironment variable totrueto enable read-only mode. The default value isfalse.In read-only mode, you cannot make changes to registered artifacts. For example, the following actions are unavailable:
-
Creating a group
-
Creating an artifact
-
Uploading a new artifact version
-
Updating artifact metadata
-
Deleting an artifact
-
Apicurio Registry observability with OpenTelemetry
You can configure Apicurio Registry to export telemetry data using OpenTelemetry (OTel) for comprehensive observability. This includes distributed tracing, metrics export by using the OTLP protocol, and log correlation with trace context.
Apicurio Registry is built with OpenTelemetry support and all telemetry signals (traces, metrics, logs) are enabled at build time. However, the OpenTelemetry SDK is disabled by default at runtime. When enabled, Apicurio Registry exports telemetry data to an OpenTelemetry-compatible collector such as Jaeger, Grafana Tempo, or the OpenTelemetry Collector.
The individual signal properties (QUARKUS_OTEL_TRACES_ENABLED, QUARKUS_OTEL_METRICS_ENABLED, QUARKUS_OTEL_LOGS_ENABLED) are build-time properties and cannot be changed at runtime. All signals are already enabled in the Apicurio Registry build. Use QUARKUS_OTEL_SDK_DISABLED=false to enable telemetry at runtime.
|
| Before you configure OpenTelemetry, ensure that you have installed Apicurio Registry and have an OpenTelemetry-compatible backend available, for example, Jaeger, Grafana Tempo, or OpenTelemetry Collector. |
To enable OpenTelemetry observability, configure the following environment variables:
| Environment variable | Description |
|---|---|
|
Set to |
|
The endpoint URL of your OpenTelemetry collector. The port must match the exporter protocol: use |
|
The protocol that the OTLP exporter uses. Default is |
environment:
QUARKUS_OTEL_SDK_DISABLED: "false"
QUARKUS_OTEL_EXPORTER_OTLP_ENDPOINT: "http://jaeger:4317"
In production environments, configure trace sampling to reduce processing overhead and control the volume of trace data:
| Environment variable | Description |
|---|---|
|
The sampling strategy. Use |
|
The sampling ratio (0.0 to 1.0). A value of |
environment:
QUARKUS_OTEL_SDK_DISABLED: "false"
QUARKUS_OTEL_EXPORTER_OTLP_ENDPOINT: "http://otel-collector:4317"
QUARKUS_OTEL_TRACES_SAMPLER: "parentbased_traceidratio"
QUARKUS_OTEL_TRACES_SAMPLER_ARG: "0.1"
When you enable the OpenTelemetry SDK and use JSON logging format, Apicurio Registry automatically includes trace context (trace ID and span ID) in log entries. You can then correlate logs and traces.
environment:
QUARKUS_OTEL_SDK_DISABLED: "false"
QUARKUS_LOG_CONSOLE_JSON: "true"
OpenTelemetry features in Apicurio Registry
When OpenTelemetry is enabled, Apicurio Registry provides several observability features that are fully compatible with your existing monitoring setup.
-
Distributed tracing: All REST API requests are automatically traced with spans containing request details, path parameters, and Apicurio-specific attributes such as
groupId,artifactId, andversion. -
Storage layer tracing: All storage operations create child spans. You can trace the complete request flow from REST API to database.
-
Kafka tracing: When using KafkaSQL storage, Kafka operations are automatically traced with context propagation.
-
Custom metrics: OpenTelemetry metrics for artifact operations, schema validations, and search requests are exported alongside existing Prometheus metrics.
-
Log correlation: When JSON logging is enabled, trace context is automatically injected into log entries for correlation.
Existing monitoring endpoints continue to function:
-
The Prometheus metrics endpoint is available at
/metricson the management port (default 9000). -
Health check endpoints (
/health/ready,/health/live) are served on the management port (default 9000). -
All existing Micrometer-based metrics continue to function.
OpenTelemetry performance impact
OpenTelemetry instrumentation adds a small performance overhead. The following table shows the measured impact when all telemetry signals are enabled with 100% sampling:
| Operation | Latency increase | Throughput decrease | Impact level |
|---|---|---|---|
System Info |
+9% (+0.35ms) |
-7% |
Low |
Create Artifact |
+4% (+0.13ms) |
-1% |
Minimal |
Get Artifact |
+5% (+0.05ms) |
-6% |
Minimal |
Search Artifacts |
+1% (+0.04ms) |
-1% |
Minimal |
List Groups |
+6% (+0.16ms) |
-5% |
Low |
Key findings:
-
Average overhead is approximately 4-6% in throughput with 100% trace sampling.
-
With the recommended 10% sampling ratio (
QUARKUS_OTEL_TRACES_SAMPLER_ARG=0.1), the overhead drops to less than 1%. -
OpenTelemetry signals are disabled by default to avoid any overhead for users who do not require observability features.
To reproduce these benchmarks, run the following commands from the project root:
# Build the project
./mvnw clean install -DskipTests
# Run benchmark with OTEL disabled (baseline)
./mvnw test -pl app -Dtest=OpenTelemetryPerformanceTest \
-DOpenTelemetryPerformanceTest=enabled
# Run benchmark with OTEL enabled
./mvnw test -pl app -Dtest=OpenTelemetryPerformanceEnabledTest \
-DOpenTelemetryPerformanceTest=enabled
Changing the Apicurio Registry log level at runtime
You can change the log level of a running Apicurio Registry instance without restarting it by setting the dynamic apicurio.log.level configuration property. The setting applies to the Apicurio Registry loggers in the io.apicurio category.
-
You have administrator access to the Apicurio Registry instance.
-
Set the log level with the admin configuration REST endpoint. Valid values are
TRACE,DEBUG,INFO,WARN,ERROR,OFF, andALL. The default value isINFO:$ curl -X PUT -H "Content-Type: application/json" \ -d '{"value": "DEBUG"}' \ http://localhost:8080/apis/registry/v3/admin/config/properties/apicurio.log.levelAlternatively, use the CLI:
$ acr server-config set apicurio.log.level DEBUGThe registry applies the change within a few seconds. The value is stored in the registry storage, so it persists across restarts.
-
To revert to the original log level, remove the dynamic property. The registry restores the log level that was active at startup:
$ acr server-config reset apicurio.log.level
REST request metrics distribution configuration
You can control how Apicurio Registry publishes distribution statistics for its REST request timer metrics. Change the distribution type when your monitoring system computes percentiles poorly from histogram buckets, for example, Datadog.
| Property | Default | Description |
|---|---|---|
|
|
Distribution type for REST request timers. The |
|
|
Percentiles to pre-compute. Each value must be between 0.0 and 1.0. |
|
|
Service level objective boundaries in seconds for the histogram buckets. Only used when the distribution type is |
Storage content cache
Apicurio Registry keeps an in-memory, byte-weighted cache of immutable artifact content in front of the storage layer. The cache reduces storage round trips for reads that look up content by content ID or content hash, such as the Confluent-compatible schema lookup by ID that Kafka clients use.
The cache is enabled by default and applies to every storage variant because Apicurio Registry adds it as a storage decorator. The storage content cache is separate from HTTP caching, which sets response headers so that a reverse proxy or CDN can cache REST responses outside Apicurio Registry. The content cache works inside the Apicurio Registry process and also serves requests that HTTP caching does not cover, such as Confluent-compatible requests, which Apicurio Registry returns without cache headers.
Cached reads
A cache entry holds the content bytes, the artifact type, and the parsed references for one piece of content. Apicurio Registry caches only reads whose result depends solely on an immutable content identifier:
-
Content by content ID, for example,
GET /apis/registry/v3/ids/contentIds/{contentId}and its/referencesvariant. -
Content by SHA-256 content hash, for example,
GET /apis/registry/v3/ids/contentHashes/{contentHash}and its/referencesvariant. -
Content and artifact type by content ID, which serves the Confluent-compatible
GET /apis/ccompat/v7/schemas/ids/{id}andGET /apis/ccompat/v7/schemas/ids/{id}/schemarequests when legacy ID mode is disabled, which is the default.
Apicurio Registry does not cache reads that resolve content through a group, artifact, and version coordinate, or through a global ID, because that mapping can change over time.
Content bytes never change for a given content ID or content hash, so the content-by-ID and content-by-hash entries have no expiry. Apicurio Registry evicts them only when a cache exceeds its size limit. The content-and-type entry also depends on whether an artifact version still references the content, so it has a bounded staleness window.
Apicurio Registry coalesces concurrent misses for the same key into a single storage call. It never caches failed lookups, so a request for content that does not exist always reaches storage.
Bounded staleness window
Apicurio Registry returns 404 Not Found for a content-and-type lookup when the content is orphaned, that is, when no artifact version references it. Orphan status can change after Apicurio Registry caches the entry, most commonly when you delete the last version that references the content. To keep the hot path fast, Apicurio Registry accepts a short window during which a cached content-and-type entry can be stale:
-
Each content-and-type entry expires
apicurio.storage.content-cache.hot-path.ttl-secondsafter Apicurio Registry writes it. The default is 30 seconds. -
When you delete a single artifact version, Apicurio Registry resolves the content ID of that version before the delete and invalidates the matching content-and-type entry after the delete. If the lookup fails, the delete still completes and the entry expires through the TTL instead.
-
Other operations that can orphan content, such as deleting a whole artifact or group, importing data, or restoring a snapshot, do not invalidate entries. The TTL bounds the staleness for these cases.
As a result, a Confluent-compatible schema lookup by ID can still succeed for up to the TTL after a bulk operation removes the last referencing version. Set the TTL to 0 to disable the content-and-type cache and remove the window at the cost of a storage query on every lookup.
The scheduled orphaned content cleanup job, configured by apicurio.storage.orphan-cleanup.every, later removes orphaned content rows from storage. Content-by-ID and content-by-hash entries never expire, so a cached copy of removed content remains readable by content ID or content hash until Apicurio Registry evicts it or restarts.
Memory sizing
apicurio.storage.content-cache.max-size sets the maximum weight, in bytes, of each of the three caches separately, not of all caches together. The default is 64 MiB (67108864 bytes) for each cache, so the caches can hold up to about 192 MiB of content in total. The same content can be present in more than one cache, for example, when clients request it by content ID and by content hash.
| The configuration reference describes this property as a maximum total weight. The limit applies to each cache separately, so the total memory bound is the configured value multiplied by the number of caches. |
Apicurio Registry weighs each entry as the size of the content bytes plus 64 bytes of overhead and 128 bytes for each reference. The weight estimates the payload, not the JVM objects that hold it. When you plan the JVM heap and the container memory limit, count up to three times the configured max-size toward the memory that Apicurio Registry uses under sustained load, in addition to the baseline memory that your capacity planning requires.
To size the cache for your workload, compare the total size of the schemas that clients read most often with the per-cache limit. Use the cache metrics to confirm the result after a change.
Configuring the storage content cache
You can disable the storage content cache, change its size limit, or tune the staleness window for the content-and-type entry. Apicurio Registry reads these settings at startup, so you apply them by restarting Apicurio Registry with updated configuration properties or environment variables.
-
Apicurio Registry is installed and you can change its configuration properties or environment variables.
-
You can reach the Apicurio Registry management port,
9000by default, to read metrics.
-
Decide whether to keep the cache enabled. To disable it entirely, set
apicurio.storage.content-cache.enabledtofalse. The default value istrue. -
Set the per-cache size limit in bytes with
apicurio.storage.content-cache.max-size. The default value is67108864(64 MiB). For example, set134217728to allow 128 MiB in each of the three caches. -
Set the staleness window for the content-and-type entry with
apicurio.storage.content-cache.hot-path.ttl-seconds. The default value is30. Set0to disable that cache and query storage on every Confluent-compatible schema lookup by ID. -
Apply the settings and restart Apicurio Registry. Use configuration properties or the equivalent environment variables:
Example: Configuration propertiesapicurio.storage.content-cache.enabled=true apicurio.storage.content-cache.max-size=134217728 apicurio.storage.content-cache.hot-path.ttl-seconds=10Example: Environment variables in a container deploymentenvironment: APICURIO_STORAGE_CONTENT_CACHE_ENABLED: "true" APICURIO_STORAGE_CONTENT_CACHE_MAX_SIZE: "134217728" APICURIO_STORAGE_CONTENT_CACHE_HOT_PATH_TTL_SECONDS: "10"
These steps assume the default setting apicurio.ccompat.legacy-id-mode.enabled=false.
-
Request the same content twice by content ID. Replace
1with a content ID that exists in your registry:$ curl -s http://localhost:8080/apis/registry/v3/ids/contentIds/1 > /dev/null $ curl -s http://localhost:8080/apis/registry/v3/ids/contentIds/1 > /dev/null -
Read the cache metrics from the management port and filter for the content caches:
$ curl -s http://localhost:9000/metrics | grep 'cache="content' -
Confirm the behavior that matches your configuration:
-
When the cache is enabled,
cache_gets_total{cache="contentById",result="hit"}increases on the second request andcache_size{cache="contentById"}is at least1. -
When the cache is disabled, the output contains no series with a
cache="contentById",cache="contentByHash", orcache="contentAndTypeById"tag. -
When the TTL is
0, thecache="contentAndTypeById"series are absent while the other two caches still report metrics.
-
Storage content cache properties and metrics
The storage content cache has three configuration properties, three named caches, and five Micrometer meters for each cache, exposed at /metrics on the management port. When apicurio.storage.content-cache.hot-path.ttl-seconds is 0, Apicurio Registry does not create the contentAndTypeById cache and does not register its meters. The properties are available since Apicurio Registry 3.3.3.
| Property | Environment variable | Default | Description |
|---|---|---|---|
|
|
|
Enables the storage content cache. When |
|
|
|
Maximum weight, in bytes, of each of the three caches. The limit applies to each cache separately, so the caches can hold up to three times this value in total. |
|
|
|
Number of seconds after which a content-and-type entry expires. Set |
Cache name (cache tag) |
Key | Expiry | Serves |
|---|---|---|---|
|
Content ID |
None. Apicurio Registry evicts entries only when the cache exceeds its size limit. |
Content and stored reference metadata by content ID, including the REST API |
|
SHA-256 content hash |
None. Apicurio Registry evicts entries only when the cache exceeds its size limit. |
Content and references by content hash, including the REST API |
|
Content ID |
|
The Confluent-compatible |
| Micrometer meter | Prometheus name | Tags | Description |
|---|---|---|---|
|
|
|
Approximate number of entries currently in the cache. |
|
|
|
Number of lookups that returned a cached value ( |
|
|
|
Number of loads from storage, including lookups that failed because the content does not exist. A failed load does not add an entry. |
|
|
|
Number of entries removed by size-based eviction or expiry. The count excludes invalidations after a version delete. |
|
|
|
Total weight, in bytes, of the evicted entries. The count excludes invalidations after a version delete. |
Reading the metrics
-
The hit ratio for a cache is
hit / (hit + miss)fromcache_gets_total. A high ratio means that most reads avoid a storage round trip. -
For
contentByIdandcontentByHash, steadily increasing evictions and misses can indicate that the working set exceeds the per-cache limit. Consider increasingapicurio.storage.content-cache.max-sizeif memory allows. -
For
contentAndTypeById, TTL expirations also count as evictions and cause misses on subsequent requests. Confirm that the size limit causes the evictions before increasingmax-size; more capacity does not prevent TTL expiry. -
When the hit ratio stays high with few evictions, a smaller limit reclaims memory without affecting throughput.
-
When the cache is disabled, or when the TTL is
0for thecontentAndTypeByIdcache, Apicurio Registry does not register the corresponding series.
Configuring Apicurio Registry health checks on OpenShift
Health check endpoints are served on the management interface (port 9000 by default), separate from the main application port (8080). Configure liveness and readiness probes to target port 9000.
You can configure optional environment variables for liveness and readiness probes to monitor the health of the Apicurio Registry server on OpenShift:
-
Liveness probes test if the application can make progress. If the application cannot make progress, OpenShift automatically restarts the failing Pod.
-
Readiness probes test if the application is ready to process requests. If the application is not ready, it can become overwhelmed by requests, and OpenShift stops sending requests for the time that the probe fails. If other Pods are OK, they continue to receive requests.
| The default values of the liveness and readiness environment variables are designed for most cases. Change these values only if required by your environment. Any changes to the defaults depend on your hardware, network, and amount of data stored. Keep these values as low as possible to avoid unnecessary processing overhead. |
-
You must have an OpenShift cluster with cluster administrator access.
-
You must have already installed Apicurio Registry on OpenShift.
-
You must have already installed and configured your chosen Apicurio Registry storage in either Strimzi or PostgreSQL.
-
In the OpenShift Container Platform web console, log in using an account with cluster administrator privileges.
-
Click Installed Operators > Apicurio Registry.
-
On the ApicurioRegistry tab, click the Operator custom resource for your deployment, for example, example-apicurioregistry.
-
In the main overview page, find the Deployment Name section and the corresponding
DeploymentConfigname for your Apicurio Registry deployment, for example, example-apicurioregistry. -
In the left navigation menu, click Workloads > Deployment Configs, and select your
DeploymentConfigname. -
Click the Environment tab, and enter your environment variables in the Single values env section, for example:
-
NAME:
LIVENESS_STATUS_RESET -
VALUE:
350
-
-
Click Save at the bottom.
Alternatively, you can complete these steps by using the OpenShift
occommand. For more details, see the OpenShift CLI documentation.
Environment variables for Apicurio Registry health checks
You can use the following environment variables to configure Apicurio Registry health checks on OpenShift. These variables control liveness and readiness probes that monitor the health of the Apicurio Registry server.
| The following environment variables are provided for reference only. The default values are designed for most cases. Change these values only if required by your environment. Any changes to the defaults depend on your hardware, network, and amount of data stored. Keep these values as low as possible to avoid unnecessary processing overhead. |
| Name | Description | Type | Default |
|---|---|---|---|
|
Number of liveness issues or errors that can occur before the liveness probe fails. |
Integer |
|
|
Period in which the threshold number of errors must occur. For example, if this value is 60 and the threshold is 1, the check fails after two errors occur in 1 minute. |
Seconds |
|
|
Number of seconds that must elapse without any more errors for the liveness probe to reset to OK status. |
Seconds |
|
|
Comma-separated list of ignored liveness exceptions. |
String |
|
| Because OpenShift automatically restarts a Pod that fails a liveness check, the liveness settings, unlike readiness settings, do not directly affect behavior of Apicurio Registry on OpenShift. |
| Name | Description | Type | Default |
|---|---|---|---|
|
Number of readiness issues or errors that can occur before the readiness probe fails. |
Integer |
|
|
Period in which the threshold number of errors must occur. For example, if this value is 60 and the threshold is 1, the check fails after two errors occur in 1 minute. |
Seconds |
|
|
Number of seconds that must elapse without any more errors for the readiness probe to reset to OK status. This value determines how long the Pod remains not ready before it returns to normal operation. |
Seconds |
|
|
Readiness tracks the timeout of two operations:
If either operation exceeds the configured timeout, Apicurio Registry reports a readiness issue or error. This value controls the timeouts for both operations. |
Seconds |
|
