performance2025-06-30·8·12/348

Transferring Large Files (4GB+) on Linux

Guide to transferring files larger than 4GB on Linux, covering filesystem limitations, rsync, split/merge, and network transfer optimization.

Transferring Large Files (4GB+) on Linux

When you need to transfer multi-gigabyte files between servers or to external storage, you quickly discover filesystem limitations and network bottlenecks. Moving a 12GB database backup from my Lisbon server to a backup location taught me several practical techniques.

Environment

  • Source: Ubuntu 22.04 LTS, ext4 filesystem
  • Destination: USB drive (FAT32), remote NAS, cloud storage
  • File: PostgreSQL database backup (12GB)
$ ls -lh /data/backups/db-backup-2025-06-30.sql.gz
-rw-r--r-- 1 joel joel 12G Jun 30 08:00 /data/backups/db-backup-2025-06-30.sql.gz

The Problem

FAT32 4GB Limitation

$ cp /data/backups/db-backup-2025-06-30.sql.gz /mnt/usb/
cp: failed to close '/mnt/usb/db-backup-2025-06-30.sql.gz' 
Error: File too large for destination filesystem

# Check filesystem type
$ mount | grep sdb1
/dev/sdb1 on /mnt/usb type vfat (rw,relatime,uid=1000,gid=1000,fmask=0022)

FAT32 has a hard 4GB file size limit. This is not a Linux issue but a filesystem constraint.

Network Transfer Speed Issues

# Basic scp is slow for large files
$ scp /data/backups/db-backup-2025-06-30.sql.gz remote:/backups/
# Transferring at 5MB/s - would take 40+ minutes

Analysis

Step 1: Check Filesystem Type and Limits

# Check all filesystems
$ df -T
Filesystem     Type  1K-blocks    Used Available Use% Mounted on
/dev/sda1      ext4   51200000 38400000  12800000  75% /
/dev/sdb1      vfat    32768000 20480000  12288000  63% /mnt/usb
/dev/sdc1      exfat  256000000 128000000 128000000  50% /mnt/nas

# Check specific filesystem limits
$ tune2fs -l /dev/sda1 | grep -i "block size"
Block size:               4096

$ stat -f /mnt/usb/
  File: "/mnt/usb/"
    ID: 5242890    Namelen: 255     Type: vfat

Step 2: Measure Current Transfer Speed

# Test network speed
$ dd if=/dev/zero of=/data/testfile bs=1G count=1 2>&1
1+0 records in
1+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 3.2 s, 335 MB/s

# Test network transfer
$ pv /data/backups/db-backup-2025-06-30.sql.gz | ssh remote "cat > /backups/db-backup.sql.gz"
5.12GB 0:02:30 [5.45MB/s] [==>                       ] 42% ETA 0:03:20

Solution

Option 1: Format USB Drive with ext4

# WARNING: This erases all data on the drive
$ sudo mkfs.ext4 /dev/sdb1

# Remount
$ sudo mount /dev/sdb1 /mnt/usb

# Verify
$ df -T /mnt/usb
Filesystem     Type  1K-blocks    Used Available Use% Mounted on
/dev/sdb1      ext4   32768000 20480000  12288000  63% /mnt/usb

# Now copy works
$ cp /data/backups/db-backup-2025-06-30.sql.gz /mnt/usb/
# Success!

Option 2: Split File for FAT32

# Split into 2GB chunks
$ split -b 2G /data/backups/db-backup-2025-06-30.sql.gz /mnt/usb/db-backup-
$ ls -lh /mnt/usb/db-backup-*
-rw-r--r-- 1 joel joel 2.0G Jun 30 08:15 db-backup-aa
-rw-r--r-- 1 joel joel 2.0G Jun 30 08:15 db-backup-ab
-rw-r--r-- 1 joel joel 2.0G Jun 30 08:15 db-backup-ac
-rw-r--r-- 1 joel joel 2.0G Jun 30 08:15 db-backup-ad
-rw-r--r-- 1 joel joel 2.0G Jun 30 08:15 db-backup-ae
-rw-r--r-- 1 joel joel 2.0G Jun 30 08:15 db-backup-af
-rw-r--r-- 1 joel joel   48M Jun 30 08:15 db-backup-ag

# Reassemble on destination
$ cat /mnt/usb/db-backup-* > /data/db-backup-2025-06-30.sql.gz

Option 3: rsync with Checksum and Resume

# rsync is ideal for large transfers - supports resume
$ rsync -avz --progress --checksum \
    /data/backups/db-backup-2025-06-30.sql.gz \
    remote:/backups/

sending incremental file list
db-backup-2025-06-30.sql.gz
  12,884,901,888  87%   12.45MB/s  0:14:23

Resume after interruption:

# rsync automatically resumes partial transfers
$ rsync -avz --partial --progress \
    /data/backups/db-backup-2025-06-30.sql.gz \
    remote:/backups/

Option 4: Compress Before Transfer

# Compress to reduce transfer time
$ pigz -p 4 -c /data/backups/db-backup-2025-06-30.sql.gz > /tmp/db-compressed.tar.gz
$ ls -lh /tmp/db-compressed.tar.gz
-rw-r--r-- 1 joel joel 4.2G Jun 30 08:30 /tmp/db-compressed.tar.gz

# Transfer compressed file
$ rsync -avz --progress /tmp/db-compressed.tar.gz remote:/backups/

# Decompress on destination
$ pigz -d /backups/db-compressed.tar.gz

Option 5: Use tar over SSH (Stream)

# Stream without creating intermediate file
$ tar czf - /data/backups/db-backup-2025-06-30.sql.gz | \
    ssh remote "tar xzf - -C /backups/"

# Or with progress
$ tar czf - /data/backups/db-backup-2025-06-30.sql.gz | \
    pv -s 12G | \
    ssh remote "tar xzf - -C /backups/"

Option 6: Multi-Connection Transfer with pigz

# Parallel compression
$ pigz -p 8 -c db-backup-2025-06-30.sql.gz > db-compressed.tar.gz

# Parallel SSH transfer using mbuffer
# On receiver:
$ mbuffer -s 128k -m 1G -p 3001 > /backups/db-backup.tar.gz

# On sender:
$ pigz -p 8 -c db-backup-2025-06-30.sql.gz | \
    nc remote 3001

Comparison Table

MethodSpeedResumeBest For
cpFastNoSame filesystem
rsyncGoodYesNetwork transfer
split/catMediumNoFAT32 destination
tar+sshFastNoStreaming
pigz+ncFastestNoSame LAN

Lessons Learned

  1. Check filesystem type first - FAT32's 4GB limit is a common surprise.
  2. rsync with --partial is essential for unreliable network connections.
  3. Parallel compression with pigz can significantly reduce transfer time.
  4. Always verify checksums after large transfers:
$ md5sum /data/backups/db-backup-2025-06-30.sql.gz
$ ssh remote "md5sum /backups/db-backup-2025-06-30.sql.gz"
  1. Consider network optimization for large transfers:
# Increase TCP buffer sizes
$ sudo sysctl -w net.core.rmem_max=16777216
$ sudo sysctl -w net.core.wmem_max=16777216

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.