Best Practices 5 min read by syncopio Team

Pre-Migration Assessment: Why ping and iperf Aren't Enough

Synthetic benchmarks don't predict real migration performance. Here's how to assess network, storage, and parallelism before you start moving data.

iperf says you have 10 Gbps. Your migration runs at 200 MB/s. That’s 1.6 Gbps. Where did the other 8.4 go?

You ping the destination. 0.3ms. Beautiful. Your NFS transfers stall every 30 seconds anyway.

This is the gap between synthetic benchmarks and real migration performance. If you plan a cutover window based on iperf numbers, you will miss it. Here’s how to run an assessment that actually predicts what’s going to happen.

Why synthetic benchmarks lie

iperf measures raw TCP throughput between two hosts. It opens a socket, blasts data, and reports the rate. That number tells you the ceiling. It does not tell you what your migration will hit.

Real migrations are not raw TCP. They are NFS or SMB, which means:

  • Protocol overhead. Every file open, stat, read, write, close, chmod, and chtimes is a separate RPC call. Each one has headers, authentication, and round-trip latency.
  • Metadata operations. Creating directories, setting permissions, preserving timestamps. None of this shows up in iperf.
  • Connection setup. NFS mounts, Kerberos tickets, session negotiation. These happen once but can take seconds.
  • Attribute caching. The client caches metadata and sometimes serves stale data, which causes retries and verification failures.
  • Small file penalty. iperf tests with large buffers. Your dataset has 400,000 files under 4KB. Those files spend more time on syscall overhead than on data transfer.

A realistic rule of thumb: expect 15% to 40% of your iperf throughput for NFS file transfers. For SMB, expect 10% to 30%. For workloads dominated by small files, expect even less.

iperf is necessary, not sufficient

Still run iperf. If your raw link can’t handle the bandwidth, nothing else matters. But don’t stop there. iperf tells you the upper bound. The rest of this guide tells you where your actual throughput will land.

Network jitter: the hidden killer

A network that averages 1ms latency sounds great. But if it spikes to 50ms every 10 seconds, your migration is in trouble.

NFS operations are synchronous. A single 50ms spike means one worker stalls for 50ms. If that happens during a metadata-heavy phase (creating thousands of directories), you get a cascade: the worker blocks, its queue backs up, the NFS server processes the delayed burst all at once, and throughput collapses.

Average latency hides this. You need to look at the tail.

Measure jitter, not just latency:

# 60-second ping with timing stats
ping -c 60 -i 0.5 nfs-server.local | tail -1
# Look at the max value in min/avg/max/mdev

# Better: run mtr for 100 probes
mtr -r -c 100 nfs-server.local
# Watch for packet loss and high std deviation

What the numbers mean:

Jitter patternImpact on migration
avg 0.5ms, max 2msExcellent. Full speed.
avg 1ms, max 15msNoticeable. Some retries.
avg 1ms, max 50ms+Significant throughput loss. Investigate.
Any packet loss > 0.1%TCP retransmits will tank your throughput. Fix first.

If you see periodic spikes, check for competing traffic. Backup jobs, replication, VM snapshots. Anything that saturates the link or the storage controller for a few seconds will cause exactly this pattern.

Test at the right time

Run your network tests during the window you plan to migrate in. A network that’s clean at 2 AM might be saturated at 2 PM when backups run. Test conditions should match migration conditions.

Parallel stream testing: finding the sweet spot

One transfer stream will never saturate a 10Gbps link. NFS has per-operation latency, and a single thread can only have one operation in flight at a time. The answer is parallel streams. But how many?

Too few and you waste bandwidth. Too many and the NFS server thrashes. Its disk heads seek constantly, its CPU burns on lock contention, and total throughput actually drops.

The sweet spot depends on your hardware. You need to test it.

Quick parallel throughput test:

# Create a test directory with representative files
mkdir -p /tmp/parallel-test
# Generate 100 files of ~100MB each
for i in $(seq 1 100); do
  dd if=/dev/urandom of=/tmp/parallel-test/file_$i bs=1M count=100 2>/dev/null
done

# Test with 1, 2, 4, 8, 16 parallel streams
for streams in 1 2 4 8 16; do
  echo "--- $streams streams ---"
  sync; echo 3 > /proc/sys/vm/drop_caches
  time find /tmp/parallel-test -type f | \
    xargs -P $streams -I {} cp {} /mnt/nfs-dest/
  rm -rf /mnt/nfs-dest/*
done

Graph the results. You’ll see something like:

StreamsThroughputNotes
1180 MB/sSingle-threaded baseline
2340 MB/sNearly linear scaling
4580 MB/sGood scaling
8720 MB/sDiminishing returns
16650 MB/sServer contention, throughput drops

In this example, 8 streams is the sweet spot. Going to 16 actually makes things worse. Your numbers will be different. The point is to test instead of guess.

Test with your actual NFS server

The optimal stream count depends on the NFS server’s disk layout (SSD vs spinning), RAID config, CPU, and available memory. A modern all-flash NAS might scale linearly to 32 streams. An older spinning-disk array might peak at 4. There is no universal answer.

File sampling: test with realistic data

Don’t benchmark with a folder of 1KB config files and then migrate a NAS full of 500MB video renders. The performance characteristics are completely different.

Sample across your actual dataset:

# Sample 200 files from each size bracket
find /path/to/source -type f -size -128k | shuf -n 200 > /tmp/sample-small.txt
find /path/to/source -type f -size +128k -size -100M | shuf -n 200 > /tmp/sample-medium.txt
find /path/to/source -type f -size +100M | shuf -n 200 > /tmp/sample-large.txt

# Transfer each sample and measure
for bracket in small medium large; do
  echo "--- $bracket files ---"
  sync; echo 3 > /proc/sys/vm/drop_caches
  time rsync -a --files-from=/tmp/sample-$bracket.txt / /mnt/nfs-dest/
  rm -rf /mnt/nfs-dest/*
done

This gives you per-bracket transfer rates. Combine them with your size distribution (see our file count planning guide) and you get a real estimate.

Storage assessment: how much actually needs to move?

Before you spend a weekend on a cutover, ask: does all of it need to move?

Capacity and stale data:

# Total size
du -sh /path/to/source

# Files not accessed in 2+ years
find /path/to/source -type f -atime +730 -printf '%s\n' | \
  awk '{ total += $1 } END { printf "Stale: %.1f GB\n", total/1073741824 }'

# Files modified in the last 30 days (active data)
find /path/to/source -type f -mtime -30 -printf '%s\n' | \
  awk '{ total += $1 } END { printf "Active: %.1f GB\n", total/1073741824 }'

On a typical enterprise NAS, 30% to 60% of data hasn’t been touched in over a year. That changes your migration strategy. Maybe you move active data first, cut over, then migrate stale data in the background. Maybe some of it goes to cold storage instead.

Growth rate:

# Compare snapshots if available, or check quota reports
# Quick estimate from recent files:
find /path/to/source -type f -mtime -90 -printf '%s\n' | \
  awk '{ total += $1 } END { printf "Last 90 days growth: %.1f GB\n", total/1073741824 }'

Growth rate determines whether you need a cutover window at all. If the dataset grows by 5GB/day and you can sustain 500MB/s, incremental syncs can keep up indefinitely. You do a bulk transfer once, then incremental updates until you flip DNS. No downtime window needed.

syncopio advantage

syncopio’s discovery scan gives you the full picture before you move a byte: file count by size bracket, stale data analysis, directory depth, and per-bracket transfer estimates. It tests the actual transfer path between source and destination, not synthetic benchmarks. You see realistic throughput numbers for your specific hardware and network before committing to a cutover window.

Planning template: bandwidth times time equals data moved (sort of)

Here’s the formula everyone uses:

transfer_time = total_bytes / throughput

Here’s why it’s wrong: it assumes constant throughput. In practice, you need overhead factors.

Realistic planning formula:

effective_throughput = iperf_throughput × protocol_factor × parallelism_factor × jitter_factor

transfer_time = total_bytes / effective_throughput
FactorTypical rangeNotes
Protocol (NFS/SMB overhead)0.15 to 0.40Lower for small files, higher for large
Parallelism0.5 to 0.95Depends on stream count vs sweet spot
Jitter / contention0.7 to 0.95Worse during business hours
Combined0.05 to 0.35Multiply all three

Example: 10 Gbps link, 50TB to move, mixed workload:

effective = 10 Gbps × 0.25 (combined factor) = 2.5 Gbps = 312 MB/s

50,000,000 MB / 312 MB/s = 160,256 seconds ≈ 44 hours

Add 20% buffer for retries, verification, and surprises: about 53 hours. That’s your cutover window. Not the 11 hours that raw iperf would suggest.

Always add buffer

Plan for 1.2x to 1.5x your calculated time. Networks degrade. Servers hiccup. Files get locked. The buffer is not pessimism. It’s realism.

The assessment checklist

Before you start any migration over 1TB, run through this:

  1. iperf3 between source and destination. This is your ceiling. If it’s not what you expect, fix the network first.
  2. Jitter test during your planned migration window. 60+ seconds of ping, check max latency and packet loss.
  3. Parallel stream test. Find the point where more streams stop helping.
  4. File size sampling. Test transfers with files from each size bracket in your dataset.
  5. Storage audit. Total size, stale data percentage, growth rate over 90 days.
  6. Calculate realistic transfer time. Use the overhead factors above, not raw throughput.
  7. Decide on strategy. Big bang cutover, incremental sync, or phased by priority?

Skip any of these and you’re guessing. Guessing is how you end up rebooking your cutover window at 3 AM on a Sunday.


Further reading:

Ready to simplify your migrations?

See how syncopio can save you hours on every migration project.

Request a Demo