How We Tested
Every provider was tested from the same baseline: a Vultr (https://vultr.com/?ref=PLACEHOLDER) KVM instance running Debian 12, 4 vCPU, 8 GB RAM, in the Frankfurt region. We used rclone 1.67.0, the AWS CLI v2.15.x, and custom Python scripts using boto3 1.34 to measure throughput and latency. Test files were 100 MB, 1 GB, and 10 GB objects. We ran each transfer 10 times and dropped the top and bottom result before averaging.
For latency, we used `aws s3api head-object` against a known object and measured the time-to-first-byte with curl:
``` curl -o /dev/null -s -w "%{time_starttransfer}\n" https://bucket.endpoint/object ```
For IAM, we attempted to create a policy that allowed `s3:GetObject` on a specific prefix while denying all other actions. Not every provider passed this test cleanly.
rclone copy localfile.tar.gz remote:bucket/ --progress --transfers=8 --s3-upload-concurrency=8
Cloudflare R2: Zero Egress, Real S3 Compatibility
R2 is the most disruptive pricing model in this roundup. Egress to the internet is $0.00. Storage is $0.015 per GB per month. Class A operations (PUT, POST) are $4.50 per million, Class B (GET) are $0.36 per million. There are no minimum storage commitments.
API compatibility is solid. We ran the full AWS CLI v2 command set against R2 with a custom endpoint and hit zero failures on the operations developers actually use: multipart upload, presigned URLs, CORS configuration, and bucket versioning. The one missing feature that matters: no server-side encryption with customer-managed keys (SSE-C) as of mid-2025. If your compliance requirement mandates SSE-C, R2 is out.
Configure rclone for R2 like this:
[r2]
type = s3
provider = Cloudflare
access_key_id = YOUR_ACCESS_KEY
secret_access_key = YOUR_SECRET_KEY
endpoint = https://ACCOUNT_ID.r2.cloudflarestorage.com
acl = private
Backblaze B2: Cheapest Storage, Cloudflare Bandwidth Alliance
B2 charges $0.006 per GB per month for storage - that is less than half of R2's rate. If you are storing cold backups, build artifacts, or large media archives, nothing beats B2 on raw storage cost. Egress to Cloudflare-peered endpoints is free under the Bandwidth Alliance. Egress to other destinations is $0.01 per GB after the first 1 GB per day.
The S3-compatible API works cleanly with boto3 and the AWS CLI. Point your endpoint at `s3.us-west-004.backblazeb2.com` (region varies by bucket). IAM is handled through application keys scoped to bucket and prefix - functional, but not as granular as AWS IAM policies. You cannot write condition blocks or deny rules at the key level.
For backup pipelines, we had good results piping restic directly to B2:
export B2_ACCOUNT_ID=youraccountid
export B2_ACCOUNT_KEY=yourappkey
restic -r b2:bucketname:/restic init
restic -r b2:bucketname:/restic backup /var/data --verbose
Wasabi: Flat Rate, 90-Day Minimum Catch
Wasabi charges $0.0088 per GB per month with zero egress fees and zero API call fees. On paper this is excellent. In practice, the 90-day minimum storage policy means deleted objects are billed for 90 days. If your workload involves frequent small-object churn - CI artifact caching, for example - Wasabi's effective cost can exceed AWS S3 Standard.
We measured this directly. A CI pipeline generating and deleting 500 MB of artifacts per build, running 20 builds per day, kept about 1.5 TB of billable storage at Wasabi even though actual stored data never exceeded 2 GB at any moment. At $0.0088/GB/month on 1.5 TB, that is $13.20/month more than expected.
Wasabi is the right choice for long-lived assets: video files, ML training datasets, database dumps you keep for 90+ days. The S3 API compatibility is complete. Endpoint format: `s3.us-central-1.wasabisys.com`. No free tier.
aws s3 cp largefile.tar.gz s3://mybucket/ \
--endpoint-url https://s3.us-central-1.wasabisys.com \
--storage-class STANDARD
AWS S3: Still the Reference Implementation
AWS S3 Standard costs $0.023 per GB per month in us-east-1 plus $0.09 per GB egress after the first 100 GB per month. It is the most expensive option in this roundup. It is also the most capable.
If you need S3 Object Lambda, S3 Select, event notifications to SQS/SNS/Lambda, cross-region replication with conflict resolution, or MFA delete, AWS S3 is the only option. IAM policy granularity is unmatched - you can write policies that restrict access by VPC endpoint, source IP CIDR, time of day, and object tag combination simultaneously.
For teams already deep in the AWS ecosystem, the egress cost is often absorbed by data transfer pricing within the same region between services. The breakeven point against R2 depends entirely on how much data leaves AWS. We calculated that at 1 TB of monthly egress, you pay roughly $90 more per month on S3 compared to R2. At 100 GB, the difference is under $10.
S3 Intelligent-Tiering is worth enabling on any bucket where object access patterns are unpredictable:
aws s3api put-bucket-intelligent-tiering-configuration \
--bucket mybucket \
--id EntireBucket \
--intelligent-tiering-configuration '{
"Id": "EntireBucket",
"Status": "Enabled",
"Tierings": [
{"Days": 90, "AccessTier": "ARCHIVE_ACCESS"},
{"Days": 180, "AccessTier": "DEEP_ARCHIVE_ACCESS"}
]
}'
Vultr Object Storage: Colocated With Your VPS
Vultr Object Storage (https://vultr.com/?ref=PLACEHOLDER) runs on the Ceph backend and exposes an S3-compatible API. Pricing is $5 per month for 250 GB storage plus 1 TB outbound bandwidth, then $0.01 per GB storage and $0.01 per GB egress beyond that. The key advantage is network locality: if your application servers are on Vultr, intra-region traffic between a Vultr VPS and Vultr Object Storage in the same region is not billed.
We measured 312 MB/s average throughput uploading a 10 GB file from a Frankfurt Vultr instance to Frankfurt Object Storage. That is faster than any cross-provider transfer we tested. For CI pipelines, build artifact storage, and application file uploads, the colocation benefit is real and measurable.
The IAM model is simpler than AWS - you get access keys scoped per cluster, not per bucket. For multi-tenant setups this is a limitation. For single-project deployments or when you are already using Vultr's broader infrastructure, it is perfectly adequate.
Configure the AWS CLI for Vultr Object Storage with a profile:
[profile vultr-fra]
aws_access_key_id = YOUR_KEY
aws_secret_access_key = YOUR_SECRET
region = fra1
# Then use:
aws s3 ls s3://mybucket \
--endpoint-url https://fra1.vultrobjects.com \
--profile vultr-fra
DigitalOcean Spaces: Predictable but Pricey
Spaces costs $25 per month for 250 GB storage and 1 TB outbound transfer. Beyond that, storage is $0.02 per GB and egress is $0.01 per GB. The S3 API works, rclone works, boto3 works. The CDN integration with DigitalOcean's edge network is straightforward.
Where Spaces falls short for developers: no bucket versioning, no lifecycle policies that trigger transitions between storage classes (there is only one class), and no event notifications. For static asset hosting behind a CDN or simple file storage attached to a Droplet, it works fine. For anything requiring operational depth, look elsewhere.
The $25/month flat rate makes cost predictable, which some teams value. In our testing, throughput from a Frankfurt Droplet to Frankfurt Spaces hit 280 MB/s on a 10 GB object - similar to Vultr for similar reasons.
s3cmd --configure
# Set endpoint to fra1.digitaloceanspaces.com
# Then:
s3cmd put largefile.tar.gz s3://mybucket/backups/
Automating Storage Operations in CI/CD
For teams running automated deployments, storage decisions interact heavily with pipeline tooling. We use rclone sync rather than aws s3 sync in most pipelines because rclone's `--checksum` flag verifies integrity without relying on ETag matching, which behaves differently across providers for multipart uploads.
If you are building automated deployment pipelines that interact with storage - artifact caching, state file management, backup rotation - taskbotshub.ai has pre-built workflow templates for rclone-based sync jobs that integrate with GitHub Actions and GitLab CI without requiring provider-specific SDKs.
For bucket naming in multi-environment setups, consistent naming conventions prevent costly mistakes. If your project names are also registered as domains, the naming carries over cleanly. nicename.me is useful for checking name availability across both domain registrars and common bucket namespaces simultaneously before committing to a naming scheme.
Backup rotation with rclone is one command:
rclone delete remote:bucket/backups/ \
--min-age 90d \
--drive-use-trash=false \
--verbose
Latency and Throughput Numbers from Frankfurt
Here are our averaged results from the Frankfurt test server, 10-sample mean after dropping outliers:
Time to first byte (HEAD request against a 1 KB object): - Cloudflare R2: 28 ms - AWS S3 eu-central-1: 19 ms - Backblaze B2 eu-central-003: 41 ms - Wasabi eu-central-2: 38 ms - Vultr Object Storage fra1: 12 ms - DigitalOcean Spaces fra1: 15 ms
10 GB upload throughput (multipart, 8 concurrent parts): - Vultr Object Storage fra1: 312 MB/s - DigitalOcean Spaces fra1: 280 MB/s - AWS S3 eu-central-1: 241 MB/s - Cloudflare R2: 198 MB/s - Wasabi eu-central-2: 187 MB/s - Backblaze B2 eu-central-003: 163 MB/s
For latency-sensitive workloads, colocating compute and storage wins every time. For bulk transfers where egress cost dominates, R2 or B2 win on economics despite the throughput gap.
# Multipart upload benchmark with AWS CLI
aws s3 cp 10gb-testfile.bin s3://testbucket/ \
--endpoint-url https://fra1.vultrobjects.com \
--expected-size 10737418240 \
--profile vultr-fra \
--no-progress
IAM and Access Control Comparison
IAM granularity matters for teams managing multiple services, environments, or customers from a single storage account. AWS S3 wins outright: bucket policies, IAM policies, ACLs, resource-based conditions, and S3 Access Points give you precise control over who can do what and from where.
R2 uses API tokens with scoped permissions at the account and bucket level. You can restrict a token to read-only on a specific bucket. You cannot write a policy that says 'allow GET on prefix /public/, deny everything on prefix /private/' with a single token.
B2 application keys support bucket-level and prefix-level scoping plus file name prefix filtering. Granular enough for most use cases, not as expressive as AWS IAM.
Wasabi IAM is a clone of AWS IAM syntax. Policy documents written for S3 work on Wasabi with endpoint changes. This is a genuine differentiator for teams migrating from AWS who want to keep their existing policy structure.
Vultr and DigitalOcean use simple key/secret pairs with no sub-key scoping. Workable for small teams, problematic for any setup where least-privilege access matters per service.
# Wasabi IAM policy - identical syntax to AWS
aws iam create-policy \
--endpoint-url https://iam.wasabisys.com \
--policy-name ReadOnlyPrefix \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::mybucket/public/*"
}]
}'