Mid-level 12 min · April 10, 2026

AWS Snowball — Manifest Overwrites That Reject Devices

Overwritten manifest.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Snowball family: Snowball (80TB, $300/job), Snowball Edge Storage Optimized (210TB, $400-600), Snowball Edge Compute Optimized (52 vCPUs + GPU option), Snowmobile (100PB truck).
  • Timeline: 10-20 business days. Order (3d) + ship to you (5d) + copy (varies) + ship back (5d) + AWS loading (3d). Plan accordingly.
  • Small files are the enemy: 10M 1KB files = 10GB total. Copy time: ~2-3 days at 5MB/s. Tar into 1GB archives: 10 archives, copy time ~2 minutes. 10-50x speedup.
  • Validate before shipping: manifest file count matches source, KMS key ARN explicit, device status 'Ready to ship'. A rejected device = start over, 2-3 weeks lost.
  • Production killer: overwriting files on device corrupts manifest. AWS rejects device. Never re-run copy scripts on same device. Cancel job, order new device, start fresh.
✦ Definition~90s read
What is AWS Snowball — Manifest Overwrites That Reject Devices?

AWS Snowball is a petabyte-scale physical data transport service that solves the fundamental bandwidth bottleneck problem: when transferring 100+ TB over a 1 Gbps link takes weeks, you instead ship hard drives. Snowball devices are ruggedized, tamper-proof storage appliances with built-in computing — not just dumb shipping containers.

Imagine you have a warehouse full of paper records that need to go into a digital archive.

Each device runs a hardened version of Amazon Linux, supports E Ink shipping labels that update via cellular, and uses hardware-validated encryption with keys managed through AWS KMS. The service exists because for many real-world migration scenarios (data center decommissions, media archives, regulatory air-gapped transfers), the math simply doesn't work for network transfer: moving 1 PB over 10 Gbps takes ~10 days of full saturation, while Snowball Edge devices can move that in a week including shipping time.

You should not use Snowball for sub-10 TB transfers (use AWS DataSync or S3 Transfer Acceleration instead), nor for data that requires sub-hourly freshness (use Direct Connect). The Snow family includes Snowcone (8 TB, 2.2 lbs, for edge computing in austere environments), Snowball Edge Storage Optimized (80 TB usable, with 40 vCPUs for local processing), and Snowmobile (100 PB per 45-foot shipping container, for exabyte-scale migrations).

The key operational reality: manifest overwrites that reject devices happen when you modify job parameters after device preparation — the device's hardware-bound manifest becomes immutable, and any mismatch between the console job configuration and the device's embedded manifest causes immediate rejection at the first power-on handshake.

Plain-English First

Imagine you have a warehouse full of paper records that need to go into a digital archive. You could scan every page and upload it over the internet — that would take months. Or you could load all the paper into a secure truck, drive it to the archive facility, and unload it there in a day. AWS Snowball is that truck. Instead of uploading petabytes over a network connection, you copy data onto a physical device, ship it to AWS, and they load it into S3 for you.

AWS Snowball is physical data transport for datasets too large to transfer over the network. At 1Gbps, 80TB takes ~7 days direct transfer. Snowball round-trip takes ~16 days. At 1Gbps, Snowball is not faster until ~200TB. At 10Gbps, Snowball loses to direct transfer until ~1.8PB.

Snowball is not always faster. Calculate the crossover point before ordering.

The Snowball family: Snowball Standard (80TB, no compute), Snowball Edge Storage Optimized (210TB, 40 vCPUs), Snowball Edge Compute Optimized (100TB, 52 vCPUs, optional NVIDIA GPU), Snowmobile (100PB shipping container).

This article covers the five rules that separate a 2-week migration from a 6-week disaster: tar small files, never re-run copy scripts, validate manifests, test network ports, and plan for the full 5-phase timeline. Miss a rule and your device gets rejected at AWS loading — a 2-3 week restart.

Why AWS Snowball Is Not Just a Shipping Container

AWS Snowball is a petabyte-scale data transport solution that uses ruggedized, tamper-resistant physical devices to transfer large datasets into or out of AWS when network transfer is impractical. The core mechanic: you order a device, copy data locally, ship it back, and AWS imports it into an S3 bucket — but the manifest file that controls device acceptance is cryptographically signed and must match the device's unique ID. In practice, Snowball devices are encrypted with a 256-bit key derived from the manifest and unlock code; without the correct manifest, the device refuses all operations. This means a manifest mismatch — from a typo in the job ID or a stale file — renders the device unusable until AWS issues a new one. Teams typically use Snowball for 10 TB+ migrations, disaster recovery seeding, or regulatory data transfers where bandwidth is limited (e.g., 1 Gbps link would take 100+ days for 100 TB). The critical property: the manifest is a single point of failure — lose it, and the device is a brick.

Manifest Mismatch = Device Rejection
A common mistake is reusing a manifest from a previous job; each device has a unique ID, and the manifest is bound to that ID — no override possible.
Production Insight
A team migrating 50 TB of logs reused a manifest from a canceled job, causing the device to reject all unlock attempts and delaying the migration by 3 days.
The exact symptom: the Snowball client returns 'Manifest validation failed: device ID mismatch' with no further details.
Rule of thumb: always download a fresh manifest per device from the AWS console immediately before use, and store it in a secure, versioned location.
Key Takeaway
The manifest is the device's identity — treat it like a private key.
Snowball is for bulk, offline transfer when network is too slow or costly — not for small or frequent syncs.
Plan for a 2-3 day shipping delay each way; the total transfer time is dominated by logistics, not copy speed.

Snowball Family and Capacity Calculator

The AWS Snow family consists of three physical data transport devices, each designed for different scales.

Snowball Standard: 80TB usable storage, no compute, $300 per job + shipping. Best for one-time data center migration, initial cloud migration under 80TB.

Snowball Edge Storage Optimized: 210TB storage, 40 vCPUs, 80GB RAM, $400-600 per job. Best for bulk transfer with light compute at edge, content distribution, disaster recovery.

Snowball Edge Compute Optimized: 100TB storage, 52 vCPUs, 208GB RAM, optional NVIDIA V100 GPU. Best for heavy compute at edge, ML inference in disconnected environments, military field operations, oil rig data processing.

Snowmobile: 100PB capacity in a 45-foot shipping container, approximately $0.005/GB (~$500K per 100PB). Requires semi-truck, dedicated security escort, site preparation. Best for exabyte-scale data center consolidation, media archive migration.

Decision point: if your dataset is under 80TB and you have stable 10Gbps+ network, direct transfer is likely faster. Snowball is not always faster — calculate before ordering.

Crossover formula: direct_days = (data_tb 8) / (network_gbps 0.0864). Snowball_days = 14 (shipping) + 2 (AWS loading) + (data_tb / 8.64) (copy at 100MB/s). At 1Gbps, crossover ~200TB. At 10Gbps, crossover ~1.8PB.

io/thecodeforge/aws/snowball_capacity_calculator.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package io.thecodeforge.aws;

# Snowball capacity and transfer time calculator.
# Use this to determine whether Snowball or direct transfer is faster.

from dataclasses import dataclass
from enum import Enum


class SnowballDevice(Enum):
    """Snowball device tiers with usable capacity in TB."""
    SNOWBALL_STANDARD = 80
    SNOWBALL_EDGE_STORAGE = 210
    SNOWBALL_EDGE_COMPUTE = 100
    SNOWBALL_EDGE_COMPUTE_GPU = 100
    SNOWBALL_EDGE_EBS = 28
    SNOWMOBILE = 100_000


@dataclass
class TransferEstimate:
    device: SnowballDevice
    data_size_tb: float
    network_speed_gbps: float
    snowball_shipping_days: int = 14  # average round-trip shipping time
    aws_loading_days: int = 2  # time for AWS to load data into S3

    def direct_transfer_days(self) -> float:
        """Calculate days to transfer over network."""
        # Convert TB to bits, Gbps to bits/day
        data_bits = self.data_size_tb * 1e12 * 8
        bits_per_day = self.network_speed_gbps * 1e9 * 86400
        return data_bits / bits_per_day

    def snowball_transfer_days(self) -> float:
        """Calculate total Snowball timeline."""
        # Copy to device: assume 100MB/s average (realistic for mixed files)
        copy_speed_mbps = 100
        data_mb = self.data_size_tb * 1e6
        copy_days = data_mb / (copy_speed_mbps * 86400)
        return copy_days + self.snowball_shipping_days + self.aws_loading_days

    def recommendation(self) -> str:
        """Return recommendation: Snowball or direct transfer."""
        direct = self.direct_transfer_days()
        snowball = self.snowball_transfer_days()

        if self.data_size_tb > self.device.value:
            return (
                f"DATA EXCEEDS DEVICE CAPACITY. "
                f"{self.data_size_tb}TB > {self.device.value}TB. "
                f"Use multiple devices or Snowmobile."
            )

        if direct < snowball:
            return (
                f"DIRECT TRANSFER is faster. "
                f"Direct: {direct:.1f} days vs Snowball: {snowball:.1f} days. "
                f"At {self.network_speed_gbps}Gbps, network transfer wins."
            )
        else:
            savings = snowball - direct
            return (
                f"SNOWBALL is faster. "
                f"Snowball: {snowball:.1f} days vs Direct: {direct:.1f} days. "
                f"Saves {savings:.1f} days."
            )


def main():
    scenarios = [
        TransferEstimate(SnowballDevice.SNOWBALL_STANDARD, 50, 1),
        TransferEstimate(SnowballDevice.SNOWBALL_STANDARD, 50, 10),
        TransferEstimate(SnowballDevice.SNOWBALL_EDGE_STORAGE, 150, 1),
        TransferEstimate(SnowballDevice.SNOWBALL_EDGE_STORAGE, 150, 10),
        TransferEstimate(SnowballDevice.SNOWBALL_EDGE_STORAGE, 200, 1),
    ]

    for scenario in scenarios:
        print(f"\n--- {scenario.data_size_tb}TB @ {scenario.network_speed_gbps}Gbps ---")
        print(f"  Device: {scenario.device.name} ({scenario.device.value}TB)")
        print(f"  Direct transfer: {scenario.direct_transfer_days():.1f} days")
        print(f"  Snowball total:  {scenario.snowball_transfer_days():.1f} days")
        print(f"  {scenario.recommendation()}")


if __name__ == '__main__':
    main()
Output
--- 50TB @ 1Gbps ---
Device: SNOWBALL_STANDARD (80TB)
Direct transfer: 4.6 days
Snowball total: 14.0 days
DIRECT TRANSFER is faster. At 1Gbps, network transfer wins.
--- 150TB @ 1Gbps ---
Device: SNOWBALL_EDGE_STORAGE (210TB)
Direct transfer: 13.9 days
Snowball total: 14.0 days
SNOWBALL is faster. Saves 0.1 days.
--- 200TB @ 1Gbps ---
Device: SNOWBALL_EDGE_STORAGE (210TB)
Direct transfer: 18.5 days
Snowball total: 14.0 days
SNOWBALL is faster. Saves 4.5 days.
The Snowball Crossover Point
  • 1Gbps network: 200TB direct takes ~18.5 days. Snowball takes ~14 days. Snowball wins by 4.5 days.
  • 1Gbps network: 80TB direct takes ~7.4 days. Snowball takes ~16 days. Direct transfer wins by 8.6 days.
  • 10Gbps network: 500TB direct takes ~4.6 days. Snowball takes ~16 days. Direct transfer wins by 11.4 days.
  • 10Gbps network: 2PB direct takes ~18.5 days. Snowball takes ~16 days. Snowball wins by 2.5 days.
  • Rule: run capacity calculator before every Snowball job. If direct transfer is faster, use AWS DataSync instead.
Production Insight
A SaaS company used Snowball to migrate 45TB of analytics data to S3. They had 10Gbps Direct Connect. Snowball took 22 days. Direct transfer over 10Gbps would have taken ~10 hours. They lost 21 days.
Cause: assumed Snowball is always faster for large datasets. Didn't calculate crossover.
Effect: analytics platform delayed by 3 weeks, missing customer-facing launch deadline.
Impact: $50K in extended infrastructure costs and customer compensation.
Action: always calculate both timelines before committing to Snowball. Use AWS DataSync for network transfer when it's faster.
Key Takeaway
Snowball is not always faster than direct transfer. Calculate crossover point: direct_days vs Snowball_days. At 1Gbps, crossover ~200TB. At 10Gbps, crossover ~1.8PB. Always calculate before ordering.
Snowball vs Direct Transfer vs DataSync Decision
IfDataset < 10TB
UseUse direct S3 upload or AWS DataSync. Snowball shipping overhead not justified.
IfDataset 10-50TB with network > 1Gbps
UseUse direct transfer or DataSync. Calculate transfer time — likely faster than Snowball.
IfDataset 50-500TB with 1Gbps network
UseUse Snowball. Direct transfer takes weeks. Snowball saves time.
IfDataset > 500TB
UseUse Snowball Edge (210TB per device, multiple in parallel) or Snowmobile (100PB).
IfNeed local compute at edge (disconnected site)
UseUse Snowball Edge Compute Optimized. Run EC2/Lambda locally, sync to S3 when connected.
IfDataset > 10PB
UseUse Snowmobile. 100PB per container. Requires site preparation and security escort.
IfOngoing sync between on-premises and S3
UseUse AWS DataSync. Snowball is for one-time or infrequent bulk transfers, not continuous sync.

Snowball Job Lifecycle — 5 Phases and Failure Modes

A Snowball job has five phases. Each phase has distinct failure modes that can add weeks to your timeline.

Phase 1: Create Job (1-3 business days). Specify source/destination S3 bucket, KMS key, IAM role, device type. AWS provisions device and prepares manifest. Failure mode: IAM role or KMS key misconfiguration discovered later at device loading.

Phase 2: Receive Device (2-5 business days). AWS ships device to your address. Failure mode: shipping delays, incorrect address, device arrives damaged. Always verify address before ordering. If device damaged, contact AWS Support immediately for replacement.

Phase 3: Copy Data (varies). Install Snowball client or use S3 adapter. Copy files using s3 cp, s3 sync. Timeline depends on data size and file size distribution (tar small files!). Failure mode: manifest corruption from re-running copy scripts, KMS permission errors, network bottlenecks. Never re-run copy scripts on same device.

Phase 4: Ship Device Back (2-5 business days). Use pre-paid shipping label. Verify device status is 'Ready to ship' in console. Failure mode: shipping damage, lost in transit, incorrect return address.

Phase 5: AWS Loads Data (1-3 business days). AWS validates manifest, decrypts data, loads into S3. Failure mode: manifest mismatch (device rejected), KMS decryption failure, S3 bucket policy blocking writes. A rejected device means ordering new device, re-copying, re-shipping — 2-3 weeks lost.

Total typical timeline: 10-20 business days per device. Plan your migration timeline accordingly. If you have 200TB, use multiple devices in parallel.

io/thecodeforge/aws/snowball_job_manager.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import boto3
import json
import hashlib
import os
from dataclasses import dataclass, field
from typing import Optional, List
from datetime import datetime


@dataclass
class SnowballJobConfig:
    """Configuration for a Snowball migration job."""
    job_id: str
    device_type: str  # STANDARD | EDGE_STORAGE | EDGE_COMPUTE
    s3_bucket: str
    s3_prefix: str
    kms_key_arn: str
    iam_role_arn: str
    source_directory: str
    shipping_address: dict
    notification_sns_topic: str


@dataclass
class ManifestEntry:
    """Single entry in the transfer manifest."""
    file_path: str
    s3_key: str
    size_bytes: int
    sha256_hash: str
    last_modified: str


class SnowballJobManager:
    """Manages Snowball job lifecycle and pre-ship validation."""

    def __init__(self, config: SnowballJobConfig):
        self.config = config
        self.snowball_client = boto3.client('snowball')
        self.kms_client = boto3.client('kms')
        self.iam_client = boto3.client('iam')
        self.s3_client = boto3.client('s3')

    def generate_manifest(self, source_dir: str) -> List[ManifestEntry]:
        """Generate a manifest of all files to be transferred."""
        manifest = []
        total_size = 0
        skipped_files = []

        for dirpath, dirnames, filenames in os.walk(source_dir):
            for filename in filenames:
                filepath = os.path.join(dirpath, filename)

                # Check path length — Snowball S3 adapter rejects > 1024 bytes
                if len(filepath.encode('utf-8')) > 1024:
                    skipped_files.append(filepath)
                    continue

                try:
                    size = os.path.getsize(filepath)
                    total_size += size

                    sha256 = hashlib.sha256()
                    with open(filepath, 'rb') as f:
                        for chunk in iter(lambda: f.read(8192), b''):
                            sha256.update(chunk)

                    s3_key = os.path.relpath(filepath, source_dir)

                    manifest.append(ManifestEntry(
                        file_path=filepath,
                        s3_key=f"{self.config.s3_prefix}/{s3_key}",
                        size_bytes=size,
                        sha256_hash=sha256.hexdigest(),
                        last_modified=datetime.fromtimestamp(
                            os.path.getmtime(filepath)
                        ).isoformat(),
                    ))
                except OSError as e:
                    skipped_files.append(f"{filepath}: {e}")

        if skipped_files:
            print(f"WARNING: {len(skipped_files)} files skipped (path too long or inaccessible)")
            for f in skipped_files[:10]:
                print(f"  - {f}")

        return manifest

    def validate_pre_ship(self, manifest: List[ManifestEntry]) -> dict:
        """Run all pre-ship validations. Returns validation result."""
        errors = []
        warnings = []

        # 1. Validate KMS key exists and is accessible
        try:
            key_response = self.kms_client.describe_key(
                KeyId=self.config.kms_key_arn
            )
            key_state = key_response['KeyMetadata']['KeyState']
            if key_state != 'Enabled':
                errors.append(f"KMS key is not enabled: {key_state}")
        except Exception as e:
            errors.append(f"KMS key validation failed: {e}")

        # 2. Validate IAM role has required permissions
        required_actions = ['s3:PutObject', 's3:CreateBucket', 'kms:GenerateDataKey']
        for action in required_actions:
            try:
                response = self.iam_client.simulate_principal_policy(
                    PolicySourceArn=self.config.iam_role_arn,
                    ActionNames=[action],
                    ResourceArns=[f"arn:aws:s3:::{self.config.s3_bucket}/*"],
                )
                decision = response['EvaluationResults'][0]['EvalDecision']
                if decision != 'allowed':
                    errors.append(f"IAM role missing {action} permission")
            except Exception as e:
                errors.append(f"IAM permission check failed for {action}: {e}")

        # 3. Validate manifest has entries
        if not manifest:
            errors.append("Manifest is empty — no files to transfer")

        # 4. Validate total size fits on device
        total_gb = sum(e.size_bytes for e in manifest) / (1024 ** 3)
        device_limits = {
            'STANDARD': 80,
            'EDGE_STORAGE': 210,
            'EDGE_COMPUTE': 100,
        }
        limit = device_limits.get(self.config.device_type, 80)
        if total_gb > limit:
            errors.append(
                f"Data size {total_gb:.1f}TB exceeds device capacity {limit}TB"
            )
        elif total_gb > limit * 0.9:
            warnings.append(
                f"Data size {total_gb:.1f}TB is >90% of device capacity {limit}TB. "
                f"Consider leaving headroom."
            )

        # 5. Validate no files have paths > 1024 bytes
        long_paths = [e for e in manifest if len(e.file_path.encode('utf-8')) > 1024]
        if long_paths:
            errors.append(
                f"{len(long_paths)} files have paths > 1024 bytes. "
                f"S3 adapter will reject them."
            )

        return {
            'valid': len(errors) == 0,
            'errors': errors,
            'warnings': warnings,
            'manifest_entries': len(manifest),
            'total_size_gb': round(total_gb, 2),
            'device_type': self.config.device_type,
            'device_capacity_tb': limit,
        }

    def save_manifest(self, manifest: List[ManifestEntry], output_path: str):
        """Save manifest to JSON for post-transfer verification."""
        manifest_data = {
            'job_id': self.config.job_id,
            'generated_at': datetime.utcnow().isoformat(),
            'total_entries': len(manifest),
            'total_bytes': sum(e.size_bytes for e in manifest),
            'entries': [
                {
                    'file_path': e.file_path,
                    's3_key': e.s3_key,
                    'size_bytes': e.size_bytes,
                    'sha256': e.sha256_hash,
                    'last_modified': e.last_modified,
                }
                for e in manifest
            ],
        }

        with open(output_path, 'w') as f:
            json.dump(manifest_data, f, indent=2)

        print(f"Manifest saved: {len(manifest)} entries, "
              f"{manifest_data['total_bytes'] / (1024**3):.2f}GB")

    def verify_post_load(self, manifest_path: str) -> dict:
        """Verify S3 contents match manifest after AWS loading."""
        with open(manifest_path, 'r') as f:
            manifest_data = json.load(f)

        mismatches = []
        missing = []

        for entry in manifest_data['entries']:
            try:
                response = self.s3_client.head_object(
                    Bucket=self.config.s3_bucket,
                    Key=entry['s3_key'],
                )
                s3_size = response['ContentLength']
                if s3_size != entry['size_bytes']:
                    mismatches.append({
                        'key': entry['s3_key'],
                        'expected_size': entry['size_bytes'],
                        'actual_size': s3_size,
                    })
            except self.s3_client.exceptions.ClientError:
                missing.append(entry['s3_key'])

        return {
            'verified': len(mismatches) == 0 and len(missing) == 0,
            'total_checked': len(manifest_data['entries']),
            'missing': len(missing),
            'size_mismatches': len(mismatches),
            'missing_files': missing[:20],
            'mismatched_files': mismatches[:20],
        }
The Five-Phase Timeline
  • Phase 1 (Create): 1-3 days. Validate IAM, KMS, S3 before creating the job.
  • Phase 2 (Receive): 2-5 days. Shipping time varies by region and carrier.
  • Phase 3 (Copy): variable. Depends on data size and local network speed.
  • Phase 4 (Return): 2-5 days. Use pre-paid label. Verify 'Ready to ship' status before shipping.
  • Phase 5 (Load): 1-3 days. AWS validates manifest and loads into S3. Failures here mean starting over.
Production Insight
A healthcare company migrating 120TB of medical imaging data planned for 2-week Snowball migration. Actual timeline: 6 weeks. Phase 1: KMS cross-account validation failed — 1 week to resolve. Phase 2: Device arrived damaged — 1 week for replacement. Phase 3: 30% of files had paths >1024 bytes — 1 week to rename files. Phase 4: Return shipment delayed by holiday — 3 extra days.
Cause: no pre-flight validation of KMS permissions, file path lengths, shipping logistics.
Effect: 6-week timeline instead of planned 2 weeks.
Impact: data center decommission delayed by 1 month, $45K in extended lease costs.
Action: run pre-flight validation (KMS, IAM, file paths, shipping calendar) before creating Snowball job.
Key Takeaway
A Snowball job is a 10-20 business day pipeline with five phases and distinct failure modes. Validate everything before creating job: KMS permissions, IAM roles, file path lengths, shipping logistics. Never re-run copy scripts on same device — cancel and restart. Manifest is your only proof of what was loaded.
Snowball Job Phase Failure Recovery
IfJob creation fails — IAM or KMS error
UseFix IAM role or KMS key policy. Re-create job. No device cost incurred yet.
IfDevice arrives damaged or wrong configuration
UseContact AWS Support immediately. Request replacement. Do not use damaged device — it will be rejected at AWS.
IfCopy phase fails — manifest corruption or file errors
UseDo NOT re-run copy on same device. Cancel job, order new device, fix source issues, start fresh.
IfDevice shipped back but rejected by AWS — manifest mismatch
UseOrder new device. Re-copy from source with validated manifest. Adds 2-3 weeks. No fix for rejected device.
IfData loaded to S3 but checksums don't match source
UseFile AWS Support case with job ID and manifest. AWS can investigate loading process. Do not delete source data until checksums verified.

Performance Optimization: Taring Small Files and Parallel Copy

The copy phase is the only phase you control. Maximizing copy speed directly reduces your migration timeline.

Bottleneck analysis
  • Network interface: Snowball Edge has 1GbE management port AND 10GbE/25GbE data port. Connect to data port. A 1GbE connection limits you to ~100MB/s.
  • File size: Per-file overhead dominates copy time for small files (<1MB). Each file requires manifest entry, metadata update, S3 adapter handshake. 10 million 1KB files (10GB total) copy at 5-10MB/s → 2-3 days. Tar them into 10 1GB archives (still 10GB total), copy time ~2 minutes (100MB/s). 10-50x speedup.
  • Concurrency: S3 adapter is single-threaded per copy session. Run 4-8 parallel copy sessions to saturate network.
  • Source I/O: Spinning disks have 100x slower random read for small files than sequential reads for large files.

Rule: if >20% of your files are <1MB, tar small files into large archives before copying. The time spent tarring is recovered 10-50x in copy speed. Use parallel copy sessions. Always verify you are on the 10GbE data port, not the 1GbE management port.

io/thecodeforge/aws/snowball_copy_optimizer.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package io.thecodeforge.aws;

import os
import subprocess
import tarfile
import tempfile
from dataclasses import dataclass
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path


@dataclass
class CopyStrategy:
    """Copy strategy based on file size distribution."""
    small_file_threshold_mb: float = 1.0
    tar_batch_size_mb: float = 1024.0  # 1GB tar archives
    parallel_sessions: int = 4
    exclude_patterns: list = None

    def __post_init__(self):
        if self.exclude_patterns is None:
            self.exclude_patterns = [
                '*.tmp', '*.temp', '*.log', '*.cache',
                '.DS_Store', 'Thumbs.db', '__pycache__',
                '.git', 'node_modules',
            ]


class SnowballCopyOptimizer:
    """Optimizes data copy to Snowball device for maximum throughput."""

    def __init__(self, source_dir: str, snowball_endpoint: str, bucket: str, prefix: str):
        self.source_dir = Path(source_dir)
        self.snowball_endpoint = snowball_endpoint
        self.bucket = bucket
        self.prefix = prefix

    def analyze_source(self) -> dict:
        """Analyze source directory to determine optimal copy strategy."""
        total_files = 0
        total_size = 0
        small_files = 0
        small_files_size = 0
        large_files = 0
        large_files_size = 0
        max_path_length = 0
        long_path_files = []

        for dirpath, dirnames, filenames in os.walk(self.source_dir):
            for filename in filenames:
                filepath = os.path.join(dirpath, filename)
                try:
                    size = os.path.getsize(filepath)
                except OSError:
                    continue

                total_files += 1
                total_size += size

                if len(filepath) > max_path_length:
                    max_path_length = len(filepath)

                if len(filepath) > 1024:
                    long_path_files.append(filepath)

                if size < 1 * 1024 * 1024:  # < 1MB
                    small_files += 1
                    small_files_size += size
                else:
                    large_files += 1
                    large_files_size += size

        return {
            'total_files': total_files,
            'total_size_gb': total_size / (1024**3),
            'small_files': small_files,
            'small_files_pct': (small_files / total_files * 100) if total_files > 0 else 0,
            'small_files_size_gb': small_files_size / (1024**3),
            'large_files': large_files,
            'large_files_size_gb': large_files_size / (1024**3),
            'max_path_length': max_path_length,
            'long_path_files': len(long_path_files),
            'recommendation': self._recommend_strategy(small_files, total_files),
        }

    def _recommend_strategy(self, small_files: int, total_files: int) -> str:
        """Recommend copy strategy based on file size distribution."""
        small_pct = (small_files / total_files * 100) if total_files > 0 else 0

        if small_pct > 50:
            return (
                "SMALL FILE DOMINANT. "
                "Tar small files into large archives before copying. "
                "Expected speedup: 10-50x."
            )
        elif small_pct > 20:
            return (
                "MODERATE SMALL FILES. "
                "Consider tarring files < 1MB. "
                "Run 4+ parallel copy sessions."
            )
        else:
            return (
                "LARGE FILE DOMINANT. "
                "Direct copy with parallel sessions should achieve near-max throughput."
            )

    def create_tar_archives(self, strategy: CopyStrategy) -> list:
        """Create tar archives of small files for efficient transfer."""
        archives = []
        current_batch = []
        current_batch_size = 0
        batch_num = 0

        for dirpath, dirnames, filenames in os.walk(self.source_dir):
            for filename in filenames:
                filepath = os.path.join(dirpath, filename)
                try:
                    size = os.path.getsize(filepath)
                except OSError:
                    continue

                if size < strategy.small_file_threshold_mb * 1024 * 1024:
                    current_batch.append(filepath)
                    current_batch_size += size

                    if current_batch_size >= strategy.tar_batch_size_mb * 1024 * 1024:
                        archive_path = self._write_tar(current_batch, batch_num)
                        archives.append(archive_path)
                        current_batch = []
                        current_batch_size = 0
                        batch_num += 1

        if current_batch:
            archive_path = self._write_tar(current_batch, batch_num)
            archives.append(archive_path)

        return archives

    def _write_tar(self, files: list, batch_num: int) -> str:
        """Write a batch of files to a tar archive."""
        archive_name = f"small_files_batch_{batch_num:04d}.tar.gz"
        archive_path = os.path.join(tempfile.gettempdir(), archive_name)

        with tarfile.open(archive_path, 'w:gz') as tar:
            for filepath in files:
                arcname = os.path.relpath(filepath, self.source_dir)
                tar.add(filepath, arcname=arcname)

        return archive_path

    def parallel_copy(self, source_dirs: list, strategy: CopyStrategy) -> list:
        """Copy multiple directories in parallel to Snowball device."""
        results = []

        with ThreadPoolExecutor(max_workers=strategy.parallel_sessions) as executor:
            futures = {}
            for i, source in enumerate(source_dirs):
                s3_key = f"{self.prefix}/batch_{i:04d}"
                future = executor.submit(
                    self._copy_to_snowball, source, s3_key
                )
                futures[future] = source

            for future in as_completed(futures):
                source = futures[future]
                try:
                    result = future.result()
                    results.append({'source': source, 'status': 'success', 'result': result})
                except Exception as e:
                    results.append({'source': source, 'status': 'error', 'error': str(e)})

        return results

    def _copy_to_snowball(self, source: str, s3_key: str) -> dict:
        cmd = [
            'aws', 's3', 'cp',
            source,
            f"s3://{self.bucket}/{s3_key}/",
            '--recursive',
            '--endpoint-url', f"https://{self.snowball_endpoint}:8443",
            '--no-verify-ssl',
        ]

        result = subprocess.run(cmd, capture_output=True, text=True)

        if result.returncode != 0:
            raise RuntimeError(f"Copy failed: {result.stderr}")

        return {'command': ' '.join(cmd), 'stdout': result.stdout}
Small Files Are the Enemy of Snowball Copy Speed
  • Per-file overhead: each file requires manifest entry, metadata update, S3 adapter handshake.
  • 10 million 1KB files = 10GB total. Copy time: ~2-3 days at 5MB/s.
  • 10 thousand 1MB files = 10GB total. Copy time: ~2 minutes at 100MB/s.
  • Tar small files into 1GB archives: 10 archives instead of 10 million files. Copy time ~2 minutes.
  • Run source analysis. If small files >20%, tar first. The time spent tarring is recovered 10-50x in copy speed.
Production Insight
A genomics lab attempted to copy 50TB of sequencing data (50 million small files averaging 1KB each) to Snowball Edge. Copy ran at 3MB/s — projected completion: 193 days. Cancel after 2 weeks, 3.6TB transferred.
Cause: 50 million small files overwhelmed S3 adapter's per-file overhead.
Effect: 3MB/s throughput instead of expected 100MB/s.
Impact: 2 weeks of wasted copy time, 50TB still to transfer.
Action: tarred small files into 10GB archives (5,000 archives). Re-ran copy: 120MB/s throughput, completed in 5 days.
Key Takeaway
Small files are the enemy. If >20% of files are <1MB, tar into large archives before copying. Run source analysis. Use parallel copy sessions (4-8). Verify 10GbE data port, not 1GbE management port.

Encryption, KMS Key Management, and Cross-Account Migrations

Every Snowball device encrypts data at rest using AES-256. KMS key management is critical — a KMS mismatch discovered at AWS loading means 2-3 week restart.

Encryption flow: 1. Specify KMS key ARN when creating Snowball job. 2. S3 adapter encrypts each object using data key derived from KMS key. 3. Device stores encrypted data. Without KMS key, data is unreadable. 4. AWS receives device, uses KMS key to decrypt and load into S3.

Cross-account migration
  • Snowball job in Account A, KMS key in Account B.
  • Account A's Snowball job IAM role must have kms:Decrypt, kms:GenerateDataKey on Account B key.
  • Account B's KMS key policy must explicitly allow Account A's IAM role.
  • Permissions required in TWO places. Missing either causes 'Access Denied' at AWS loading.

Common failure: default KMS key (aws/s3) used, but S3 bucket policy requires specific customer-managed key. AWS loads data with default key, S3 rejects writes because encryption key doesn't match bucket policy.

Best practice: always specify KMS key ARN explicitly. Never rely on default key behavior. Validate with iam:SimulatePrincipalPolicy before creating job.

io/thecodeforge/aws/snowball_kms_validator.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package io.thecodeforge.aws;

import boto3
import json
from dataclasses import dataclass


@dataclass
class KMSValidationResult:
    key_arn: str
    key_exists: bool
    key_enabled: bool
    key_account: str
    job_account: str
    account_match: bool
    permissions_valid: bool
    bucket_policy_compatible: bool
    errors: list


class SnowballKMSValidator:
    """Validates KMS key configuration for Snowball jobs."""

    def __init__(self, job_account_region: str = 'us-east-1'):
        self.kms_client = boto3.client('kms', region_name=job_account_region)
        self.iam_client = boto3.client('iam', region_name=job_account_region)
        self.s3_client = boto3.client('s3', region_name=job_account_region)
        self.sts_client = boto3.client('sts', region_name=job_account_region)

    def validate_kms_for_snowball(
        self,
        kms_key_arn: str,
        iam_role_arn: str,
        s3_bucket: str,
    ) -> KMSValidationResult:
        """Comprehensive KMS validation for a Snowball job."""
        errors = []

        # Get current account
        job_account = self.sts_client.get_caller_identity()['Account']

        # 1. Verify KMS key exists and is enabled
        key_exists = False
        key_enabled = False
        key_account = ''
        try:
            key_response = self.kms_client.describe_key(KeyId=kms_key_arn)
            key_metadata = key_response['KeyMetadata']
            key_exists = True
            key_enabled = key_metadata['KeyState'] == 'Enabled'
            key_account = key_metadata['Arn'].split(':')[4]

            if not key_enabled:
                errors.append(f"KMS key is not enabled. State: {key_metadata['KeyState']}")

            if key_metadata.get('KeyManager') == 'AWS':
                errors.append(
                    "AWS-managed keys (aws/s3) are not recommended for Snowball. "
                    "Use a customer-managed key for cross-account compatibility."
                )
        except Exception as e:
            errors.append(f"KMS key not found or not accessible: {e}")

        # 2. Check cross-account permissions
        account_match = key_account == job_account
        permissions_valid = False

        if not account_match:
            errors.append(
                f"KMS key is in account {key_account} but Snowball job is in {job_account}. "
                f"Cross-account KMS permissions required."
            )

            for action in ['kms:Decrypt', 'kms:GenerateDataKey', 'kms:DescribeKey']:
                try:
                    response = self.iam_client.simulate_principal_policy(
                        PolicySourceArn=iam_role_arn,
                        ActionNames=[action],
                        ResourceArns=[kms_key_arn],
                    )
                    decision = response['EvaluationResults'][0]['EvalDecision']
                    if decision != 'allowed':
                        errors.append(
                            f"IAM role missing {action} on cross-account KMS key. "
                            f"Add permission to IAM policy AND KMS key policy."
                        )
                    else:
                        permissions_valid = True
                except Exception as e:
                    errors.append(f"Permission simulation failed for {action}: {e}")
        else:
            permissions_valid = True

        # 3. Check S3 bucket policy compatibility
        bucket_policy_compatible = True
        try:
            bucket_policy = self.s3_client.get_bucket_policy(Bucket=s3_bucket)
            policy = json.loads(bucket_policy['Policy'])

            for statement in policy.get('Statement', []):
                if statement.get('Effect') == 'Deny':
                    condition = statement.get('Condition', {})
                    kms_condition = condition.get('StringNotEquals', {}).get(
                        's3:x-amz-server-side-encryption-aws-kms-key-id', ''
                    )
                    if kms_condition and kms_condition != kms_key_arn:
                        errors.append(
                            f"Bucket policy denies writes not encrypted with {kms_condition}. "
                            f"Snowball job uses {kms_key_arn}. Mismatch."
                        )
                        bucket_policy_compatible = False
        except self.s3_client.exceptions.NoSuchBucketPolicy:
            pass
        except Exception as e:
            errors.append(f"Bucket policy check failed: {e}")

        return KMSValidationResult(
            key_arn=kms_key_arn,
            key_exists=key_exists,
            key_enabled=key_enabled,
            key_account=key_account,
            job_account=job_account,
            account_match=account_match,
            permissions_valid=permissions_valid,
            bucket_policy_compatible=bucket_policy_compatible,
            errors=errors,
        )

    def generate_cross_account_kms_policy(
        self,
        job_account_id: str,
        job_role_arn: str,
    ) -> str:
        """Generate a KMS key policy for cross-account Snowball access."""
        policy = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "AllowSnowballJobAccount",
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": job_role_arn
                    },
                    "Action": [
                        "kms:Decrypt",
                        "kms:GenerateDataKey",
                        "kms:DescribeKey",
                    ],
                    "Resource": "*",
                },
                {
                    "Sid": "AllowRootAccountFullAccess",
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": f"arn:aws:iam::{job_account_id}:root"
                    },
                    "Action": "kms:*",
                    "Resource": "*",
                },
            ],
        }
        return json.dumps(policy, indent=2)
Cross-Account KMS Requires Double Permission
  • Same account: IAM role needs kms:Decrypt, kms:GenerateDataKey on the key.
  • Cross account: IAM role needs same permissions. KMS key policy must allow the role ARN.
  • Always specify KMS key ARN explicitly in Snowball job. Never rely on default key.
  • AWS-managed keys (aws/s3) cannot be shared cross-account. Use customer-managed keys.
  • Validate with iam:SimulatePrincipalPolicy before creating job. Do not discover error at AWS loading.
Production Insight
A company migrated data from subsidiary's account (Account B) to parent's account (Account A) using Snowball. Job created in Account A with default KMS key (aws/s3). S3 bucket in Account A required customer-managed key. AWS loaded data with default key, S3 rejected all writes. 180TB not loaded. Cause: default key didn't match bucket policy. Effect: 180TB not loaded. Impact: 3-week delay — new device, re-copied with correct key, re-shipped. Action: always specify KMS key ARN explicitly. Validate bucket policy compatibility before creating job.
Key Takeaway
Always specify KMS key ARN explicitly. Never rely on default key. For cross-account, permissions required in IAM role AND KMS key policy. Validate with iam:SimulatePrincipalPolicy before creating job. KMS mismatch at AWS loading means 2-3 week restart.

The Snow Family: Pick the Right Plastic Brick or Eat the Cost

AWS sells three tiers of data-shipping hardware, not one. Snowcone, Snowball Edge, and Snowmobile. They're not interchangeable. Pick wrong and you're either waiting weeks for multiple shipments or paying forklift rates for a device that idles at 10% utilization.

Snowcone is the little guy. 8 TB HDD or 14 TB SSD, 4.5 lbs. It runs on a 12V cigarette lighter. Use it when your data source is a drone, a delivery truck, or a sensor array in a shack with solar power. No, you cannot shove 80 TB through it. Don't try.

Snowball Edge is the fat middle. Two variants: Storage Optimized (80 TB HDD, 40 vCPUs) for bulk data migration, and Compute Optimized (42 TB NVMe, 104 vCPUs, optional V100 GPU) for edge processing like real-time video analysis or ML inference in disconnected environments. If you need compute at the edge, do not order the Storage Optimized version and then complain it can't run your model.

Snowmobile is a shipping container on a semi-trailer. 100 PB per trip. You order this when your dataset is measured in exabytes. Yes, you literally drive a truck to your data center. If you're asking "do I need Snowmobile?" you probably don't.

SnowballDeviceSelection.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// io.thecodeforge — devops tutorial
//
// Device sizing for a 50 TB migration with 20 concurrent edge compute jobs
// Adjust for your actual data shape — flat files vs 4k video matter

Environment:
  DatasetSizeTB: 50
  EstimatedTransferDays: 14
  EdgeComputeJobs: 20

SelectionLogic:
  - if DatasetSizeTB <= 14:
      Device: "Snowcone SSD"
      Quantity: ceil(DatasetSizeTB / 14)
  - elif DatasetSizeTB <= 80:
      Device: "Snowball Edge Storage Optimized"
      Quantity: ceil(DatasetSizeTB / 80)
  - elif DatasetSizeTB <= 100000:
      Device: "Snowball Edge Storage Optimized"
      Quantity: ceil(DatasetSizeTB / 80)
      Note: "Multiple shipments required. Plan staging."
  - else:
      Device: "Snowmobile"
      Quantity: 1
      Note: "Call your AWS account manager. This is not self-service."
Output
Device: Snowball Edge Storage Optimized
Quantity: 1
Note: Multiple shipments required. Plan staging.
Production Trap:
If you need edge compute (GPUs, real-time inference) and order Storage Optimized, you will hit a brick wall. No GPU. No NVMe. The Compute Optimized variant costs more per unit but saves you from shipping data back to AWS just to process it.
Key Takeaway
Match device variant to your bottleneck: storage capacity for bulk migration, compute capacity (GPU/CPU/RAM) for edge processing.

Key Features Across the Family: What Every Brick Does Out of the Box

Every Snow Family device shares a common feature set. Memorize these. They're the difference between a job that completes in 2 days and one that drags into a month-long support ticket.

High-speed offline data transfer is the whole point. You fill the device locally, ship it, and AWS ingests it into an S3 bucket you specify. The transfer speed is limited only by your local network and I/O — typically 10-30 Gbps per device. Do not assume you'll saturate that. If your source is a RAID array of spinning disks, you won't.

Secure and encrypted storage is mandatory. All data is encrypted at rest using AES-256. The key lives in AWS KMS, not on the device. If someone steals the Snowball in transit, they get scrambled bits. The device also has tamper-resistant seals and a chain-of-custody report. If the seal is broken, trust nothing on it.

Built-in compute for edge processing means you can run Lambda functions or EC2 instances directly on the device. This is huge for remote sites with no internet. Process data locally, ship only the results. You pay for compute capacity by the day, not by the hour, so batch your jobs efficiently.

Easy integration with NFS support lets you mount the Snowball as a network drive. No special clients. Just mount it, copy files, unmount. This works for Linux and Windows. Do not try to SCP over a packet-switched network — that misses the entire point of physical shipping.

S3 compatibility means the object storage on the device uses the S3 API. Your existing scripts and tools (aws s3 cp, boto3, s3fs) work without modification. The device presents as an S3 endpoint. Configure your bucket name and prefix during job creation, and your data lands in the right place automatically.

SnowballMountAndCopy.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// io.thecodeforge — devops tutorial
//
// Mount Snowball Edge via NFS and copy data
// Assumes device is powered on and network reachable from this host

Steps:
  1. GetEndpoint:
      - Action: "Query AWS CLI or Snowball client for NFS endpoint"
      - Command: "aws snowball describe-job --job-id JOB123456"
      - Returns: "nfs://192.168.1.100/snowball-bucket"

  2. Mount:
      - Command: "sudo mount -t nfs -o vers=4.0,hard,timeo=600,retrans=5 192.168.1.100:/snowball-bucket /mnt/snowball"
      - Note: "Use NFS v4.0 for RHEL/CentOS. v4.1 for Ubuntu."

  3. Rsync:
      - Command: "rsync -ah --progress --stats /data/production-capture/ /mnt/snowball/"
      - Expected: "~80 MB/s per stream on 10GbE"

  4. Unmount:
      - Command: "sudo umount /mnt/snowball"
      - Note: "Run sync before unmount to flush buffers."
Output
sending incremental file list
production-capture/
production-capture/file_001.tar
1,024,000,000 100% 79.86MB/s 0:00:12 (xfr#1, to-chk=999/1001)
...
Number of files: 1000
Number of files transferred: 1000
Total file size: 1,023,999,999,000 bytes
Total transferred file size: 1,023,999,999,000 bytes
Literal data: 1,023,999,999,000 bytes
Matched data: 0 bytes
File list size: 12,345
File list generation time: 0.001 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 1,024,000,011,345
Total bytes received: 12,345
sent 1,024,000,011,345 bytes received 12,345 bytes 79,999,999.56 MB/sec
total size is 1,023,999,999,000 speedup is 1.00
Senior Shortcut:
Mount the Snowball via NFS and use rsync with -ah --progress — not cp. Rsync handles partial transfers and resume. If the power flickers or the network drops, you don't restart from zero.
Key Takeaway
Mount via NFS, copy with rsync, trust the S3 API compatibility — everything else is standard Linux file management.

Snowball Edge Compute — Why You Run Containers in a Shipping Container

Stop treating Snowball Edge like a dumb USB drive. The Compute instances let you run EC2, ECS, or Lambda directly on the device. This matters when you're in a data center with no uplink, on an oil rig, or migrating petabyte-scale datasets that can't hit the public internet.

Why run compute locally? You process data before it leaves your network. Deduplicate. Compress. Encrypt. Validate checksums. You send clean bits to S3 instead of raw garbage. Snowball Edge supports up to 52 vCPUs and 208 GB of RAM — that's a legitimate server in a suitcase.

The workflow: Ship the device, deploy your container image or Lambda function via the Snowball client, execute against local data, then ship back. No network dependency. No bandwidth bill. You decouple compute from the cloud.

For edge IoT, battlefield medical imaging, or seismic data processing, this is your only play. If you're not using compute on Snowball, you're paying twice — once for shipping garbage, once for processing it in AWS.

snowball-compute-job.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// io.thecodeforge — devops tutorial

name: "seismic-data-processing"
jobType: LOCAL_COMPUTE

compute:
  instances:
    - type: "sbe-c.4xlarge"
      count: 1
      ami: "ami-0c55b159cbfafe1f0"
      userData: |
        #!/bin/bash
        aws s3 cp s3://my-bucket/processor.zip /tmp/
        unzip /tmp/processor.zip -d /opt/processor/
        /opt/processor/run.sh

lambda:
  functions:
    - name: "validate-files"
      handler: "validate.handler"
      runtime: "python3.9"
      timeout: 300

storage:
  - source: "/mnt/seismic/raw"
    destination: "s3://seismic-processed/east-texas/"
    encryption: "AES256"
Output
Job created: seismic-data-processing
Compute: 1x sbe-c.4xlarge (16 vCPU, 64 GB RAM)
Lambda: validate-files (python3.9)
Storage: /mnt/seismic/raw → s3://seismic-processed/east-texas/
Production Trap:
Local compute adds 12-24 hours to job preparation. You must test the AMI or container exactly on the Snowball Edge hardware — the ARM architecture bites you if you build on x86. Always run 'snowball validate-compute' before shipping.
Key Takeaway
Process data on the device to reduce egress costs and avoid re-shipping garbage. Always test compute images on actual Snowball hardware before deployment.

Snowball Edge Storage — More Than Just S3 in a Box

Everyone thinks Snowball is just S3 on local disk. Wrong. The storage backend supports S3-compatible APIs — yes — but also NFS and file-level access via the Snowball Edge client. You hit S3 keys, but the underlying device is running a custom filesystem optimized for sequential writes.

Why this matters: You can mount the device as an NFS share. Your legacy backup scripts, rsync jobs, or file-based migration tools work without modification. No SDK changes. No API rewrite. You map a drive letter, copy files, and the device handles chunking and encryption transparently.

Performance ceiling is 1 Gbps per interface, but you can bond multiple 10GbE ports. Real-world throughput hits 300-500 MB/s for sequential reads. Random I/O? Forget it — this is bulk transfer hardware, not a SAN.

The killer feature: You can add storage tiers by ordering multiple devices and chaining them via the Snowball client. 80 TB per device, 10 devices, 800 TB per job. AWS handles the logistics. You handle the data.

Stop optimizing for filesystem features you don't need. Snowball is a firehose, not a database. Treat it like one.

snowball-storage-mount.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// io.thecodeforge — devops tutorial

name: "legacy-file-migration"
storageConfig:
  mountPoint: "/mnt/snowball-nfs"
  nfsExport: "/export/data"
  permissions: "0775"
  bonding:
    interfaces: ["eth0", "eth1"]
    mode: "balance-rr"

transfer:
  sourcePath: "/data/archives/2024/"
  targetPath: "/mnt/snowball-nfs/backups/2024/"
  options:
    parallel: 4
    chunkSize: "64MB"
    verify: true
    deleteAfterCopy: false
Output
NFS mount: /mnt/snowball-nfs -> 192.168.1.100:/export/data
Interface bonding: eth0+eth1 (balance-rr)
Transfer started: /data/archives/2024/ -> /mnt/snowball-nfs/backups/2024/
Parallel streams: 4, Chunk size: 64MB
Estimated time: 14 hours 23 minutes
Senior Shortcut:
Bond two 10GbE ports for 15-20% better throughput. Don't use S3 API for file migrations over 1 TB — NFS avoids SDK overhead and retry logic. For files under 1 MB, tar them into 64 MB chunks before copying.
Key Takeaway
Snowball Edge supports NFS mounts for legacy workflows. Bond network interfaces and chunk small files. Treat it as a sequential write device, not random-access storage.

Snowball Export Workflow — Reverse the Pipeline Without Losing Your Sanity

Importing data into AWS is the easy path. Exporting from S3 back to your on-premises data center? That's where engineers lose weeks. Snowball supports export jobs — AWS reads from S3, writes to the device, ships it to you. You plug it in, copy the data off, and ship back.

Why the reverse matters: Compliance, DR, or vendor lock-in escape hatches. If you need a physical copy of your S3 bucket for legal hold, or you're migrating off AWS, Snowball export is your only option under 100 PB. No bandwidth bottleneck, no egress fees per GB — just a flat device rental.

The catch: Export jobs require an inventory manifest. AWS lists every object key and ETag. If you have buckets with millions of small objects, the manifest generation takes hours. You pay for that CPU time in the background. Also, export speeds are slower than import — the device writes sequentially from S3, and S3's eventual consistency can cause retries.

Optimization: Use S3 Inventory reports to pre-generate the object list. Filter by prefix or last-modified date. Don't export Glacier objects without restoring them first — that job fails silently. Test with 10-50 GB before committing to a full bucket.

Export is a safety valve, not a daily driver. Use it sparingly, audit the manifest twice, and budget 30% overhead for retries.

snowball-export-job.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// io.thecodeforge — devops tutorial

name: "legal-hold-export-q4-2024"
jobType: EXPORT

source:
  bucket: "compliance-archive-2024"
  prefix: "financials/"
  filter:
    type: "STANDARD"
    lastModified: {"start": "2024-01-01", "end": "2024-12-31"}

manifest:
  format: "CSV"
  includeEtag: true
  preGenerate: true

destination:
  mountPoint: "/mnt/snowball-export"
  verifyChecksums: true
  deleteAfterCopy: true

retry:
  maxRetries: 3
  backoff: "exponential"
Output
Export job created: legal-hold-export-q4-2024
Source: s3://compliance-archive-2024/financials/
Objects: 1,234,567 (estimated 2.3 TB)
Manifest generated: 45 minutes
Estimated transfer time: 2.1 days
Retry policy: 3 attempts, exponential backoff
Production Trap:
Export jobs fail silently on Glacier objects. Always run S3 Inventory with restoration status before kicking off the job. Also, the device must be wiped after export — AWS does not offer a 'read-only' mode for export devices.
Key Takeaway
Export jobs are for compliance and DR only. Pre-generate inventory manifests, exclude Glacier objects, and budget 30% overhead for retries. Treat export as a safety valve, not a regular workflow.

Snowball as a Compute Node: Running EC2 on the Edge

AWS Snowball Edge Compute is not a data shuttle—it's a portable data center. When you need to process data where bandwidth is zero or latency kills, you launch EC2 instances directly on the device. Why? Because moving terabytes to the cloud for processing wastes time and money. Snowball Edge runs a hypervisor that supports standard AMIs, so your existing EC2 workflows deploy unchanged. You define instance types, attach EBS-equivalent storage, and even run containers on top. The device syncs results back via S3 export or network once the job completes. This eliminates round-trips: process on arrival, ship results. Production trap: Snowball EC2 instances do not support all instance families—only compute and storage optimized types. Validate your AMI against the Snowball-specific instance catalog before creating the job. Key takeaway: Treat Snowball Edge as a physical EC2 rack, not a glorified USB drive.

SnowballEdgeEC2.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// io.thecodeforge — devops tutorial

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SnowballJob:
    Type: AWS::Snowball::Job
    Properties:
      JobType: LOCAL_USE
      SnowballCapacityPreference: T100
      EC2Instance:
        AMI: ami-0abcdef1234567890
        InstanceType: sbe-c.large
        BlockDeviceMappings:
          - DeviceName: /dev/xvda
            Ebs:
              VolumeSize: 80
              VolumeType: gp2
      LambdaPrecedence: PROCESS_ON_ARRIVAL
Output
Creates a Snowball Edge job that runs EC2 on arrival, not after export.
Production Trap:
Snowball Edge instances reboot when the device loses power—there is no persistent EBS beyond the local NVMe. Use S3 sync for state.
Key Takeaway
Snowball Edge runs real EC2 instances—ship code, not data.

Lambda on Snowball: Trigger Compute Before Data Leaves the Device

Snowball Edge supports AWS Lambda functions that execute locally on the device—no internet required. Why? Because you want to filter, transform, or validate data before it ships to AWS. Stop uploading garbage. Lambda on Snowball triggers on S3 PUT events, job state changes, or schedule. Write Python or Node.js functions, package them as .zip, and associate them during job creation. During the 'Processing in Transit' phase, Lambda runs inside the device's compute slot, consuming local storage and returning results to the same Snowball S3 bucket. This pattern crushes egress costs: only valuable data exports. Production trap: Lambda on Snowball has a 5-minute timeout and 512 MB memory cap—no GPU, no large dependencies. Test locally using snowball-edge-lambda-simulator. Key takeaway: Move computation to the data, not data to computation.

SnowballLambdaJob.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// io.thecodeforge — devops tutorial

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SnowballProcessingJob:
    Type: AWS::Snowball::Job
    Properties:
      JobType: IMPORT
      LambdaResources:
        - arn:aws:lambda:us-east-1:123456789012:function:filter-sensitive-data
      Notification:
        SnsTopicArn: arn:aws:sns:us-east-1:123456789012:SnowballStatus
      Resources:
        S3Resources:
          - BucketArn: arn:aws:s3:::my-data-bucket
      RoleArn: arn:aws:iam::123456789012:role/snowball-lambda-role
Output
Triggers Lambda on device upon data arrival, before export.
Production Trap:
Lambda logs stay on the device until export—enable CloudWatch logging only if the device has network connectivity during the job.
Key Takeaway
Run Lambda on Snowball to validate and filter data before it ever reaches AWS.

Cross-Region Snowball Exports: Routing Data to Multiple AWS Regions

Standard Snowball exports dump to a single S3 bucket in one region. When you need data in multiple AWS regions—say, for disaster recovery or global distribution—you must chain jobs. Why? Snowball cannot natively multicast to multiple S3 destinations. The pattern: create one export job per target region, each with its own Snowball device. Alternatively, use a single import to a central bucket, then trigger cross-region replication (CRR) after data lands. But CRR costs per-object replication fees and adds latency. The smarter way: use Snowball Edge Compute to run a script that copies objects to multiple local directories, then create separate export jobs per region. This reduces egress time by parallelizing the copy. Production trap: Cross-region transfers via Snowball require separate KMS keys per region—one key cannot encrypt for two regions. Pre-generate keys and attach them per job. Key takeaway: Snowball is single-region per job—architect for fan-out if you need geographic distribution.

CrossRegionSnowball.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// io.thecodeforge — devops tutorial

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  SnowballExportEU:
    Type: AWS::Snowball::Job
    Properties:
      JobType: EXPORT
      SnowballCapacityPreference: T80
      Resources:
        S3Resources:
          - BucketArn: arn:aws:s3:::eu-west-1-data
            KeyRange:
              BeginMarker: prefix/2024/
      KmsKeyArn: arn:aws:kms:eu-west-1:123456789012:key/eu-key
  SnowballExportUS:
    Type: AWS::Snowball::Job
    Properties:
      JobType: EXPORT
      SnowballCapacityPreference: T80
      Resources:
        S3Resources:
          - BucketArn: arn:aws:s3:::us-east-1-data
      KmsKeyArn: arn:aws:kms:us-east-1:123456789012:key/us-key
Output
Two parallel Snowball export jobs targeting different regions with distinct KMS keys.
Production Trap:
Snowball does not support multi-region exports in a single job—you must create separate jobs, each with its own device and KMS key.
Key Takeaway
One Snowball job equals one region—plan parallel jobs for multi-region data distribution.
● Production incidentPOST-MORTEMseverity: high

The 47-Day Migration Delay: Overwritten Manifest and Wrong KMS Key

Symptom
Two of three Snowball devices rejected by AWS on arrival. Device 1: 'Manifest mismatch — device contents do not match manifest.' Device 2: 'Access denied — KMS key not found in target account.' Only one device of three successfully loaded into S3.
Assumption
The team assumed Snowball process was straightforward: copy, ship, done. They didn't validate manifests before shipping, didn't verify KMS key permissions across accounts, and didn't know re-running the copy script corrupts the manifest.
Root cause
Device 1: A cleanup script ran 'cp -r /source/* /snowball/' twice. The second run overwrote the manifest.json file that Snowball's S3 adapter generates during the first copy. Manifest recorded only the second run's files, but device contained files from both runs (some overwritten, some not). AWS validation detected mismatch, rejected device. Device 2: Snowball job created in Account A, but KMS key used for encryption was in Account B. Device encrypted data with Account B's key. On arrival, AWS tried to decrypt using Account A's key — access denied. Snowball job's IAM role did not have kms:Decrypt permission on Account B's key. Cross-account KMS requires permissions in BOTH places.
Fix
1. Device 1: Ordered new Snowball device (5-day wait). Re-ran copy with single, idempotent script. Added manifest validation: diff manifest.json against file list generated from source directory. 2. Device 2: Created new Snowball job with KMS key ARN from Account B explicitly specified. Added Account A's Snowball job role to Account B's KMS key policy with kms:Decrypt, kms:GenerateDataKey, and kms:DescribeKey permissions. 3. Added pre-ship checklist mandatory gate: (a) manifest matches source file count and total size, (b) KMS key ARN matches target account, (c) IAM role has kms:Decrypt on specified key, (d) device status is 'Ready to ship' in console. 4. Added CI pipeline step that generates and validates manifest before marking job as ready.
Key lesson
  • Never re-run copy scripts on a Snowball device. If you must re-copy, cancel job, order new device.
  • Manifest is contract between your data and AWS. If file counts don't match, device is rejected.
  • Cross-account KMS requires permissions in IAM role AND KMS key policy. Validate both before creating job.
  • Add pre-ship checklist as mandatory gate. A rejected device costs 2-3 weeks, not just device cost.
  • For cross-account migrations, specify KMS key ARN explicitly. Never rely on default key behavior.
Production debug guideSymptom-to-action guide for Snowball job failures, transfer speed issues, and device rejections5 entries
Symptom · 01
Copy speed to Snowball device < 50MB/s on 10GbE connection
Fix
Check network interface — Snowball Edge has 1GbE management port AND 10GbE data port. Are you on the right port? Also check file size distribution: millions of small files (<1MB) are 10-50x slower. Tar small files into archives before copying.
Symptom · 02
Manifest file count does not match source directory
Fix
Snowball S3 adapter may have skipped files due to path length >1024 bytes, unsupported characters, or file size limits. Run: diff <(find /source -type f | wc -l) <(jq '.entries | length' manifest.json). If counts differ, grep for files with long paths or special characters.
Symptom · 03
AWS rejects returned device — 'Job validation failed'
Fix
Check console for rejection reason. Common: manifest mismatch (files on device don't match manifest), KMS key mismatch (device encrypted with wrong key), device tampered (security seal broken). If manifest mismatch, you must order new device and re-copy. No fix.
Symptom · 04
KMS 'Access Denied' during copy or after AWS loading
Fix
Verify KMS key permissions for Snowball job IAM role. For cross-account, need permissions in IAM role AND KMS key policy. Use iam:SimulatePrincipalPolicy and check KMS key policy. Re-create job with explicit key ARN.
Symptom · 05
Snowball Edge compute instance fails to launch — 'Insufficient capacity'
Fix
Compute instances share physical storage with data transfer volume. If device is full of transferred data, no capacity for compute. Check storage: snowballEdge describe-device. Reduce data loaded or use larger device.
★ Snowball Triage Cheat SheetFirst 5 minutes of Snowball failure diagnosis. Commands that save weeks.
Manifest file count mismatch
Immediate action
Compare source file count with manifest entry count.
Commands
find /source -type f | wc -l
jq '.entries | length' manifest.json
Fix now
If counts differ, check for files with paths >1024 bytes or unsupported characters. Relocate and re-copy on new device.
Copy speed < 50MB/s on 10GbE+
Immediate action
Verify you are using 10GbE data port, not 1GbE management port.
Commands
ethtool eth1 | grep Speed
find /source -size -1M | wc -l
Fix now
If small files >20% of total, tar them into 1GB archives. Run 4-8 parallel cp sessions.
KMS 'Access Denied' at AWS loading+
Immediate action
Verify KMS key permissions for Snowball role across both accounts.
Commands
aws iam simulate-principal-policy --policy-source-arn <role-arn> --action-names kms:Decrypt kms:GenerateDataKey --resource-arns <kms-key-arn>
aws kms describe-key --key-id <kms-key-arn> --query 'KeyMetadata.KeyState'
Fix now
Add Snowball role ARN to KMS key policy with kms:Decrypt. Re-create job with explicit key ARN.
Device rejected — manifest mismatch+
Immediate action
Cancel job, order new device. No fix for rejected device.
Commands
aws snowball cancel-job --job-id <job-id>
aws snowball create-job --job-type IMPORT --resources file://job-config.json
Fix now
Order new device. Validate manifest before shipping next time. 2-3 weeks lost.
AWS Snowball Family Comparison
Feature / AspectSnowball StandardSnowball Edge Storage OptimizedSnowball Edge Compute OptimizedSnowmobile
Usable storage80TB210TB100TB100PB
ComputeNone40 vCPUs, 80GB RAM52 vCPUs, 208GB RAM, optional NVIDIA V100 GPUN/A
Network interfaces1x 10GbE1x 10GbE, 1x 25GbE1x 10GbE, 1x 25GbEFiber optic connection on-site
Local S3 endpointYesYesYesN/A
EC2/Lambda supportNoYes (limited)Yes (full, with GPU option)No
Cost per job$300 + shipping$400-600 + shipping$400-600 + shipping, $600+ with GPU~$0.005/GB (~$500K per 100PB)
ShippingStandard carrierStandard carrierStandard carrierSemi-truck with security escort
Typical timeline10-20 business days10-20 business days10-20 business daysWeeks to months (site prep required)
Best forOne-time data migration <80TBBulk transfer + light computeEdge compute + moderate transfer, ML inferenceExabyte-scale data center migration
EncryptionAES-256, KMS-managedAES-256, KMS-managedAES-256, KMS-managedAES-256, KMS-managed
Tamper detectionTPM chip, tamper-evident enclosureTPM chip, tamper-evident enclosureTPM chip, tamper-evident enclosureArmed security escort, GPS tracking
Use case exampleData center decommission, initial cloud migrationContent distribution, disaster recovery, bulk archivalMilitary field ops, oil rig processing, disconnected environmentsEnterprise data center consolidation, media archive migration

Key takeaways

1
Snowball is not always faster than direct transfer. Calculate crossover point before ordering. At 1Gbps, crossover ~200TB. At 10Gbps, crossover ~1.8PB.
2
Small files are the enemy. If >20% of files are <1MB, tar into large archives before copying. 10-50x speedup.
3
Never re-run copy scripts on a Snowball device. Overwriting files corrupts the manifest. AWS will reject the device.
4
Validate manifest before shipping
source file count must match manifest entry count. A rejected device costs 2-3 weeks.
5
Cross-account KMS requires permissions in IAM role AND KMS key policy. Validate with iam:SimulatePrincipalPolicy before creating job.
6
Always specify KMS key ARN explicitly. Never rely on default key behavior. KMS mismatch at AWS loading means 2-3 week restart.
7
Use 10GbE data port, not 1GbE management port. Run 4-8 parallel copy sessions. Copy large files first, skip temp files.
8
Plan for the full 5-phase timeline
create (3d) + receive (5d) + copy (varies) + return (5d) + AWS loading (3d) = 10-20 business days.
9
Add pre-ship checklist as mandatory gate
manifest diff, KMS key validation, IAM role permissions, device status 'Ready to ship'.
10
For datasets <10TB with stable 10Gbps+, direct transfer or DataSync is faster than Snowball. Calculate before ordering.

Common mistakes to avoid

7 patterns
×

Assuming Snowball is always faster than direct network transfer

Symptom
45TB dataset over 10Gbps direct connect took 10 hours direct; Snowball took 22 days (shipping + copy + AWS loading). Team lost 3 weeks waiting for device.
Fix
Always calculate crossover point before ordering. At 10Gbps, direct transfer wins for <1.8PB. Use DataSync for ongoing sync, not Snowball.
×

Not validating the manifest before shipping

Symptom
Device rejected by AWS with 'Manifest mismatch — device contents do not match manifest'. 3-week restart.
Fix
Run pre-ship validation: diff <(find /source -type f | wc -l) <(jq '.entries | length' manifest.json). If counts differ, investigate missing or skipped files. Never ship without a clean diff.
×

Using default KMS key without checking bucket policy compatibility

Symptom
AWS loads data with default key (aws/s3), but S3 bucket policy requires customer-managed key. S3 rejects all writes. 180TB not loaded. 3-week restart.
Fix
Always specify KMS key ARN explicitly in Snowball job. Validate bucket policy compatibility before creating job. Use customer-managed keys for cross-account or compliance scenarios.
×

Re-running copy scripts on the same device, corrupting manifest

Symptom
Device rejected: 'Manifest mismatch'. The second copy run overwrote manifest.json, but device contained files from both runs.
Fix
Never re-run copy scripts on a Snowball device. If you must re-copy, cancel job, order new device, fix source issues, start fresh. The cost of a rejected device is 2-3 weeks.
×

Connecting to the 1GbE management port instead of 10GbE data port

Symptom
Copy speed stuck at 100MB/s instead of 1GB/s. 200TB dataset projected to take 23 days instead of 2 days.
Fix
Snowball Edge has two network ports: 1GbE management port AND 10GbE data port. Connect to data port. Verify with ethtool eth1 | grep Speed.
×

Not tarring small files before copying to Snowball

Symptom
50 million 1KB files (50GB total) copy at 3MB/s → 193 days projected. 2 weeks wasted, only 3.6TB transferred.
Fix
Run source analysis: if >20% of files are <1MB, tar small files into 1GB archives first. Copy time drops from weeks to hours. The time spent tarring is recovered 10-50x.
×

Cross-account KMS without permissions in BOTH places

Symptom
Amazon S3 access denied after AWS loading. KMS key is in Account B, Snowball job in Account A. IAM role permissions OK, but KMS key policy missing Allow for Account A.
Fix
Cross-account KMS requires permissions in IAM role AND KMS key policy. Validate with iam:SimulatePrincipalPolicy before creating job. Ensure KMS key policy includes the role ARN.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
When should you use AWS Snowball instead of direct network transfer or A...
Q02SENIOR
What are the capacity and cost differences between Snowball Standard, Sn...
Q03SENIOR
What is the manifest file and why is it critical to Snowball migrations?
Q04SENIOR
How can you maximize copy speed to a Snowball device? List the top 3 bot...
Q05SENIOR
How do you handle cross-account KMS key migrations with Snowball?
Q06SENIOR
What happens if AWS rejects your Snowball device on arrival? How do you ...
Q07SENIOR
What is the typical timeline for a Snowball job from order to completion...
Q08SENIOR
How do you clean up test jobs and stale devices after a Snowball migrati...
Q01 of 08SENIOR

When should you use AWS Snowball instead of direct network transfer or AWS DataSync?

ANSWER
Snowball is ideal for one-time bulk transfers of datasets >200TB over 1Gbps networks or >1.8PB over 10Gbps networks. Calculate crossover point: if direct transfer time exceeds Snowball round-trip time (14-16 days + copy time), use Snowball. For ongoing sync between on-premises and AWS, use DataSync. For datasets <10TB with stable 10Gbps+, direct transfer is faster. For disconnected or edge environments needing local compute, use Snowball Edge.
FAQ · 10 QUESTIONS

Frequently Asked Questions

01
When should I use Snowball instead of direct network transfer?
02
What is the difference between Snowball, Snowball Edge, and Snowmobile?
03
How long does a Snowball job take from start to finish?
04
How do I maximize copy speed to a Snowball device?
05
What is the manifest file and why is it important?
06
What happens if AWS rejects my Snowball device?
07
How do I handle cross-account KMS key migrations?
08
Can I re-run copy scripts on a Snowball device?
09
What should a pre-ship checklist include?
10
What is the crossover point for Snowball vs direct transfer?
🔥

That's Cloud. Mark it forged?

12 min read · try the examples if you haven't

Previous
AWS Bedrock Explained: Building GenAI Apps Without Managing Models
22 / 23 · Cloud
Next
AWS Fargate: Serverless Containers on ECS and EKS