ibee
The Hidden Cost of Moving Data Between Services in the Cloud

The Hidden Cost of Moving Data Between Services in the Cloud

Betrand Yella
Betrand YellaFounder
August 19, 20269 min read

Most teams don’t get data costs wrong because they don’t know networking. They get them wrong because they treat “moving data” as a side effect of building features, not as a first-class cost driver.

The migration took six weeks. Three of them were spent undoing a decision made in the first hour. Your team moved a workflow from one service to another because “it’s cleaner.” Then your cloud bill started climbing every week, even though traffic stayed flat. When you finally looked, the issue wasn’t compute or storage. It was the number of times you shipped the same bytes across service boundaries, plus the retries and reprocessing that multiplied that shipping.

You need to stop thinking in terms of “services” and start thinking in terms of “bytes moved, how often, and where they land.”

The real problem: bytes multiplied by hops

In cloud systems, data movement is not one event. It’s a chain. One read here triggers a write there. That write triggers another read. Then you transform, serialize, validate, log, and ship again.

Every hop can add at least one of these cost components:

You pay network charges when data leaves a boundary (egress, inter-region, cross-zone). You pay service request fees when you call managed APIs for reads, writes, events, or batch transfers. You pay processing fees when managed services transform data as part of the call. You pay storage for intermediate artifacts (landing zones, staging tables, temporary objects) and again later for moving those artifacts to the next stage. And you pay the “oops tax” when retries happen because payload size, timeouts, or throttling weren’t accounted for.

Here’s the trap: even if each hop looks cheap in isolation, the total cost scales with the number of hops times the number of times you touch the same dataset.

Most teams get this wrong in a very specific way: they design pipelines around developer convenience, not around data locality. They land data in object storage, then spin up compute to transform it into a new format, then load a warehouse, then export results back out for another service, then do analytics again on the same raw data because “we need a different view.” You end up with three or four copies of the dataset and five to ten transfers per record. That’s how you get a bill that grows with architecture complexity, not with user growth.

Required visual block (architecture diagram)

Cloud services connected by arrows labeled with data movement cost drivers

Shows how repeated service-to-service hops multiply egress, requests, retries, and intermediate artifacts.

Where the hidden cost shows up in your bill

You can’t optimize what you can’t name. So name the cost buckets that show up when data moves.

Network egress is the usual headline. But don’t stop there. Request-based charges matter when your design sends lots of small payloads. Data processing charges matter when managed services process the bytes you just moved. Retries and reprocessing matter because they multiply transfers, not just CPU time. Intermediate storage matters because it creates additional bytes to move later.

The most common “gotchas” I see in production architectures:

If you split a record into many events and publish each event separately, you pay request fees per event and you increase serialization overhead. If your consumer reads raw objects repeatedly for different downstream models, you turn one transfer into many. If you do cross-region replication for disaster recovery and keep it “always on,” you effectively double transfer volume. If you log or audit by shipping full payloads to a logging pipeline, you create hidden duplication. If you use a format conversion step (JSON to Parquet, row to column) but you keep the original and the converted copy, you pay for both the write and the later read.

Even worse, these costs interact. A retry doesn’t just re-run compute. It often re-sends the payload, re-uploads the object, re-creates the intermediate artifact, and re-triggers downstream consumers. One small failure becomes a full pipeline re-run.

The “data movement multiplier” model

Treat “data moved” like a multiplier across the whole system. If one record moves 3 times across boundaries, and you process 10 million records per day, you moved 30 million record payloads. If each payload averages 200 KB, you just moved about 6 TB per day. Now add retries, fan-out, and cross-region DR and you can easily end up at 2x to 5x that volume.

That’s why compute optimizations often disappoint. You can make your services 30% faster and still lose money because the bytes you ship never got smaller.

Fan-out, retries, and locality: the three multipliers

You need to look at your architecture through three lenses: fan-out, retries, and locality.

Fan-out is when one incoming event spawns multiple downstream consumers. If you broadcast the full payload to each consumer, total egress scales with the number of consumers. A common mistake is assuming “events are small.” They aren’t small once you include nested JSON, base64 blobs, or large metadata. Even if the event is small, you also pay per request, and request charges add up quickly.

Retries are the silent killer. Teams usually tune timeouts and concurrency for throughput, not for cost. Then throttling happens, retries kick in, and suddenly you reprocess the same data multiple times. If your retry strategy is “at-least-once” without idempotency or deduplication, you might process duplicates downstream too. That means extra reads, extra writes, extra storage, and extra exports.

Locality is the design choice that determines whether you pay egress at all. If your compute runs in one region or network segment and your data sits elsewhere, you will move bytes. If your pipeline repeatedly fetches the same dataset from storage because it’s not cached near the compute, you will move bytes again. If you keep moving data between object storage and databases because “we need both,” you’ll pay for the shuffle.

Most teams get locality wrong by defaulting to “managed services everywhere,” then connecting them with the simplest integration. It works on day one. It bleeds money on day sixty.

One real-world example (single company)

A mid-market streaming company I worked with had a “simple” architecture: ingest events, enrich them with a managed enrichment service, store enriched events in object storage, then run daily analytics by exporting from storage into a query engine in a different region. Traffic was stable, but costs climbed because the daily analytics export re-read the same enriched objects repeatedly and did it from a different region. The fix was not “tune the query engine.” The fix was to keep the analytics data local to the compute and avoid exporting full objects when the analytics only needed a subset of fields.

How to measure data movement cost without guessing

Most teams try to estimate costs from diagrams. That fails. You need to measure where the bytes actually go.

Start by writing down your data paths as “source to sink” flows, not as “service to service” relationships. For each flow, capture:

Where the data originates (object store, database, stream). Where it lands next (another service, another region, another storage tier). How many times it moves (number of hops). How many times it gets processed (retries, reprocessing, scheduled backfills). How large it is (payload size distribution, not averages). Whether the payload includes unnecessary fields.

Then map those flows to billable events. You don’t need exact cents to start. You need relative multipliers. If one flow moves 10x more bytes than the others, it will dominate your bill even if it has “low per-GB pricing.”

Use logs and metrics to find the retry rate and the failure modes. If you see 2% retries but each retry re-uploads 1 GB objects, that’s already a 2% extra transfer tax on the largest objects. If you see fan-out with 6 consumers and each consumer gets a full payload, you can compute the expected multiplier immediately.

A practical IBEE angle (only where it helps)

If you’re storing datasets and intermediate pipeline artifacts in Object Storage and you’re moving them between services inside your platform, the cost often hinges on egress. IBEE’s ₹2/GB egress can be dramatically lower than typical India-region egress pricing elsewhere, which matters when your pipeline design forces multiple reads and exports. The key is you still have to reduce hops and payload size; pricing alone won’t save a badly shaped pipeline.

Concrete fixes that cut data movement, not just compute

You don’t need a rewrite. You need targeted changes that reduce bytes moved per unit of business value.

First, stop moving full records when you only need projections. If your downstream only needs 5 fields, don’t ship 50. This reduces payload size and also reduces serialization and request overhead.

Second, collapse steps that force intermediate artifacts. If you land raw data, transform it, and then load it again as a second copy, you probably created a staging pattern that duplicates bytes. Consider streaming transformations or writing directly into the final layout format once. If you must stage, stage smaller artifacts, not full payloads.

Third, fix locality. Run compute where the data is. Keep your analytics query engine close to the storage it reads. If you do cross-region DR, accept the cost but make sure you’re not doing cross-region reads for normal operations.

Fourth, control fan-out. Instead of broadcasting the full payload, publish references (object keys, IDs) and let consumers fetch only what they need, preferably from local storage. If you must broadcast, compress and strip fields. Also ensure your consumers can handle duplicates so retries don’t multiply work.

Fifth, make retries cheap and idempotent. Add deduplication keys, idempotent writes, and backoff strategies. If you can’t make processing idempotent, you’ll pay for duplicates in storage and downstream systems.

Sixth, treat observability carefully. Don’t log entire payloads. Log metadata and hashes. Your logging pipeline is still a data movement pipeline, and it can become a major source of hidden bytes.

Optional visual block (comparison table)

Comparison of cost levers and their impact on egress, requests, and retries

Summarizes which architecture changes reduce which cost components.

The one action you can take today

Pick one of your top two cloud cost line items that you can’t explain (often egress, storage reads, or managed service requests). Then do this exact exercise:

  1. Choose a single end-to-end workflow (ingest to final output).
  2. Trace every hop the bytes take across services and regions.
  3. For each hop, estimate bytes moved per successful workflow run and multiply by retry rate.
  4. Identify the hop with the highest bytes moved per run.
  5. Cut that hop first, either by reducing payload size, collapsing stages, or fixing locality.

If you do only one thing, do step 5 on the biggest-bytes hop. That’s how you stop paying for architectural “cleanliness” and start paying for business outcomes.

Related articles