Reference
Configuration
Everything the driver reads: The S3A_* variables on its container, the volumeAttributes on a volume, and the rules for combining them.
Environment (driver container)#
| Variable | Default | Meaning |
|---|---|---|
CSI_ENDPOINT | unix:///csi/csi.sock | Socket the driver listens on. Unix domain sockets only; a tcp:// value is rejected at startup. |
S3A_STATE_DIR | /var/lib/kubelet/plugins/…/state | Where per-volume state records and upload manifests live. Must be on a host path that outlives the driver container, or a restart re-uploads everything. |
NODE_NAME | hostname | The node id reported by NodeGetInfo. Set it from the downward API; the hostname fallback exists for local runs and logs a warning. |
S3A_LOG_LEVEL | info | Global log threshold: trace, debug, info, warn, error, off. |
S3A_LOG_LEVELS | unset | Per-logger overrides, comma-separated prefix=level (for example io.grpc=warn,de.trion.csi=debug). Longest matching prefix wins. |
S3A_NODE_CONCURRENCY | 4 | Concurrent uploads across all volumes on the node. A file is never uploaded concurrently with itself. |
S3A_RESCAN_SECONDS | 60 | Interval of the full rescan that backs up the inotify watcher. |
S3A_LOG_FORMAT | text | text for one human-readable line per record, json for one JSON object per line. JSON carries the full logger name and renders a throwable as fields, because a record that spans lines is not a JSON record. |
S3A_METRICS_PORT | unset (off) | Serve Prometheus metrics at /metrics. Off by default: A node plugin running as root should not open a listening port because someone upgraded it. Not 9808, which the livenessprobe sidecar already holds. |
S3A_WEBUI_PORT | unset (off) | Serve the read-only statistics page. Setting the port is what enables it; there is no separate switch. |
S3A_WEBUI_RETENTION_DAYS | 7 | How much hourly history each node keeps for the statistics page. 1 to 90. |
S3A_WEBUI_PEERS | unset (this node only) | Bare DNS name of a headless Service resolving to every driver pod, so any pod can render the whole cluster. Plain DNS rather than the Kubernetes API, which is what keeps the driver without permissions. |
S3A_CONTROLLER | false | Also serve the Controller service on the node socket. For csi-sanity; in a cluster the controller runs in its own pod. |
volumeAttributes#
Set on the pod's inline CSI volume. Every attribute is validated at publish time: An unknown name, a malformed value or a contradictory combination fails the pod with a message on the pod's events, rather than producing a bucket that quietly stays empty.
| Attribute | Default (env override) | Meaning |
|---|---|---|
bucket | required (S3A_BUCKET) | Target bucket, from either place. |
prefix | {namespace}/{podName}/ (S3A_PREFIX) | Key prefix template. See placeholders below. |
endpoint | AWS (S3A_ENDPOINT) | S3 endpoint override for Ceph, SeaweedFS and the like. |
region | us-east-1 (S3A_REGION) | Region. |
pathStyle | false (S3A_PATH_STYLE) | Force path-style addressing; most non-AWS stores need it. |
appendOnly | false (S3A_APPEND_ONLY) | Periodically sync growing files instead of waiting for them to go quiet. |
deleteAfterUpload | false (S3A_DELETE_AFTER_UPLOAD) | Delete the local file after a successful upload. Mutually exclusive with appendOnly. |
quiescenceSeconds | 30 (S3A_QUIESCENCE_SECONDS) | How long a file must be unmodified before it is uploaded. |
appendSyncSeconds | 60 (S3A_APPEND_SYNC_SECONDS) | Sync interval in appendOnly mode. |
appendStrategy | rewrite (S3A_APPEND_STRATEGY) | rewrite re-uploads the whole file per sync; segments uploads only new tail bytes and compacts at pod end. |
segmentTargetBytes | 8388608 (S3A_SEGMENT_TARGET_BYTES) | segments: Flush the pending tail at this size. |
include / exclude | all / none (S3A_INCLUDE, S3A_EXCLUDE) | Comma-separated glob filters on paths inside the volume. |
storageClass | bucket default (S3A_STORAGE_CLASS) | For example STANDARD_IA. |
sweepTimeoutSeconds | unlimited (S3A_SWEEP_TIMEOUT_SECONDS) | Bound for the final sweep at pod termination. |
presignEndpoint | unset (S3A_PRESIGN_ENDPOINT) | Signer service URL; switches the volume to presigned-upload mode. |
presignMultipart | false (S3A_PRESIGN_MULTIPART) | Presigned mode: Upload large files in parts, lifting the 5 GiB single-PUT cap. Needs a signer that signs the multipart operations, and is opt-in because a putObject-only signer answers them plausibly and wrongly. |
compression | none (S3A_COMPRESSION) | gzip compresses each object and appends .gz to the key. zstd is refused with a reason: It needs a per-architecture native library, which a static binary does not have. |
serverSideEncryption | none (S3A_SERVER_SIDE_ENCRYPTION) | AES256 or aws:kms, with sseKmsKeyId. A KMS key without KMS mode is a hard error rather than a silently ignored setting. |
sseKmsKeyId | none (S3A_SSE_KMS_KEY_ID) | The KMS key id or ARN to encrypt with. Only meaningful alongside serverSideEncryption: aws:kms; setting it without that mode is a hard error rather than a silently ignored value. |
sizeLimitBytes | unlimited (S3A_SIZE_LIMIT_BYTES) | Reported as the volume capacity by NodeGetVolumeStats, and an overrun is logged. Not enforced by deletion: The driver will not delete a workload's files to stay under a limit. |
appendUpload | auto (S3A_APPEND_UPLOAD) | How a growing object is extended: copy uses UploadPartCopy, offset uses x-amz-write-offset-bytes and needs an S3 Express directory bucket, off re-uploads the whole file. Only in appendOnly mode. |
durable | false (S3A_DURABLE) | The volume outlives its pod: Restored from the archive at publish, archived and mirrored at unpublish. PVC-declared volumes only, and credential mode only. |
restoreTimeoutSeconds | unlimited (S3A_RESTORE_TIMEOUT_SECONDS) | Bounds how long a pod start may block on a durable restore. Past it the pod gets what arrived and the driver logs that the volume is incomplete. |
appendSyncSeconds, appendStrategy and segmentTargetBytes only mean anything with
appendOnly: "true", so setting one on a volume without it is rejected. As a driver-global
S3A_* default they are fine: They simply apply to the volumes that do use appendOnly.
Prefix placeholders#
prefix may contain {namespace}, {podName}, {podUid}, {serviceAccount}, {nodeName},
{volumeId} and {date} (UTC yyyy-MM-dd at upload time). Anything else is an error rather
than a literal:
$ kubectl describe pod heapdump-example
Events:
Type Reason Message
---- ------ -------
Warning FailedMount MountVolume.SetUp failed for volume "dumps" :
rpc error: code = InvalidArgument desc = volumeAttributes: unknown placeholder
"{namesapce}" in prefix; known placeholders are {namespace}, {podName}, {podUid},
{serviceAccount}, {nodeName}, {volumeId}, {date}Keys are capped at S3's 1024 UTF-8 bytes. A file whose key would exceed that is skipped and logged, never silently truncated, because a truncated key would overwrite an unrelated object.
Choosing an append strategy#
rewrite re-uploads the whole file every sync: The object always equals the file and only
PutObject is needed, but transfer grows quadratically with file size. segments uploads only
new tail bytes as immutable objects under {key}.parts/: Transfer is linear and data reaches
S3 promptly, at the cost of a mid-flight reader needing to concatenate segments in sequence
order.
Both end up as one clean object at the canonical key after the pod terminates, because the
sweep compacts. Rule of thumb: rewrite for files that stay small, segments for a log that
runs for hours.
rewrite | segments | |
|---|---|---|
| Transfer for an n-byte file | O(n²) across syncs | O(n) |
| S3 operations | PutObject only | PutObject plus DeleteObject at compaction |
| Object during the run | always a complete file | segments under {key}.parts/ |
| Object after termination | complete file | complete file (compacted) |
| Reading mid-flight | just read the key | concatenate a generation's segments in seq order |
Before compaction the data lives at {key}.parts/{generation}/{seq}-{startOffset}. A new
generation appears when the file is truncated or rotated.