Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

S3 Sink Connector

Stream Kafka events to Amazon S3 for data lake storage, analytics, and long-term archival.


The S3 Sink Connector writes Kafka records to S3 objects, supporting:

  • Multiple output formats (Parquet, Avro, JSON)
  • Time-based and field-based partitioning
  • Exactly-once delivery with commit tracking
  • Automatic file rotation by size, time, or record count

The S3 Sink Connector is included in Confluent Platform. For standalone installation:

Terminal window
# Download from Confluent Hub
confluent-hub install confluentinc/kafka-connect-s3:latest
# Or manually download
curl -O https://packages.confluent.io/archive/7.5/kafka-connect-s3-7.5.0.zip
unzip kafka-connect-s3-7.5.0.zip -d /usr/share/kafka/plugins/

{
"name": "s3-sink",
"config": {
"connector.class": "io.confluent.connect.s3.S3SinkConnector",
"tasks.max": "3",
"topics": "events",
"s3.bucket.name": "data-lake-bucket",
"s3.region": "us-east-1",
"format.class": "io.confluent.connect.s3.format.parquet.ParquetFormat",
"flush.size": "10000",
"storage.class": "io.confluent.connect.s3.storage.S3Storage",
"partitioner.class": "io.confluent.connect.storage.partitioner.TimeBasedPartitioner"
}
}

{
"aws.access.key.id": "${secrets:aws/access-key}",
"aws.secret.access.key": "${secrets:aws/secret-key}"
}

When running on EC2 or EKS, use instance profiles or IAM roles for service accounts:

{
"s3.credentials.provider.class": "com.amazonaws.auth.DefaultAWSCredentialsProviderChain"
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket",
"s3:DeleteObject",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::data-lake-bucket",
"arn:aws:s3:::data-lake-bucket/*"
]
}
]
}

Columnar format optimized for analytics workloads.

{
"format.class": "io.confluent.connect.s3.format.parquet.ParquetFormat",
"parquet.codec": "snappy"
}
CodecCompressionSpeedUse Case
snappyMediumFastGeneral purpose
gzipHighSlowStorage optimization
zstdHighMediumBalanced
noneNoneFastestRaw data

Schema-aware binary format.

{
"format.class": "io.confluent.connect.s3.format.avro.AvroFormat",
"avro.codec": "snappy"
}

Human-readable format for debugging or simple integrations.

{
"format.class": "io.confluent.connect.s3.format.json.JsonFormat"
}

Organize data by time for efficient querying.

{
"partitioner.class": "io.confluent.connect.storage.partitioner.TimeBasedPartitioner",
"path.format": "'year'=YYYY/'month'=MM/'day'=dd/'hour'=HH",
"locale": "en-US",
"timezone": "UTC",
"partition.duration.ms": "3600000"
}

Output path: topics/events/year=2024/month=01/day=15/hour=10/events+0+0000000000.parquet

Partition by record field values.

{
"partitioner.class": "io.confluent.connect.storage.partitioner.FieldPartitioner",
"partition.field.name": "region,event_type"
}

Output path: topics/events/region=us-east/event_type=click/events+0+0000000000.parquet

Simple topic-partition structure.

{
"partitioner.class": "io.confluent.connect.storage.partitioner.DefaultPartitioner"
}

Output path: topics/events/partition=0/events+0+0000000000.parquet


{
"flush.size": "10000"
}

Create new file every 10,000 records.

{
"rotate.schedule.interval.ms": "3600000"
}

Create new file every hour.

{
"s3.part.size": "26214400"
}

Flush when S3 part reaches 25MB.


{
"key.converter": "io.confluent.connect.avro.AvroConverter",
"key.converter.schema.registry.url": "http://schema-registry:8081",
"value.converter": "io.confluent.connect.avro.AvroConverter",
"value.converter.schema.registry.url": "http://schema-registry:8081"
}
{
"key.converter": "org.apache.kafka.connect.storage.StringConverter",
"value.converter": "org.apache.kafka.connect.json.JsonConverter",
"value.converter.schemas.enable": "false"
}

{
"errors.tolerance": "all",
"errors.deadletterqueue.topic.name": "dlq-s3-sink",
"errors.deadletterqueue.topic.replication.factor": 3,
"errors.deadletterqueue.context.headers.enable": true,
"errors.log.enable": true
}

{
"tasks.max": "6"
}

Set to number of topic partitions for maximum throughput.

{
"s3.part.size": "52428800",
"flush.size": "50000",
"rotate.interval.ms": "600000"
}
{
"s3.retry.backoff.ms": "200",
"s3.part.retries": "3"
}

{
"name": "events-to-s3",
"config": {
"connector.class": "io.confluent.connect.s3.S3SinkConnector",
"tasks.max": "6",
"topics": "events,transactions",
"s3.bucket.name": "analytics-data-lake",
"s3.region": "us-east-1",
"s3.credentials.provider.class": "com.amazonaws.auth.DefaultAWSCredentialsProviderChain",
"format.class": "io.confluent.connect.s3.format.parquet.ParquetFormat",
"parquet.codec": "snappy",
"storage.class": "io.confluent.connect.s3.storage.S3Storage",
"partitioner.class": "io.confluent.connect.storage.partitioner.TimeBasedPartitioner",
"path.format": "'year'=YYYY/'month'=MM/'day'=dd/'hour'=HH",
"locale": "en-US",
"timezone": "UTC",
"partition.duration.ms": "3600000",
"flush.size": "50000",
"rotate.interval.ms": "600000",
"s3.part.size": "52428800",
"key.converter": "io.confluent.connect.avro.AvroConverter",
"key.converter.schema.registry.url": "http://schema-registry:8081",
"value.converter": "io.confluent.connect.avro.AvroConverter",
"value.converter.schema.registry.url": "http://schema-registry:8081",
"errors.tolerance": "all",
"errors.deadletterqueue.topic.name": "dlq-s3-sink",
"errors.deadletterqueue.topic.replication.factor": 3
}
}

Configure S3 lifecycle rules for cost optimization:

{
"Rules": [
{
"ID": "MoveToGlacier",
"Status": "Enabled",
"Filter": {"Prefix": "topics/"},
"Transitions": [
{"Days": 90, "StorageClass": "GLACIER"},
{"Days": 365, "StorageClass": "DEEP_ARCHIVE"}
]
}
]
}

Query S3 data directly:

CREATE EXTERNAL TABLE events (
event_id STRING,
event_type STRING,
timestamp BIGINT,
user_id STRING
)
PARTITIONED BY (year STRING, month STRING, day STRING, hour STRING)
STORED AS PARQUET
LOCATION 's3://analytics-data-lake/topics/events/';
MSCK REPAIR TABLE events;

Configure Glue crawler to automatically discover schema:

{
"s3.part.size": "52428800",
"schema.compatibility": "FULL"
}

IssueCauseSolution
Access DeniedIAM permissionsVerify S3 bucket policy and IAM role
Slow uploadsSmall flush sizeIncrease flush.size
Many small filesFrequent rotationIncrease rotate.interval.ms
Schema errorsRegistry mismatchVerify converter configuration