13.17.1Externalized Resource Body Storage
Experimental

 
This feature is currently provided as an early preview for testing, and will be changed in upcoming releases. Do not use this feature in production environments.

FHIR resource bodies (JSON payloads) are stored by default in the database. However, in systems that manage high volumes of resources or require more cost-effective storage, it is possible to externalize resource body storage to an object storage system such as Amazon S3, MinIO, or Azure Blob Storage. This is especially useful for high-volume databases, enabling up to 20% storage savings depending on the selected resource body offload mode and the number of resource bodies stored in the database. Moving resource bodies to external storage introduces additional I/O overhead whenever the offloaded bodies need to be read back, including search results, bulk exports, and any scenario that requires resource body content retrieval.

Smile CDR provides two options to allow offloading of the resource bodies:

  • Real-time Resource Body Offload which enables offloading for newly created and updated resources. Existing resources remain unchanged. This sets the default storage policy for all future updates. Note that this feature operates on a best-effort basis and may not offload bodies during transient network failures or during process shutdown (e.g., Kubernetes pod termination).
  • Batch job triggered by Resource Body Offload Operation that offloads resource bodies for existing resources. This can be used to migrate historical data, for example, as part of an archiving strategy. This job is durable, retries on transient errors, and will report failures in the job if storage changes cannot be completed.

13.17.2Real-time Resource Body Offload
Experimental

 

To enable Real-time resource body offload it is necessary to configure the parameters listed below:

The following sections describe options for resource body offload:

13.17.2.1Resource Body Offload Mode: OFF (Default)

By default, saving the resource body to external storage is disabled. All resource body content is stored in the relational database.

13.17.2.2Resource Body Offload Mode: SAVE

In this mode, all resource body content continues to be stored in the database. Additionally, each newly created or updated resource is also saved to the configured external object storage system (e.g., S3, MinIO, or Azure Blob Storage). Resource bodies are not removed from the database, and the external storage acts as an archive. This setup allows the FHIR server to operate entirely from the database for reads, updates, searches, and retrieving historical versions of resources. It can serve as a foundation for future migration to offload modes that reduce database size. Since all read operations are executed against the database, query performance remains unchanged.

13.17.2.3Resource Body Offload Mode: SAVE_AND_CLEAR_OLD

In this mode, all older versions of a resource (i.e., versions prior to the latest version) are moved to the configured external object storage system (e.g., S3, MinIO, or Azure Blob Storage) and removed from the relational database. The latest version is saved to external storage as well, but it remains in the database. This setup reduces database size while preserving quick access to the most recent resource body version. Historical versions are retrieved on demand from external storage when needed (e.g., via the _history operation). This mode offers an optimal balance between minimizing database storage and maintaining high performance for read, update, and search operations. Query performance remains unchanged, but historical reads will be slower, as they require fetching resource bodies from external storage.

13.17.2.4Resource Body Offload Mode: SAVE_AND_CLEAR_ALL

In this mode, all resource body versions, including the latest, are moved to the configured external object storage system (e.g., S3, MinIO, or Azure Blob Storage), and none are stored in the database. Only resource metadata remains in the relational database, significantly reducing its size. All resource bodies must be retrieved from external storage when needed, including for reads, updates, or historical access. This mode offers maximum storage efficiency and is best suited for systems where minimizing database storage is a priority and latency introduced by external storage is acceptable. Note that read, update, and search operations may be slower due to network overhead and object storage performance.

The following sections describe options for resource body storage:

13.17.2.5Resource Body Storage Target Mode: Database (Default)

By default, all resource body content is stored in the database, including all historical versions as well as resource metadata.

This provides efficient reads, writes, and searches. However, as the number of resources and versions grows, database size can increase significantly.

13.17.2.6Resource Body Storage Target Mode: AWS S3

In this mode, FHIR resource bodies are stored in an AWS S3 bucket. Resource bodies will be stored in one bucket, which can be named via the Blob Service Bucket / Container property. You can also configure the region by setting the Blob Service Region property. On boot, Smile CDR will create a bucket if it does not already exist.

Authentication to S3 is done using the DefaultAwsCredentialsProviderChain. This means that credentials can be provided in a variety of ways, including:

  • Environment Variables
  • Java System Properties
  • Credential Profiles File

However, you also have the option to provide your own credentials via the Blob Service S3 Access Key property and the Blob Service S3 Secret Key property. If credentials are provided in this fashion, they will be used instead of the default credentials.

The account you authenticate with will need permissions to create buckets, as well as to put/head/get/delete objects in the bucket.

13.17.2.7Resource Body Storage Target Mode: MinIO

In this mode, resource bodies are stored in a MinIO server. All data will be stored in one bucket, which can be named via the Blob Service Bucket / Container property.

Authentication for MinIO must be provided via the Blob Service S3 Access Key and Blob Service S3 Secret Key properties.

Currently, MinIO is only recommended for development purposes.

13.17.2.8Resource Body Storage Target Mode: Azure Blob Storage

In this mode, resource bodies are stored in an Azure Blob Storage container. All data will be stored in one container, which can be named via the Blob Service Bucket / Container property. You can also configure the account name by setting the Blob Service Azure Account property. On boot, Smile CDR will create a container if it does not already exist.

There are three different supported authentication methods:

  1. Access Key
  2. Account-level Shared Access Signatures (SAS) token
  3. Azure Active Directory

The authenticated account will need permissions to create containers, as well as to PUT/HEAD/GET/DELETE objects in the container.

13.17.2.9Blob Service Path Prefix For Resource Body Storage

When resource bodies are stored in an external object storage system (e.g., S3, MinIO, or Azure Blob Storage), each version is written using the following path format:

{resource_type}/{fhir_id}/_history/{resource_version}

Example Path:

Patient/1706/_history/5

This corresponds to storing the fifth version of a Patient resource with FHIR ID 1706. A mandatory Blob Service Path Prefix is required to be configured to prepend a logical folder structure to all object keys. For example, if the prefix is set to fhir-resource-body/dev, the full object key becomes:

fhir-storage/dev/Patient/1706/_history/5

This is required as the storage bucket for resource bodies is shared with Externalized Binary Storage.

13.17.3Resource Body Offload Operation
Experimental

 

The $sdh.resource-body-offload operation can be used to trigger a batch job that offloads existing resource bodies to external storage. The batch job runs in the background and processes resource bodies based on input search criteria, offload mode, and last updated timestamp. This can be used to implement an archival policy driven by resource age, version history, resource type, or other criteria.

To enable the $sdh.resource-body-offload operation it is necessary to configure parameters listed below:

13.17.3.1Input Parameters

The $sdh.resource-body-offload operation accepts the following parameters:

  • url (optional): Search URLs to filter which resources should be offloaded (e.g., "Patient?active=true" or "Observation?"). Multiple URLs can be provided to offload different resource types or subsets. If no URLs are provided, all resources of all types will be offloaded.
  • offloadMode (required): Offload mode to use for this batch job. Valid values are:
    • SAVE_AND_KEEP_ALL - Save all resource body versions to external storage and ensure all versions remain in the database.
    • SAVE_AND_CLEAR_OLD - Save all resource body versions to external storage and ensure only the latest version remains in the database.
    • SAVE_AND_CLEAR_ALL - Save all resource body versions to external storage and remove all versions from the database.
  • _since (optional): Only offload resources that were last updated on or after this timestamp.
  • _until (optional): Only offload resources that were last updated before this timestamp.

13.17.3.2Offload Modes

The $sdh.resource-body-offload operation supports the following offload policies:

13.17.3.2.1`SAVE_AND_KEEP_ALL`

In this mode:

  • All resource bodies currently stored in the database are saved (copied) to external storage
  • All resource bodies that are already externalized (stored only in external storage, removed from database) are restored back to the database

This mode is useful for migration scenarios where you want to restore previously offloaded resource bodies back to the database for faster query performance, or for creating redundant backups. Resource bodies are not removed from the database, and the external storage acts as an archive.

13.17.3.2.2`SAVE_AND_CLEAR_OLD`

In this mode:

  • All resource bodies currently stored in the database are saved (copied) to external storage
  • All historical resource body versions (excluding the latest) are removed from the database
  • If the latest version was previously externalized(stored only in external storage, removed from database), it is restored back to the database

This offload mode reduces database size while preserving quick access to the most recent resource body version. Historical versions are retrieved on demand from external storage as needed (e.g., via the _history operation) This mode offers a good balance between minimizing database storage and maintaining high performance for read, update, and search operations. It is especially useful for resources with many historical versions, such as ward Location resources, or frequently modified, large Group resources.

13.17.3.2.3`SAVE_AND_CLEAR_ALL`

In this mode:

  • All resource body versions currently stored in the database are saved (copied) to external storage
  • All resource body versions (including the latest version) are removed from the database

In this mode, all resource body versions, including the latest, are moved to the configured external object storage system (e.g., S3, MinIO, or Azure Blob Storage), and none are stored in the database. Only resource metadata remains in the relational database, significantly reducing its size. All resource bodies must be retrieved from external storage when needed, including for reads, updates, or historical access. This mode offers maximum storage efficiency and is best suited for systems where minimizing database storage is a priority and latency introduced by external storage is acceptable. Note that read, update, and search operations may be slower due to network overhead and object storage performance. This can be applied to historical data that is unlikely to be queried.

13.17.3.3Request Example

POST {fhir_endpoint}/$sdh.resource-body-offload
Content-Type: application/fhir+json

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "url",
      "valueString": "Patient?active=true"
    },
    {
      "name": "offloadMode",
      "valueString": "SAVE_AND_CLEAR_OLD"
    },
    {
      "name": "_since",
      "valueInstant": "2024-01-01T00:00:00Z"
    },
    {
      "name": "_until",
      "valueInstant": "2024-12-31T23:59:59Z"
    }
  ]
}

13.17.3.4Response Example

The operation returns a Parameters resource containing the batch job instance ID:

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "jobId",
      "valueString": "550e8400-e29b-41d4-a716-446655440000"
    }
  ]
}

The returned job ID can be used to monitor the progress of the offload operation using the standard Batch2 job monitoring endpoints.

13.17.3.5Externalizing and Restoring Deleted Resources

Deleted resources may be externalized or restored by adding the _includeDeleted parameter to the url field in the Parameters resource. In this case, offloading policies do not affect the latest version and apply only to historical versions of the deleted resource. The _includeDeleted parameter accepts 3 different values:

  • never – (default) the search for resources will include only non-deleted resources
  • exclusive – the search for resources will include only deleted resources
  • both – the search for resources will include both deleted and non-deleted resources

13.17.3.5.1Limitations

Due to how deleted resources are stored, the _includeDeleted parameter can only be combined with the _id and _lastUpdated parameters in the URL when resource type is provided. When resource type is not provided (a request to reindex all resources), only the _lastUpdated parameter may be combined with _includeDeleted.

13.17.3.5.2Examples

Including the following url in the parameters will externalize or restore resource bodies for all deleted patients.

{
    "name": "url",
    "valueString": "Patient?_includeDeleted=exclusive"
}

Including the following url in the parameters will externalize or restore resource bodies for all deleted resources.

{
    "name": "url",
    "valueString": "?_includeDeleted=exclusive"
}

13.17.4Performance And Storage Implications

 

When enabling externalized resource body storage, consider the following operational impacts:

  • Moving resource bodies to external storage introduces additional database load and processing overhead. Each create or update operation requires both saving to the database and writing to external storage, which may increase overall system load.
  • When a resource body is externalized, it is first written to the database and then saved to external storage. Depending on the database engine, the space used by the removed data may not be reclaimed immediately. Additional maintenance (e.g., VACUUM/ VACUUM FULL operations for PostgreSQL etc.) may be required to recover disk space.

13.17.5Limitations

 
  • Externalizing resource bodies is only supported for relational database backends (e.g., PostgreSQL, MSSQL, Oracle). MongoDB is not supported for this feature.
  • During search operations in SAVE_AND_CLEAR_ALL mode, resource bodies are retrieved sequentially from external storage. As this process is not asynchronous, it may result in slower search performance compared to modes where resource bodies are stored in the database.

13.17.6Implementation Roadmap

 

Externalized Resource Body Storage functionality is under active development. Below is a roadmap of planned features.

13.17.6.1February 2026 Release

  • Asynchronous reading of resource bodies during search to improve performance when the latest resource bodies are stored externally.