A bulletproof bash script for streaming large tar archives directly to disk with automatic retry logic, stall detection, and progress monitoring.
- ✅ Streaming extraction - Downloads and extracts simultaneously, no temporary files
- ✅ Production-grade reliability - 10 automatic retries with exponential backoff
- ✅ Stall detection - Watchdog automatically kills and retries stalled downloads
- ✅ Progress monitoring - Real-time status updates with speed, ETA, and percentage
- ✅ Compression support - Auto-detects and handles zstd, lz4, gzip, bzip2, xz, and plain tar
- ✅ Minimal disk usage - No temporary tar file, extracts on-the-fly
- ✅ Connection resilience - TCP keepalive, nodelay, and aggressive timeout handling
Perfect for:
- Blockchain snapshot restoration (Ethereum, Cosmos, etc.)
- Large database backups
- CI/CD deployment of large archives
- Any scenario where disk space is limited but reliability is critical
- bash 4.0+
- curl
- tar
- Compression tools (zstd, lz4, gzip, bzip2, xz) - only needed if using compressed archives
- Standard Unix utilities: du, awk, grep, stat, numfmt
export RESTORE_SNAPSHOT=true
export URL="https://example.com/snapshot.tar"
export DIR="/data"
./stream-download.shFROM alpine:3.22
RUN apk add --no-cache \
bash curl tar \
zstd lz4 gzip bzip2 xz \
coreutils dumb-init
COPY stream-download.sh /usr/local/bin/stream-download.sh
RUN chmod +x /usr/local/bin/stream-download.sh
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/bin/bash", "-c", "/usr/local/bin/stream-download.sh"]apiVersion: v1
kind: Pod
metadata:
name: snapshot-restore
spec:
initContainers:
- name: restore-snapshot
image: your-image:latest
env:
- name: RESTORE_SNAPSHOT
value: "true"
- name: URL
value: "https://snapshot.arbitrum.foundation/arb1/classic-archive.tar"
- name: DIR
value: "/storage"
- name: SUBPATH
value: "db"
- name: TAR_ARGS
value: "--strip-components=1"
volumeMounts:
- name: data
mountPath: /storage
containers:
- name: main
image: your-app:latest
volumeMounts:
- name: data
mountPath: /storage
volumes:
- name: data
persistentVolumeClaim:
claimName: your-pvc| Variable | Default | Description |
|---|---|---|
RESTORE_SNAPSHOT |
false |
Set to true to enable snapshot restore |
URL |
- | Required. URL of the snapshot to download |
DIR |
- | Required. Directory to extract snapshot to |
SUBPATH |
"" |
Subdirectory within DIR to extract to (e.g., db) |
TAR_ARGS |
"" |
Additional arguments to pass to tar (e.g., --strip-components=1) |
COMPRESSION |
auto |
Compression format: auto, none, gzip, bzip2, xz, zstd, lz4 |
RM_SUBPATH |
true |
Remove SUBPATH directory before extraction (set to false to keep) |
MAX_RETRIES |
10 |
Number of retry attempts before giving up |
┌─────────┐ ┌──────────────┐ ┌─────┐ ┌────────────┐
│ curl │───▶│ decompressor │───▶│ tar │───▶│ /storage/* │
└─────────┘ └──────────────┘ └─────┘ └────────────┘
│ │ │ │
└──────────────┴──────────────────┴──────────────┘
│
┌─────▼──────┐
│ monitors │
│ watchdog + │
│ status │
└────────────┘
- curl streams data from URL with connection monitoring
- decompressor (if needed) decompresses on-the-fly
- tar extracts files directly to disk
- monitors track progress and detect stalls
- Automatic retry - Up to 10 attempts with exponential backoff (10s, 20s, 30s...)
- Stall detection - Watchdog kills download if no progress for 3 minutes
- Connection monitoring - curl aborts if speed drops below 100KB/s for 3 minutes
Updates every 30 seconds showing:
Status: 45% | 278GiB / 613GiB | Speed: 245MiB/s | ETA: 23m
Warnings on stall:
WARNING: No progress detected for 1 minute(s) (278GiB extracted)
WARNING: No progress detected for 2 minute(s) (278GiB extracted)
WARNING: No progress detected for 3 minute(s) (278GiB extracted)
WATCHDOG: Detected stall for 3 minutes, killing download to trigger retry
export RESTORE_SNAPSHOT=true
export URL="https://snapshot.arbitrum.foundation/arb1/classic-archive.tar"
export DIR="/storage"
export SUBPATH="db"
export TAR_ARGS="--strip-components=1"
./stream-download.shexport RESTORE_SNAPSHOT=true
export URL="https://example.com/snapshot.tar.zst"
export DIR="/data"
export COMPRESSION="zstd" # or use "auto" to auto-detect
export MAX_RETRIES=5
./stream-download.shexport RESTORE_SNAPSHOT=true
export URL="https://example.com/snapshot.tar"
export DIR="/data"
export SUBPATH="database"
export RM_SUBPATH="false" # Don't delete existing data
./stream-download.shCheck connection stability:
# Test download speed
curl -o /dev/null https://your-snapshot-url.tar
# Check if server supports HTTP keepalive
curl -I https://your-snapshot-url.tar | grep -i "keep-alive"Increase retry attempts:
export MAX_RETRIES=20The watchdog detects stalls after 3 minutes of no progress. If your connection is very slow but stable:
Option 1: Accept longer extraction time - the watchdog will trigger retry Option 2: Use the chunked download version instead (see below)
This script uses minimal space (extracts on-the-fly), but you need enough space for the extracted data.
Check space:
df -h /storageIf you need resume capability and have extra space, use the chunked download version instead.
Why? Tar archives must be read sequentially. Jumping to a mid-point causes tar to see garbage data and fail to extract correctly.
Mitigation:
- 10 retry attempts
- Stall detection and auto-recovery
- Connection monitoring
- Most downloads succeed on first attempt with good internet
This streaming approach prioritizes:
- ✅ Minimal disk space usage
- ✅ Immediate file availability
- ✅ Simple, predictable behavior
The trade-off is that failed downloads restart from the beginning. However, with 10 retry attempts, stall detection, and connection monitoring, the vast majority of downloads complete successfully on the first attempt.
| Snapshot Size | Network Speed | Extraction Time |
|---|---|---|
| 100GB | 100Mbps | ~2.5 hours |
| 500GB | 100Mbps | ~12 hours |
| 1TB | 1Gbps | ~2.5 hours |
- Network - Usually the limiting factor
- Disk I/O - Can bottleneck on slow disks (HDD vs SSD)
- CPU - Decompression (zstd, lz4) can be CPU-intensive
- Uses
--insecureflag for curl (skips SSL verification) - Consider using
--cacertinstead for production with proper SSL - No authentication - assumes public snapshot URLs
- No integrity verification - consider adding checksums
Edit the script to add curl options:
curl --fail --location \
--cacert /path/to/ca-bundle.crt \ # Add SSL verification
--speed-limit 102400 \
--speed-time 180 \
"$URL" | ...Edit stall detection threshold:
# In watchdog function
local max_stalls=5 # Wait 5 minutes instead of 3Comment out watchdog in stream_and_extract function:
# watchdog &
# local watchdog_pid=$!For issues, questions, or contributions, please refer to your internal documentation or contact your DevOps team.
This project is based on the excellent init-stream-download tool by GraphOps. We've extended it to support additional compression formats while maintaining full backward compatibility with the original.