Skip to main content

Overview

Apache Iceberg provides first-class support for Amazon S3 through the iceberg-aws module. The S3FileIO implementation offers optimized performance, security features, and seamless integration with AWS services.

Enabling AWS Integration

The iceberg-aws module is bundled with Spark and Flink runtimes from version 0.11.0+. You’ll need to provide AWS SDK v2 dependencies separately.

Spark Example

spark-sql \
  --packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:{icebergVersion},org.apache.iceberg:iceberg-aws-bundle:{icebergVersion} \
  --conf spark.sql.catalog.my_catalog=org.apache.iceberg.spark.SparkCatalog \
  --conf spark.sql.catalog.my_catalog.warehouse=s3://my-bucket/warehouse \
  --conf spark.sql.catalog.my_catalog.catalog-impl=org.apache.iceberg.aws.glue.GlueCatalog \
  --conf spark.sql.catalog.my_catalog.io-impl=org.apache.iceberg.aws.s3.S3FileIO
CREATE CATALOG my_catalog WITH (
  'type' = 'iceberg',
  'warehouse' = 's3://my-bucket/warehouse',
  'catalog-type' = 'glue',
  'io-impl' = 'org.apache.iceberg.aws.s3.S3FileIO'
);

S3FileIO Features

Progressive Multipart Upload

S3FileIO uses an optimized multipart upload algorithm that:
  • Uploads parts in parallel as soon as they’re ready
  • Deletes local parts immediately after upload
  • Maximizes upload speed and minimizes disk usage

Configuration

PropertyDefaultDescription
s3.multipart.num-threadsAvailable processorsThreads for parallel uploads
s3.multipart.part-size-bytes32MBSize of each upload part
s3.multipart.threshold1.5Threshold (× part size) to use multipart
s3.staging-dirjava.io.tmpdirDirectory for temporary files
spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.multipart.num-threads=16 \
  --conf spark.sql.catalog.my_catalog.s3.multipart.part-size-bytes=67108864

Server-Side Encryption

SSE-S3 (Amazon S3-Managed Keys)

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.sse.type=s3
Each object is encrypted with a unique key using AES-256 encryption.

SSE-KMS (AWS KMS-Managed Keys)

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.sse.type=kms \
  --conf spark.sql.catalog.my_catalog.s3.sse.key=arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012
Provides additional audit trails and key rotation.

DSSE-KMS (Dual-layer Encryption)

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.sse.type=dsse-kms \
  --conf spark.sql.catalog.my_catalog.s3.sse.key=arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012
Applies two layers of encryption for compliance requirements.

SSE-C (Customer-Provided Keys)

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.sse.type=custom \
  --conf spark.sql.catalog.my_catalog.s3.sse.key=<base64-AES256-key> \
  --conf spark.sql.catalog.my_catalog.s3.sse.md5=<base64-md5-digest>
You manage encryption keys; S3 manages encryption/decryption.

Object Store Location Provider

Traditional Hive-style layouts can cause S3 throttling due to all files being under the same prefix. The ObjectStoreLocationProvider distributes files across multiple prefixes.

Enable Object Storage Layout

CREATE TABLE my_catalog.db.events (
  id bigint,
  event_type string,
  timestamp timestamp,
  data string
)
USING iceberg
OPTIONS (
  'write.object-storage.enabled' = 'true',
  'write.data.path' = 's3://my-bucket/data'
)
PARTITIONED BY (days(timestamp));

How It Works

Files are written with a 20-bit hash distributed across directories:
s3://my-bucket/data/0101/0110/1001/10110010/day=2024-03-15/00000-0-abc123.parquet
                     ^^^^ ^^^^ ^^^^ ^^^^^^^^
                     4-bit directories (3 levels) + final 8-bit
This ensures even distribution across S3 bucket prefixes for optimal throughput.

Omit Partition Paths

CREATE TABLE my_catalog.db.events (
  id bigint,
  category string
)
USING iceberg
OPTIONS (
  'write.object-storage.enabled' = 'true',
  'write.object-storage.partitioned-paths' = 'false'
)
PARTITIONED BY (category);
Results in:
s3://my-bucket/data/1101/0100/1011/00111010-00000-0-abc123.parquet

S3 Access Control

Access Control Lists (ACL)

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.acl=bucket-owner-full-control
Valid values: private, public-read, public-read-write, authenticated-read, bucket-owner-read, bucket-owner-full-control

S3 Tags

Write Tags

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.write.tags.team=analytics \
  --conf spark.sql.catalog.my_catalog.s3.write.tags.env=production
All objects will be tagged with team=analytics and env=production.

Delete Tags

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.delete-enabled=false \
  --conf spark.sql.catalog.my_catalog.s3.delete.tags.status=deleted \
  --conf spark.sql.catalog.my_catalog.s3.delete.num-threads=10
Objects are tagged before deletion, enabling lifecycle policies to handle cleanup.

Auto-tag with Table/Namespace

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.write.table-tag-enabled=true \
  --conf spark.sql.catalog.my_catalog.s3.write.namespace-tag-enabled=true
Objects tagged with iceberg.table=<table-name> and iceberg.namespace=<namespace>.

Performance Optimization

S3 Retries

For high-throughput workloads encountering throttling:
spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.retry.num-retries=32 \
  --conf spark.sql.catalog.my_catalog.s3.retry.min-wait-ms=2000 \
  --conf spark.sql.catalog.my_catalog.s3.retry.max-wait-ms=20000
Setting retries to 32 allows time for S3 to auto-scale capacity.

Write Checksum Verification

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.checksum-enabled=true
Enables integrity checks for uploads (adds overhead).

Advanced Features

S3 Access Points

Use access points for multi-region or cross-region access:
spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.access-points.my-bucket=arn:aws:s3:us-west-2:123456789:accesspoint/my-ap \
  --conf spark.sql.catalog.my_catalog.s3.use-arn-region-enabled=true

S3 Access Grants

Use IAM principals for fine-grained access:
spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.access-grants.enabled=true \
  --conf spark.sql.catalog.my_catalog.s3.access-grants.fallback-to-iam=true
Requires S3 Access Grants Plugin in classpath.

Transfer Acceleration

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.acceleration-enabled=true
Speed up transfers by 50-500% for long-distance, large object transfers.

Analytics Accelerator

Use the Analytics Accelerator Library for improved performance:
spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.analytics-accelerator.enabled=true \
  --conf spark.sql.catalog.my_catalog.s3.crt.enabled=true \
  --conf spark.sql.catalog.my_catalog.s3.crt.max-concurrency=500

Key Configuration

PropertyDefaultDescription
s3.analytics-accelerator.enabledfalseEnable accelerator
s3.crt.enabledtrueUse CRT client
s3.crt.max-concurrency500Max concurrent requests
s3.analytics-accelerator.physicalio.blocksizebytes8MBBlock size
s3.analytics-accelerator.logicalio.prefetch.footer.enabledtruePrefetch Parquet footers

Cross-Region Access

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.cross-region-access-enabled=true
Allows access to buckets in different regions (adds latency to first request).

Dual-stack (IPv6) Endpoints

spark-sql \
  --conf spark.sql.catalog.my_catalog.s3.dualstack-enabled=true
Resolves to IPv6 when available, falls back to IPv4.

AWS Client Customization

AssumeRole for Cross-Account Access

spark-sql \
  --conf spark.sql.catalog.my_catalog.client.factory=org.apache.iceberg.aws.AssumeRoleAwsClientFactory \
  --conf spark.sql.catalog.my_catalog.client.assume-role.arn=arn:aws:iam::123456789:role/IcebergRole \
  --conf spark.sql.catalog.my_catalog.client.assume-role.region=us-west-2 \
  --conf spark.sql.catalog.my_catalog.client.assume-role.external-id=my-external-id \
  --conf spark.sql.catalog.my_catalog.client.assume-role.timeout-sec=3600

Custom Client Factory

Implement org.apache.iceberg.aws.AwsClientFactory:
public class CustomAwsClientFactory implements AwsClientFactory {
  @Override
  public S3Client s3() {
    return S3Client.builder()
      .credentialsProvider(customCredentialsProvider())
      .region(Region.US_WEST_2)
      .httpClient(ApacheHttpClient.builder()
        .maxConnections(100)
        .build())
      .build();
  }
  
  // Implement other methods...
}
Then configure:
--conf spark.sql.catalog.my_catalog.client.factory=com.example.CustomAwsClientFactory

Migration from S3A

S3FileIO is recommended over HadoopFileIO with S3A for better performance and AWS integration.
S3FileIO can read paths written by S3A (s3a:// and s3n:// schemes), making migration seamless. If you must use S3A:
  1. Set warehouse to s3a://my-bucket/warehouse
  2. Add hadoop-aws dependency
  3. Configure Hadoop properties:
--conf spark.hadoop.fs.s3a.access.key=... \
--conf spark.hadoop.fs.s3a.secret.key=... \
--conf spark.hadoop.fs.s3a.endpoint=s3.us-west-2.amazonaws.com

Best Practices

Enable write.object-storage.enabled=true to avoid S3 throttling from hot prefixes.
Set s3.retry.num-retries=32 for workloads that may trigger S3 auto-scaling.
KMS provides audit trails and key rotation required by many compliance frameworks.
Use s3.delete.tags with lifecycle policies instead of hard deletes for cost optimization.
Use a common write.data.path to maximize prefix distribution benefits.

Troubleshooting

Throttling Errors

SlowDown: Please reduce your request rate
Solutions:
  • Enable object storage layout
  • Increase retry count
  • Reduce parallelism temporarily
  • Contact AWS to increase partition count

Access Denied

Access Denied (Service: S3, Status Code: 403)
Check:
  • IAM permissions (GetObject, PutObject, DeleteObject, ListBucket)
  • Bucket policy
  • S3 Access Points configuration
  • Encryption key access (for SSE-KMS)

Connection Timeout

Unable to execute HTTP request: connect timed out
Solutions:
  • Check network connectivity
  • Verify VPC endpoints (if using)
  • Increase timeout:
    --conf spark.sql.catalog.my_catalog.s3.connection-timeout-ms=60000
    

Next Steps

Glue Catalog

Configure AWS Glue catalog for metadata

Dell ECS Storage

Use Dell Enterprise Cloud Storage

Custom FileIO

Implement custom storage backends

Build docs developers (and LLMs) love