All Settings
All settings live under the "FileStorage" configuration section and are bound to FileStorageSettings via services.Configure<FileStorageSettings>(config.GetSection("FileStorage")).
| Key | Type | Default | Required | Description |
BucketName | string | bizfirst-files | Yes | Name of the S3/MinIO bucket. Must exist before first use. |
Endpoint | string | blank | For MinIO/R2/B2 | Full URL to the S3-compatible endpoint. Leave blank for AWS S3 (uses RegionEndpoint). |
AccessKey | string | — | Yes | S3 access key ID. For MinIO: root user or service account key. |
SecretKey | string | — | Yes | S3 secret access key. Store in a secrets manager — never in appsettings.json for production. |
Region | string | us-east-1 | Yes | AWS region name. For MinIO, any valid region string works (e.g. us-east-1). For R2, use auto. |
ForcePathStyle | bool | true | — | Must be true for MinIO, R2, B2. Set false for AWS S3 (uses virtual-hosted style). |
PrependTenantID | bool | false | — | When true, storage key becomes tenant_{tenantID}/{cid}. Enables per-tenant isolation and prefix-level IAM policies. |
CannedAcl | string | NoACL | — | Default ACL applied to uploaded objects. Overridable per request via StoreOptions.CannedAcl. |
StorageClass | string | STANDARD | — | Default S3 storage class. Overridable per request via StoreOptions.StorageClass. |
WriteStopThresholdPercent | double | 90.0 | — | Block writes when disk usage reaches this percentage. Set 0 to disable the capacity guard. |
Environment Variable Names
ASP.NET Core maps double-underscore (__) to section separator. Any setting can be overridden via environment variable:
| Environment Variable | Maps to | Example Value |
FileStorage__BucketName | FileStorage:BucketName | bizfirst-files |
FileStorage__Endpoint | FileStorage:Endpoint | http://minio:9000 |
FileStorage__AccessKey | FileStorage:AccessKey | your-access-key |
FileStorage__SecretKey | FileStorage:SecretKey | your-secret-key |
FileStorage__Region | FileStorage:Region | us-east-1 |
FileStorage__ForcePathStyle | FileStorage:ForcePathStyle | true |
FileStorage__PrependTenantID | FileStorage:PrependTenantID | true |
FileStorage__CannedAcl | FileStorage:CannedAcl | NoACL |
FileStorage__StorageClass | FileStorage:StorageClass | STANDARD |
FileStorage__WriteStopThresholdPercent | FileStorage:WriteStopThresholdPercent | 90.0 |
Kubernetes Secret example
apiVersion: v1
kind: Secret
metadata:
name: bizfirst-storage-secrets
type: Opaque
stringData:
FileStorage__AccessKey: "your-access-key"
FileStorage__SecretKey: "your-secret-key"
Per-Backend Configuration Examples
MinIO — Local Development
{
"FileStorage": {
"BucketName": "bizfirst-files",
"Endpoint": "http://localhost:9000",
"AccessKey": "admin",
"SecretKey": "admin123",
"Region": "us-east-1",
"ForcePathStyle": true,
"PrependTenantID": false,
"WriteStopThresholdPercent": 0
}
}
MinIO — Production (Docker)
{
"FileStorage": {
"BucketName": "bizfirst-files",
"Endpoint": "http://minio:9000",
"Region": "us-east-1",
"ForcePathStyle": true,
"PrependTenantID": true,
"CannedAcl": "NoACL",
"StorageClass": "STANDARD",
"WriteStopThresholdPercent": 90.0
}
}
// AccessKey and SecretKey injected via environment variables
AWS S3
{
"FileStorage": {
"BucketName": "my-bizfirst-prod-files",
"Endpoint": "",
"Region": "us-east-1",
"ForcePathStyle": false,
"PrependTenantID": true,
"CannedAcl": "NoACL",
"StorageClass": "STANDARD",
"WriteStopThresholdPercent": 0
}
}
// AccessKey and SecretKey from IAM role / environment variables
Cloudflare R2
{
"FileStorage": {
"BucketName": "bizfirst-files",
"Endpoint": "https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com",
"Region": "auto",
"ForcePathStyle": true,
"PrependTenantID": true,
"WriteStopThresholdPercent": 0
}
}
Backblaze B2
{
"FileStorage": {
"BucketName": "bizfirst-files",
"Endpoint": "https://s3.us-west-004.backblazeb2.com",
"Region": "us-west-004",
"ForcePathStyle": true,
"PrependTenantID": true,
"WriteStopThresholdPercent": 0
}
}
DI Registrations
Calling services.AddObjectStorage(configuration) registers the following:
| Interface | Implementation | Lifetime | Notes |
IOptions<FileStorageSettings> | FileStorageSettings | Singleton | Bound from "FileStorage" config section |
IHttpClientFactory | DefaultHttpClientFactory | Singleton | Used by MinioCapacityGuard to call Prometheus endpoint |
IS3ObjectService | S3ObjectService | Scoped | Registered via AddS3Services() |
IS3BucketService | S3BucketService | Scoped | Registered via AddS3Services() |
IS3FolderService | S3FolderService | Scoped | Registered via AddS3Services() |
ICidProvider | Sha256CidProvider | Singleton | Stateless pure computation — safe as Singleton |
IStorageCapacityGuard | MinioCapacityGuard | Scoped | Depends on IHttpClientFactory and IOptions |
IObjectStorageProvider | S3ObjectStorageProvider | Scoped | Depends on session context, IS3ObjectService, and all above |
StoreOptions — Per-Request Overrides
public sealed record StoreOptions(
string? CannedAcl = null, // null = use FileStorage:CannedAcl global default
string? StorageClass = null); // null = use FileStorage:StorageClass global default
| Property | Null behaviour | Override example |
CannedAcl | Uses FileStorage:CannedAcl (default NoACL) | "private", "public-read" |
StorageClass | Uses FileStorage:StorageClass (default STANDARD) | "STANDARD_IA", "GLACIER" |
Valid Values
CannedAcl valid values
| Value | Meaning | MinIO support |
NoACL | No ACL — bucket policy governs access | Yes (recommended) |
private | Owner full control, others no access | Yes |
public-read | Public read, owner write | Yes |
bucket-owner-full-control | Bucket owner full control | Yes |
StorageClass valid values
| Value | Description | MinIO | AWS S3 | R2 |
STANDARD | Hot storage — default | Yes | Yes | Yes |
REDUCED_REDUNDANCY | Lower durability, lower cost | Yes | Yes | No |
STANDARD_IA | Infrequent access — lower storage cost, retrieval fee | Via tier | Yes | No |
INTELLIGENT_TIERING | Auto-tier based on access patterns | No | Yes | No |
GLACIER | Archival — minutes to hours retrieval | No | Yes | No |
DEEP_ARCHIVE | Deep archival — 12 hour retrieval | No | Yes | No |