CloudBrum
CloudBrum · Cloud decision-support platform
AWS Storage

Amazon S3Last reviewedReading time~12 minLevelBeginner to advanced

Amazon Web Services · Storage · Object storage

Storage you never have to size. You put files (objects) in a bucket and fetch them by name over HTTPS. AWS keeps the copies spread across data centres, so you don’t plan capacity. The choice that matters is the storage class: cheaper classes cost less to keep and more to read. Everything starts private, and most S3 horror stories are someone making it public.

On this page (17 sections)

    Mind map: Amazon S3 at a glance

    Amazon S3 mind map Amazon S3 at a glance: Buckets & objects: Bucket, key, prefix, Up to 50 TB each, Metadata and tags; Storage classes: Standard, Express One Zone, Intelligent-Tiering, IA, Glacier tiers; Protection: Versioning, Object Lock (WORM), Replication; Access: IAM and bucket policy, Block Public Access, Presigned URLs; Lifecycle: Transition rules, Expiry rules, Clean up old versions; Events & insight: Event notifications, Inventory, Storage Lens, Query with Athena; Performance: Prefix request rates, Multipart upload, Transfer Acceleration; Pricing: GB per month, Requests and retrieval, Data transfer out. Amazon S3 at a glance Object storage on AWS: the main building blocks and decisions CloudBrum Cloud decision-support platform CloudBrum Bucket, key, prefix Up to 50 TB each Metadata and tags Buckets & objects Standard, Express One Zone Intelligent-Tiering, IA Glacier tiers Storage classes Versioning Object Lock (WORM) Replication Protection IAM and bucket policy Block Public Access Presigned URLs Access Transition rules Expiry rules Clean up old versions Lifecycle Event notifications Inventory, Storage Lens Query with Athena Events & insight Prefix request rates Multipart upload Transfer Acceleration Performance GB per month Requests and retrieval Data transfer out Pricing Amazon S317-point analysis CloudBrum· 17-point cloud service analysis cloudbrum.com
    CloudBrum Amazon S3 at a glance · 17-point analysis Buckets & objects Bucket, key, prefix Up to 50 TB each Metadata and tags Storage classes Standard, Express One Zone Intelligent-Tiering, IA Glacier tiers Protection Versioning Object Lock (WORM) Replication Access IAM and bucket policy Block Public Access Presigned URLs Lifecycle Transition rules Expiry rules Clean up old versions Events & insight Event notifications Inventory, Storage Lens Query with Athena Performance Prefix request rates Multipart upload Transfer Acceleration Pricing GB per month Requests and retrieval Data transfer out CloudBrum · cloudbrum.com

    Free to share. The downloaded image includes the CloudBrum link.

    Key features (summary)

    • Objects, not filesYou store an object under a key such as invoices/2026/10/inv-1042.pdf. There are no real folders, only keys with slashes in them, and a single object can be up to 50 TB.
    • A class for every access patternStandard for hot data, Intelligent-Tiering when you can’t predict it, the infrequent-access classes for backups, and three Glacier classes for archives. Express One Zone gives single-digit millisecond latency in one zone.
    • Built to not lose dataS3 is designed for 11 nines of durability, and most classes keep copies in at least three Availability Zones. Reads after a write are consistent, including list calls.
    • Versioning and Object LockKeep every version of an object so a bad overwrite or delete can be undone. Object Lock adds write-once-read-many retention for records that must not change.
    • Lifecycle rulesMove data to a cheaper class after N days, expire it, and tidy up old versions and unfinished uploads, all without a script.
    • Private by defaultNew buckets block public access, have ACLs switched off, and encrypt new objects with S3-managed keys. You choose when to loosen anything, and you can switch to KMS keys.
    • Events when objects changeSend a notification to Lambda, SQS, SNS or EventBridge when something is created or removed, which is how a lot of serverless pipelines start.
    • Replication and global reachCopy objects to another bucket in the same Region or a different one, and speed up long-distance uploads with Transfer Acceleration or multi-Region access points.

    Basic concepts

    Bucket
    The container for your objects. Its name is unique across AWS, and it lives in one Region.
    Object
    A piece of data plus its key, metadata and (if you use it) a version ID.
    Key and prefix
    The key is the object’s full name. A prefix is the leading part up to a slash, and it behaves like a folder in the console but isn’t one.
    Storage class
    How S3 keeps and charges for the object, from Standard to Glacier Deep Archive. You pick it per object.
    Versioning
    When on, every overwrite or delete keeps the older copy. A delete just adds a delete marker on top.
    Lifecycle rule
    A bucket-level instruction such as move to Standard-IA after 30 days, or delete noncurrent versions after 90.
    Bucket policy
    A JSON policy attached to the bucket that says who may do what, including conditions such as TLS only or a specific VPC endpoint.
    Block Public Access
    A set of switches, at account or bucket level, that overrides any policy or ACL that would make data public.
    Presigned URL
    A temporary link signed with your credentials. Whoever holds it can upload or download one object until it expires.
    Access point
    A named door into a bucket with its own policy, handy when many teams or apps share one large bucket.
    Multipart upload
    Large objects sent in parts, in parallel, and assembled by S3. Up to 10,000 parts of 5 MiB to 5 GiB each.
    Object Lock
    Write-once-read-many protection. Retention mode and a hold stop an object being deleted or changed for a set time.
    Event notification
    A message sent when an object is created, removed or restored. The target is Lambda, SQS, SNS or EventBridge.
    Replication
    Automatic copying of new objects to another bucket, in the same Region (SRR) or a different one (CRR), usually with versioning on both.

    How to create & configure it

    Set up a private bucket from the AWS console

    1. Pick the Region firstOpen S3 and check the Region in the top bar. Choose the one closest to the people and services that use the data. You can’t move a bucket later without copying everything.
    2. Name the bucketGeneral purpose bucket, then a name that’s unique across AWS, lower case, 3 to 63 characters. Include something that tells you what it is, such as acme-prod-invoices-ap-south-1.
    3. Leave Block Public Access onIt’s ticked by default. Keep it ticked. If a website really has to serve files publicly, put CloudFront in front instead of opening the bucket.
    4. Turn on versioningIt costs a little extra for old copies, and it’s the cheapest insurance you can buy against deleting the wrong thing.
    5. Check the encryptionNew objects get S3-managed keys (SSE-S3) automatically. Choose SSE-KMS if you need your own key, an audit trail of key use, or the ability to disable access by disabling the key.
    6. Upload somethingDrag a file into the bucket. For anything over a few hundred megabytes use the CLI or an SDK, which upload in parts and retry failed ones.
    7. Add a lifecycle ruleUnder Management, create a rule: move current objects to a cheaper class after the period that matches your reads, delete noncurrent versions after 90 days, and abort incomplete multipart uploads after 7 days.
    8. Write the access downGrant your application a role with just s3:GetObject and s3:PutObject on the prefix it needs. Add a bucket policy that denies any request not using TLS.
    9. Watch itSwitch on CloudWatch request metrics for the busy prefixes, add S3 Storage Lens, and set a budget alert so you hear about a runaway job before the invoice.

    Tip: when the console steps work, capture them as code (CloudFormation, the CDK or Terraform, see the code references) so every environment gets the same safe settings and a typo shows up in review.

    Common issues after configuration

    SymptomLikely causeWhat to check / fix
    403 AccessDenied on a bucket you ownThe role or user lacks the permission, or a policy or Block Public Access is overriding itRead the error for the action and resource, then check three places: the caller’s IAM policy, the bucket policy, and any SCP. If the objects are encrypted with KMS, the caller needs permission on the key too.
    NoSuchBucket or NoSuchKeyWrong name, Region or keyCapital letters and a leading slash both matter in a key, so Photo.jpg and /photo.jpg are different objects. Check the Region too: a bucket in another Region answers with a redirect or an error, depending on the client.
    SignatureDoesNotMatchThe request was altered or signed with the wrong credentials or RegionLook for a proxy that rewrites headers, a clock that’s more than a few minutes out, or an SDK using the wrong Region or signing version.
    Presigned URL says it has expiredThe link lifetime is shorter than you think, or the credentials behind it expired firstA URL can’t outlive the temporary credentials that signed it. Sign with a role session that lasts at least as long as the link, or sign with long-term credentials in a safe place.
    503 Slow DownRequest rate over what a prefix is handling right nowS3 scales gradually. Retry with exponential backoff, spread keys over several prefixes, and use the SDK’s retry settings.
    EntityTooLarge or an upload that never finishesA single PUT over 5 GB, or a multipart upload with too few or too many partsUse multipart. Parts are 5 MiB to 5 GiB, a maximum of 10,000 of them, so pick a part size that fits the object.
    Bucket suddenly costs moreOld versions, incomplete uploads or a chatty applicationLook at the Storage Lens dashboard and the bucket’s metrics. Add a rule to expire noncurrent versions and incomplete multipart uploads, and check request counts by prefix.
    Can’t delete a bucketIt still has objects, versions or delete markersEmpty it first, including versions. A lifecycle rule that expires everything is the easy way for a big bucket.
    Object can’t be changed or deletedObject Lock retention or legal holdThat’s the point of it. Retention in compliance mode can’t be shortened by anyone, including the account root user. Plan retention periods carefully.
    Restore from Glacier takes hoursFlexible Retrieval or Deep Archive need a restore requestRequest a restore, choose a speed tier, and wait. If you need data in milliseconds, store it in Glacier Instant Retrieval instead.
    Browser blocks access from your siteCORS isn’t configuredAdd a CORS rule to the bucket that names your origin and the methods and headers you use. Don’t use * except for truly public assets.
    Replication isn’t copying old objectsReplication applies to new objects onlyUse S3 Batch Replication for what already exists, and check that versioning is on for both buckets and that the replication role has the right permissions.

    Use cases: when and where to use it

    Good fit

    • Photos, video, PDFs and everything else your users upload
    • Backups, logs and archives that you want to keep for years
    • The data lake behind analytics, machine learning and Athena queries
    • Static websites and single-page apps, behind CloudFront
    • Software releases, build artefacts and package mirrors
    • A drop zone for files that partners and devices send in

    Consider something else

    • A database-like workload with many small, random updates → a database or Amazon EFS, not S3
    • A shared file system for servers → Amazon EFS or FSx
    • Block storage for one server → Amazon EBS
    • Moving files in and out of a managed fleet with SFTP → AWS Transfer Family in front of S3
    • The same job on other clouds → compare Azure Blob Storage and Cloud Storage

    Pros: why consider it?

    Why teams choose it

    • Practically unlimited space, so there’s nothing to size or patch
    • Strong durability and consistent reads after writes
    • Storage classes that follow how your data ages
    • Plugs into almost everything on AWS, and most third-party tools speak the S3 API
    • Fine-grained access control and good defaults on new buckets
    • Cheap to store, and you pay nothing for space you don’t use

    Trade-offs to weigh

    • It’s an object store: no appending to a file and no partial in-place edits
    • Request and data transfer charges surprise people more than the storage price does
    • Policies, ACL history and Block Public Access interact in ways that take time to learn
    • Infrequent-access and Glacier classes bill for reads and for a minimum stay
    • Bucket names are global and can’t be changed
    • Leaving AWS means copying the data out, and that transfer is billed

    Common threats, vulnerabilities & risks

    RiskSeverityWhy it mattersMitigation
    A bucket or object made public by mistakeHighPublic buckets are behind a long list of data leaks, usually through an old ACL or an over-wide policy.Keep Block Public Access on at the account level, and review any exception in code review.
    Over-broad IAM permissionsHighs3: on lets a leaked key read or wipe every bucket in the account.Scope roles to named buckets and prefixes, and use conditions such as source VPC endpoint.
    No versioning or backup for important dataHighA bug or a malicious delete is permanent if there’s no older copy.Turn on versioning, and add replication or Object Lock for data you can’t recreate.
    Unencrypted or weakly encrypted dataMediumDefaults cover encryption at rest, but traffic over plain HTTP is still possible unless you refuse it.Add a bucket policy that denies requests without TLS, and use KMS keys for sensitive data.
    Presigned links shared too widelyMediumWhoever has the link can use it until the clock runs out.Short expiry, one object, one action, and access logging.
    Delete of a bucket’s contents by a compromised accountMediumVersioning alone can be undone by someone with delete-version rights.Use MFA delete or Object Lock, and send CloudTrail data events somewhere the same account can’t change.
    Silent cost growthMediumOld versions, unused buckets and cross-Region traffic keep billing without anyone noticing.Lifecycle rules, Storage Lens, tagging and a budget alert.
    Public access through a CloudFront origin or website settingLow–MedA bucket opened up just for a website stays open for everything else.Use CloudFront with origin access control and keep the bucket private.
    No audit trailLow–MedWithout logs you can’t tell who read or deleted something, or when.Enable CloudTrail data events for sensitive buckets and server access logs where needed.

    Performance optimisation

    • Spread load over prefixes. Each partitioned prefix can take at least 3,500 write and 5,500 read requests a second, and the limits add up across prefixes.
    • Use multipart upload for anything big, and run parts in parallel. The SDKs' transfer managers do this for you.
    • Download large objects with byte-range requests in parallel, in chunks of about 8 to 16 MB.
    • Put CloudFront in front of content that many people read, so most requests never reach the bucket.
    • Use Transfer Acceleration for long-distance uploads, or run the client in the same Region as the bucket.
    • Try S3 Express One Zone (directory buckets) when you need single-digit millisecond latency for many small requests, such as machine learning training data.
    • Keep objects reasonably sized. Millions of tiny files mean millions of requests, so batch small files into larger ones where you can.
    • Use an S3 gateway VPC endpoint so traffic from your VPC stays on the AWS network and avoids a NAT gateway.

    Reduce monthly cost

    FinOps is the habit of managing cloud spend together with engineering. These are the FinOps checks that matter most for Amazon S3.

    • Match the storage class to real access. Look at Storage Lens and access logs before moving data, because retrieving from a cold class can cost more than you saved.
    • Use Intelligent-Tiering when access is unpredictable. It moves objects on its own, with a small monitoring fee per object, and leaves tiny objects alone.
    • Write lifecycle rules for every bucket: transitions, expiry, noncurrent versions and incomplete multipart uploads.
    • Mind the minimums. Infrequent-access classes bill at least 128 KB per object and 30 days, and Glacier classes have 90 or 180 day minimums.
    • Cut request costs by batching small objects, caching with CloudFront and avoiding polling LIST calls.
    • Keep traffic inside AWS. A gateway VPC endpoint is free, and moving data between S3 and EC2 in the same Region doesn’t cost transfer fees.
    • Watch cross-Region replication and copy jobs. They bill for both transfer and the second copy.
    • Delete what you don’t need: abandoned buckets, old logs, noncurrent versions, and test data.
    • Compress and use efficient formats such as Parquet for analytics. You pay for every byte stored and scanned.
    • Tag buckets by team and product, and set a budget alert for S3 as a line item.

    Treat the prices and free-tier terms here as examples from AWS's public pages. They vary by Region and they change, so check the live figures first (see Pricing).

    Security optimisation

    • Keep Block Public Access on for the account, and treat any exception as a reviewed change.
    • Give each application its own IAM role with permissions on one bucket or prefix. Don’t hand out long-lived access keys.
    • Add a bucket policy that denies requests not using TLS, and, for internal data, requests that don’t come through your VPC endpoint.
    • Rely on the default SSE-S3 encryption for most data, and use SSE-KMS with a customer-managed key where you need key-level control and logs.
    • Switch on versioning, and add MFA delete or Object Lock where deletes must be hard to do.
    • Share one object through a presigned URL that expires soon, and leave the bucket closed.
    • Log with CloudTrail data events and server access logs for sensitive buckets, and send them to a separate account.
    • Scan for exposure with IAM Access Analyzer for S3 and Amazon Macie to find sensitive data in the wrong place.
    • Keep ACLs off (the bucket owner enforced setting) so policies are the only way access is granted.
    • Review who can create buckets and change policies, and use service control policies to stop the dangerous actions across accounts.

    Governance & compliance

    Guardrails

    • Tagging rules for buckets (owner, environment, data class, cost centre) checked when a bucket is created
    • A service control policy that denies turning off Block Public Access or disabling default encryption
    • A shared template for buckets with logging, versioning, lifecycle and policy built in
    • A data classification that decides which buckets need KMS keys, Object Lock or replication

    Continuous checks (AWS Config)

    • “s3-bucket-level-public-access-prohibited”
    • “s3-bucket-ssl-requests-only”
    • “s3-bucket-versioning-enabled”
    • “s3-bucket-logging-enabled”
    • “s3-default-encryption-kms” for sensitive buckets

    Use AWS CloudTrail to see who changed a bucket or read an object, AWS Config for configuration history and rules, IAM Access Analyzer to find buckets shared outside the account, and AWS Artifact for compliance reports. S3 is in scope for many assurance programmes, but the duty is shared: AWS runs the service, and you own the policies, encryption choices, retention and data.

    Reduce carbon footprint

    • Keep less. Data nobody needs still sits on disks that use electricity, so deleting it is the simplest saving there is.
    • Compress files and prefer compact formats such as Parquet, so there is less to store, copy and scan.
    • Use lifecycle rules and Intelligent-Tiering so cold data sits in the classes meant for it.
    • Cache popular objects at the edge, so one copy at the origin serves many readers.
    • Put buckets near the people and services that read them, which also shortens the long-haul transfers.
    • For big archives that nobody waits on, a Region with a cleaner power grid is an easy pick.
    • The AWS Customer Carbon Footprint Tool estimates emissions for your account. See how it treats S3 before you quote a figure.

    Pricing

    Purchase options

    Storage by class Per GB-month

    Storage is billed per GB-month, and the price drops as the class gets colder, from Standard at the top to Glacier Deep Archive at the bottom. The Region changes the rates.

    Requests Per request

    PUT, COPY, POST and LIST requests cost more than GET requests, and both are billed per thousand. Chatty applications can spend more on requests than on storage.

    Retrieval and minimums Cold classes

    Infrequent-access and Glacier classes charge to read data back, and bill a minimum period and object size. Cheap to keep can mean costly to touch.

    Data transfer Out only

    Data going into S3 is free. Data going out to the internet is billed after a monthly allowance, and cross-Region copies are billed too.

    What you actually pay for

    • Storage, by class and by the GB-month
    • Requests (PUT, GET, LIST and others), per thousand
    • Fees for reading back from infrequent-access and Glacier classes, and charges for leaving them early
    • Data transferred out to the internet or to another Region
    • Features: Intelligent-Tiering monitoring, replication, inventory, Storage Lens advanced metrics, S3 Object Lambda and others

    Rates move with the Region and with time. As a rough guide, Standard storage in US East starts at about 2.3 cents per GB-month. Check the official Amazon S3 pricing page and the AWS Pricing Calculator for current figures, and read the AWS Free Tier page for what new accounts get.

    Important code references

    A private bucket for user uploads, set up as you’d want it in production: Block Public Access, versioning, encryption, a TLS-only policy and a lifecycle rule. Use the CLI or the Terraform tab to create the bucket. The Python and C# tabs put an invoice in it and hand back a link that stops working after 15 minutes. Swap in your own bucket name, Region and key.

    # Names must be unique across AWS: change the bucket name. Mumbai is used as the example Region.
    BUCKET=acme-uploads-123456789012
    REGION=ap-south-1
    
    aws s3api create-bucket --bucket $BUCKET --region $REGION \
      --create-bucket-configuration LocationConstraint=$REGION
    
    # Nothing public, ever
    aws s3api put-public-access-block --bucket $BUCKET --public-access-block-configuration \
      BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
    
    # Keep older versions so a bad overwrite can be undone
    aws s3api put-bucket-versioning --bucket $BUCKET --versioning-configuration Status=Enabled
    
    # SSE-S3 is already the default; stating it makes the intent visible
    aws s3api put-bucket-encryption --bucket $BUCKET --server-side-encryption-configuration \
      '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"},"BucketKeyEnabled":true}]}'
    
    # Refuse any request that is not sent over TLS
    aws s3api put-bucket-policy --bucket $BUCKET --policy '{
      "Version": "2012-10-17",
      "Statement": [{
        "Sid": "DenyPlainHttp",
        "Effect": "Deny",
        "Principal": "*",
        "Action": "s3:*",
        "Resource": ["arn:aws:s3:::'$BUCKET'", "arn:aws:s3:::'$BUCKET'/*"],
        "Condition": {"Bool": {"aws:SecureTransport": "false"}}
      }]
    }'
    
    # Cheaper class after 30 days, old versions gone after 90, unfinished uploads cleaned up after 7
    aws s3api put-bucket-lifecycle-configuration --bucket $BUCKET --lifecycle-configuration '{
      "Rules": [{
        "ID": "age-out",
        "Status": "Enabled",
        "Filter": {"Prefix": ""},
        "Transitions": [{"Days": 30, "StorageClass": "STANDARD_IA"}],
        "NoncurrentVersionExpiration": {"NoncurrentDays": 90},
        "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7}
      }]
    }'
    
    # Upload, then make a link that works for 15 minutes
    aws s3 cp ./invoice-1042.pdf s3://$BUCKET/invoices/2026/10/invoice-1042.pdf
    aws s3 presign s3://$BUCKET/invoices/2026/10/invoice-1042.pdf --expires-in 900
    # Assumes var.bucket_name (globally unique) and the AWS provider version 4 or later
    resource "aws_s3_bucket" "uploads" {
      bucket = var.bucket_name
    }
    
    resource "aws_s3_bucket_public_access_block" "uploads" {
      bucket                  = aws_s3_bucket.uploads.id
      block_public_acls       = true
      ignore_public_acls      = true
      block_public_policy     = true
      restrict_public_buckets = true
    }
    
    resource "aws_s3_bucket_ownership_controls" "uploads" {
      bucket = aws_s3_bucket.uploads.id
      rule {
        object_ownership = "BucketOwnerEnforced"  # ACLs off
      }
    }
    
    resource "aws_s3_bucket_versioning" "uploads" {
      bucket = aws_s3_bucket.uploads.id
      versioning_configuration {
        status = "Enabled"
      }
    }
    
    resource "aws_s3_bucket_server_side_encryption_configuration" "uploads" {
      bucket = aws_s3_bucket.uploads.id
      rule {
        apply_server_side_encryption_by_default {
          sse_algorithm = "AES256"
        }
        bucket_key_enabled = true
      }
    }
    
    data "aws_iam_policy_document" "tls_only" {
      statement {
        sid       = "DenyPlainHttp"
        effect    = "Deny"
        actions   = ["s3:*"]
        resources = [aws_s3_bucket.uploads.arn, "${aws_s3_bucket.uploads.arn}/*"]
    
        principals {
          type        = "*"
          identifiers = ["*"]
        }
        condition {
          test     = "Bool"
          variable = "aws:SecureTransport"
          values   = ["false"]
        }
      }
    }
    
    resource "aws_s3_bucket_policy" "uploads" {
      bucket     = aws_s3_bucket.uploads.id
      policy     = data.aws_iam_policy_document.tls_only.json
      depends_on = [aws_s3_bucket_public_access_block.uploads]
    }
    
    resource "aws_s3_bucket_lifecycle_configuration" "uploads" {
      bucket = aws_s3_bucket.uploads.id
    
      rule {
        id     = "age-out"
        status = "Enabled"
    
        filter {}
    
        transition {
          days          = 30
          storage_class = "STANDARD_IA"
        }
        noncurrent_version_expiration {
          noncurrent_days = 90
        }
        abort_incomplete_multipart_upload {
          days_after_initiation = 7
        }
      }
    
      depends_on = [aws_s3_bucket_versioning.uploads]
    }
    # pip install boto3
    import boto3
    from botocore.config import Config
    
    BUCKET = "acme-uploads-123456789012"
    REGION = "ap-south-1"
    
    # Signature version 4 is needed for presigned URLs in newer Regions
    s3 = boto3.client("s3", region_name=REGION, config=Config(signature_version="s3v4"))
    
    
    def upload_invoice(path, key):
        """Upload one file. upload_file switches to multipart on its own for large files."""
        s3.upload_file(
            path,
            BUCKET,
            key,
            ExtraArgs={
                "ContentType": "application/pdf",
                "ServerSideEncryption": "AES256",
                "Metadata": {"source": "billing-service"},
            },
        )
    
    
    def download_link(key, seconds=900):
        """A URL that lets the holder fetch this one object until it expires."""
        return s3.generate_presigned_url(
            "get_object",
            Params={"Bucket": BUCKET, "Key": key},
            ExpiresIn=seconds,
        )
    
    
    def list_prefix(prefix):
        """List keys under a prefix, page by page, so a large listing doesn't need one huge response."""
        keys = []
        for page in s3.get_paginator("list_objects_v2").paginate(Bucket=BUCKET, Prefix=prefix):
            keys.extend(item["Key"] for item in page.get("Contents", []))
        return keys
    
    
    if __name__ == "__main__":
        key = "invoices/2026/10/invoice-1042.pdf"
        upload_invoice("invoice-1042.pdf", key)
        print(download_link(key))
        print(list_prefix("invoices/2026/10/"))
    // Program.cs: put an invoice in the bucket and print a 15-minute download link
    // NuGet package: AWSSDK.S3
    using Amazon;
    using Amazon.S3;
    using Amazon.S3.Model;
    
    const string bucket = "acme-uploads-123456789012";
    const string key = "invoices/2026/10/invoice-1042.pdf";
    
    // Credentials come from the usual chain: environment, shared profile or the role the code runs under
    using var s3 = new AmazonS3Client(RegionEndpoint.APSouth1);
    
    // Upload; PutObject suits files up to a few hundred megabytes, and TransferUtility handles larger ones in parts
    var put = new PutObjectRequest
    {
        BucketName = bucket,
        Key = key,
        FilePath = "invoice-1042.pdf",
        ContentType = "application/pdf",
        ServerSideEncryptionMethod = ServerSideEncryptionMethod.AES256
    };
    put.Metadata["source"] = "billing-service";
    await s3.PutObjectAsync(put);
    
    // A signed link that works until it expires
    var link = s3.GetPreSignedURL(new GetPreSignedUrlRequest
    {
        BucketName = bucket,
        Key = key,
        Verb = HttpVerb.GET,
        Expires = DateTime.UtcNow.AddMinutes(15)
    });
    Console.WriteLine(link);
    
    // List what's under a prefix, one page at a time
    string? token = null;
    do
    {
        var page = await s3.ListObjectsV2Async(new ListObjectsV2Request { BucketName = bucket, Prefix = "invoices/2026/10/", ContinuationToken = token });
        foreach (var item in page.S3Objects)
        {
            Console.WriteLine($"{item.Key}  {item.Size} bytes");
        }
        token = page.IsTruncated == true ? page.NextContinuationToken : null;
    } while (token is not null);

    Best videos to explore

    Short walkthrough videos for this service are being prepared. They will appear here and load only when you press play. Until then, follow CloudBrum on YouTube for new videos.

    Introduction to Amazon S3Video coming soon · Beginner
    Choosing an S3 storage classVideo coming soon · Intermediate
    Locking down an S3 bucketVideo coming soon · Intermediate
    Lifecycle rules and cost control in S3Video coming soon · Advanced

    Interview questions

    BasicWhat is Amazon S3?

    It’s AWS’s object storage service. You create buckets and store objects in them, each with a key, and read them back over HTTPS with an SDK, the CLI or a plain URL. S3 spreads the data across facilities for durability, so you don’t manage disks or capacity.

    BasicWhat is the difference between a bucket and an object?

    A bucket is the named container, created in one Region. An object is one item inside it: the data, its key (the full name), metadata and optionally a version ID. The folder view in the console is just a display of key prefixes.

    BasicName some S3 storage classes and when you’d use them.

    Standard for data used often, Intelligent-Tiering when access is unpredictable, Standard-IA and One Zone-IA for infrequent access, Glacier Instant Retrieval, Flexible Retrieval and Deep Archive for archives, and Express One Zone when you need very low latency in a single zone. Colder classes cost less to store and more to read.

    IntermediateHow do you make sure a bucket isn’t public?

    Turn on Block Public Access at the account level, keep ACLs disabled with the bucket-owner-enforced setting, and check bucket policies for wildcard principals. Serve public content through CloudFront with origin access control instead of opening the bucket, and use IAM Access Analyzer to catch anything shared outside the account.

    IntermediateHow does S3 keep objects safe from accidental deletion?

    Versioning keeps older copies, so a delete only adds a delete marker. MFA delete asks for a second factor to remove a version, and Object Lock stops changes for a retention period. For disaster recovery, add replication to a bucket in another account or Region.

    IntermediateWhat is a presigned URL and what is the risk?

    It’s a time-limited link, signed with the creator’s credentials, that lets someone upload or download one object without having AWS access. The risk is that anyone who gets the link can use it until it expires, and it stops working when the signing credentials expire, so keep the lifetime short and the scope narrow.

    AdvancedA bucket’s bill has doubled in two months and nobody added data. How do you find out why?

    Look at Storage Lens and the Cost Explorer breakdown first: is it storage, requests, retrieval or transfer? Common causes are noncurrent versions piling up with no expiry rule, incomplete multipart uploads, a job listing the bucket constantly, or data being pulled out of a cold class. Fix it with lifecycle rules, caching and a change in how the application reads.

    AdvancedYou need to keep records for seven years in a way no administrator can alter. What do you set up?

    Create the bucket with Object Lock, use compliance mode with a seven-year default retention, turn on versioning, and limit who can change the lock settings. Replicate to a second bucket in another account, enable CloudTrail data events, and test a restore before you rely on it.

    AdvancedHow would you move 200 TB from an on-premises system to S3?

    It depends on the link and the deadline. With a fast and reliable network, use AWS DataSync or the CLI in parallel and validate checksums. If the network would take weeks, order a Snowball device, and keep the live data in sync with DataSync until cutover. Pick the destination class on day one so you don’t pay to transition it later.

    AdvancedUploads to a busy prefix are returning 503 Slow Down. What do you do?

    Retry with exponential backoff and jitter, which the SDKs do by default. Then spread the load, since the rate limits apply per partitioned prefix: add key prefixes, avoid putting every object under one hot prefix, and consider S3 Express One Zone if many small requests need very low latency. Make sure parallelism is raised gradually, not all at once.

    Similar or alternative services on other clouds

    CapabilityAWSAzureGoogle Cloud
    ServiceAmazon S3Azure Blob StorageCloud Storage
    Where objects liveA bucket, tied to one RegionA container inside a storage accountA bucket in a region, dual-region or multi-region
    Largest single object50 TBAbout 190.7 TiB for a block blob5 TiB
    Cheap tiers and how fast data comes backInfrequent-access and Glacier Instant answer in milliseconds; Flexible and Deep Archive need a restoreCool and Cold answer at once; Archive is offline and must be rehydratedNearline, Coldline and Archive all answer in milliseconds
    Tiering on autopilotIntelligent-TieringLifecycle rules, including rules based on last accessAutoclass
    Undoing a deleteVersioning, with MFA delete as an extra lockSoft delete and versioningSoft delete (seven days by default) and versioning
    A link that expiresPresigned URLShared access signature (a user delegation SAS is the safer kind)Signed URL
    Run code when a file arrivesEvent notifications to Lambda, SQS, SNS or EventBridgeEvent Grid, often feeding Azure FunctionsPub/Sub notifications or Eventarc, feeding Cloud Run functions

    The same job does not always need object storage. A shared file system for servers is Amazon EFS or FSx territory, and a block disk for one server is Amazon EBS. If the data is mostly rows you query, a database or a warehouse is a better home.

    Did you find this article helpful?

    Your answer helps us improve this page for the next reader.

    Sources and further reading

    CloudBrum wrote this page in its own words, using the provider’s public documentation listed below as the reference. Features, limits, names and prices change, so confirm anything that matters on the provider’s own pages before you decide.