Storage providers
Configure the filesystem, S3 or Azure storage provider. · 4 min
dedup4j ships three storage adapters and an SPI for writing your own. All three adapters arrive with the starter; configuration selects one, and the others stay inert.
dedup4j:
storage:
provider: local # local | s3 | azure
Unselected providers do nothing
An adapter that is not selected constructs no client and resolves no
credentials. Having dedup4j-storage-s3 on the classpath does not mean
your application contacts AWS, and a missing AWS credential file is not an
error when provider is local.
This is a design commitment. It is verified from an external consumer project at release gate G6 rather than asserted here.
Local filesystem
For development, tests, and single-node deployments.
dedup4j:
storage:
provider: local
local:
root-directory: ./dedup4j-storage
| Property | Meaning |
|---|---|
root-directory | Directory holding stored blobs |
Intended for a single node
Nothing in the adapter prevents multi-node use, but two instances with separate filesystems share one database and two different sets of bytes. The database can say content exists while the disk of the node handling the request does not have it. Use object storage for anything multi-node, or give every node the same shared mount.
Amazon S3
Also works with S3-compatible stores — MinIO, Cloudflare R2, Backblaze B2 —
via endpoint.
dedup4j:
storage:
provider: s3
s3:
bucket: my-application-blobs
region: eu-west-1
| Property | Meaning |
|---|---|
bucket | Target bucket. Must already exist |
region | AWS region |
endpoint | Custom endpoint for S3-compatible stores |
path-style | true for MinIO and most S3-compatible stores |
Credentials
There is no credentials property, deliberately. dedup4j uses the AWS SDK's default credential provider chain — environment variables, system properties, the shared credentials file, then container and instance metadata.
That means in production you attach an IAM role and configure nothing. A
library that accepted access-key and secret-key as configuration would
invite them into application.yaml, and from there into version control.
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_REGION=eu-west-1
MinIO or other S3-compatible
dedup4j:
storage:
provider: s3
s3:
bucket: blobs
region: us-east-1
endpoint: http://localhost:9000
path-style: true
path-style: true is required by most S3-compatible stores, which do
not implement virtual-host-style bucket addressing.
Azure Blob Storage
dedup4j:
storage:
provider: azure
azure:
container: application-blobs
endpoint: ${AZURE_BLOB_SAS_URL} # or connection-string
container plus one of endpoint or connection-string is required.
With only container and account-name, startup fails with
connectionString or endpoint must be configured.
| Property | Meaning |
|---|---|
container | Target container. Must already exist |
endpoint | Service URL. dedup4j sets no credential, so it must carry a SAS token (or point at Azurite). Overrides the endpoint inside connection-string when both are set |
connection-string | Full connection string, with an account key or a SAS |
account-name | Accepted but not used in 0.1.0 |
Authentication
dedup4j 0.1.0 does not configure a managed identity or any other Azure credential for you. Three ways work:
connection-string: simplest; fine against Azurite.endpointwith a SAS token in its query string.- Your own client. Declare a
BlobContainerClientbean and dedup4j uses it instead of building one. This is how to use a managed identity today:
@Bean
BlobContainerClient blobContainerClient() {
return new BlobServiceClientBuilder()
.endpoint("https://mystorageaccount.blob.core.windows.net")
.credential(new DefaultAzureCredentialBuilder().build()) // azure-identity
.buildClient()
.getBlobContainerClient("application-blobs");
}
container is still required when you supply your own client.
Connection strings and SAS URLs are credentials
Both embed a secret in plaintext. Supply them from the environment or a
secret store, never from a committed application.yaml.
Custom providers
Implement BlobStorage from dedup4j-core — four methods:
public interface BlobStorage {
StoredBlob put(PutBlobRequest request);
BlobResource get(String objectKey);
void delete(String objectKey);
boolean exists(String objectKey);
}
Register it as a bean and the built-in adapters step aside:
@Configuration
public class GcsStorageConfiguration {
@Bean
BlobStorage blobStorage() {
return new GcsBlobStorage(...);
}
}
Your adapter handles bytes only. Content hashing, deduplication, reference counting, and metadata stay in dedup4j — an adapter is a transport, not a participant in the dedup decision.
Contract notes:
getreturns aBlobResourceholding an openInputStream; the caller closes it.existsshould be cheap — a metadata probe, not a download.putmust be safe to call with content that already exists at that key. Deduplication reduces how often that happens; it does not guarantee it never will.
Comparison
| Local | S3 | Azure | |
|---|---|---|---|
| Multi-node | ❌ | ✅ | ✅ |
| Credentials | none | default chain | connection string, SAS endpoint, or your own client |
| Custom endpoint | — | ✅ | ✅ |
| Best for | development, tests | production | production on Azure |
Next
- Uploading & deduplication — what happens on
store - Configuration properties — every key
- Troubleshooting — misconfiguration symptoms