17. POC and demo scripts
A POC is a decision instrument. Write the success criteria before you write the first command.
POC charter template
| Field | Example |
|---|---|
| Workload | Veeam SOBR capacity tier, 40 TB subset |
| Success criteria | 1) Backup job completes nightly within window. 2) Object Lock prevents deletion by admin key. 3) Restore of 2 TB VM set completes in under X hours. 4) Projected 3-year TCO under $Y. |
| Duration | 14 to 30 days |
| Owners | Customer: backup admin. Backblaze: SE. |
| Data | Non-production or copy |
| Exit | Signed decision meeting on day N |
Backblaze advertises a free POC for 50 TB+ (cloud migration and CDN pages: “Want to run a free proof of concept with more than 50TB?”).
Demo 0: setup (2 minutes)
# b2 CLI (python)
pip install --upgrade b2
b2 account authorize # prompts for keyID and applicationKey
b2 bucket create --help # check flags for your CLI version
CLI syntax changed in b2 CLI v4 (subcommands like
b2 bucket create,b2 file upload). Older docs showb2 create-bucket. Runb2 --helpbefore the demo. [verify flags against installed version]
Create a bucket-scoped key in the web console for the demo. Never demo with the master key.
Demo 1: S3 drop-in with aws CLI (5 minutes)
export AWS_ACCESS_KEY_ID=<b2 keyID>
export AWS_SECRET_ACCESS_KEY=<b2 applicationKey>
export EP=https://s3.us-west-004.backblazeb2.com # copy exact endpoint from bucket page
aws s3 mb s3://nix-demo-bucket --endpoint-url $EP
aws s3 cp ./sample.mp4 s3://nix-demo-bucket/media/sample.mp4 --endpoint-url $EP
aws s3 ls s3://nix-demo-bucket --recursive --endpoint-url $EP
aws s3 presign s3://nix-demo-bucket/media/sample.mp4 --expires-in 3600 --endpoint-url $EP
Say: “Same CLI. I changed two things: the endpoint and the keys.”
Demo 2: boto3 (5 minutes)
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://s3.us-west-004.backblazeb2.com",
aws_access_key_id="<keyID>",
aws_secret_access_key="<applicationKey>",
)
s3.upload_file("sample.mp4", "nix-demo-bucket", "media/sample.mp4")
for o in s3.list_objects_v2(Bucket="nix-demo-bucket", Prefix="media/").get("Contents", []):
print(o["Key"], o["Size"])
url = s3.generate_presigned_url(
"get_object", Params={"Bucket": "nix-demo-bucket", "Key": "media/sample.mp4"}, ExpiresIn=900
)
print(url)
# presigned PUT for direct browser/device upload
put_url = s3.generate_presigned_url(
"put_object", Params={"Bucket": "nix-demo-bucket", "Key": "uploads/user1.jpg"}, ExpiresIn=900
)
Demo 3: Object Lock, the money demo (7 minutes)
import boto3, datetime
s3 = boto3.client("s3", endpoint_url=EP, aws_access_key_id=KID, aws_secret_access_key=KEY)
s3.create_bucket(Bucket="nix-immutable-demo", ObjectLockEnabledForBucket=True)
until = datetime.datetime.utcnow() + datetime.timedelta(days=1)
r = s3.put_object(
Bucket="nix-immutable-demo", Key="backups/restorepoint.vbk", Body=b"pretend backup",
ObjectLockMode="GOVERNANCE", # use COMPLIANCE only if you accept paying until expiry
ObjectLockRetainUntilDate=until,
)
vid = r["VersionId"]
try:
s3.delete_object(Bucket="nix-immutable-demo", Key="backups/restorepoint.vbk", VersionId=vid)
except Exception as e:
print("Delete blocked:", e) # AccessDenied: object is locked
print(s3.get_object_retention(Bucket="nix-immutable-demo", Key="backups/restorepoint.vbk", VersionId=vid))
Say: “That was my key. Imagine it is the attacker’s. The restore point is still there.”
Demo hygiene: use GOVERNANCE and 1 day in demos. Compliance mode on a demo bucket means paying for it until expiry. Object Lock cannot be turned off on the bucket afterwards.
Demo 4: rclone migration (8 minutes)
rclone config create b2s3 s3 provider=Other \
access_key_id=<keyID> secret_access_key=<applicationKey> \
endpoint=s3.us-west-004.backblazeb2.com
# or native backend
rclone config create b2native b2 account=<keyID> key=<applicationKey>
# copy from AWS (configured as remote 'aws') to B2 with parallelism and checksum compare
rclone copy aws:source-bucket b2s3:nix-demo-bucket \
--transfers 64 --checkers 32 --s3-chunk-size 64M --checksum --progress
rclone check aws:source-bucket b2s3:nix-demo-bucket --one-way
Tune --transfers and chunk size to bandwidth. Show rclone check output as integrity proof.
Demo 5: lifecycle and versions (3 minutes)
Web console: Bucket Settings → Lifecycle Settings. Show “Keep only the last version” vs custom rule with prefix. Explain hide vs delete. Or native:
b2 bucket update nix-demo-bucket allPrivate \
--lifecycle-rules '[{"fileNamePrefix":"tmp/","daysFromUploadingToHiding":7,"daysFromHidingToDeleting":1}]'
[verify flag name for your CLI version]
Demo 6: Event Notifications (5 minutes, needs access enabled by Support)
- Spin up a webhook receiver (e.g., a Cloudflare Worker or
python -m http.serverbehind a tunnel). - Console: Bucket → Event Notifications → add rule: event
b2:ObjectCreated:*[verify event type names], prefixmedia/, endpoint URL, signing secret. - Upload a file; show the POST payload; verify the HMAC.
Demo 7: MCP server with a scoped key (5 minutes)
- Create a read-only key on one bucket.
- Configure an MCP client (Claude Desktop, Cursor, etc.) with the B2 MCP Server per the docs repo.
- Ask: “List the 10 largest files in nix-demo-bucket.” Then ask it to delete one. Show the tool is not available.
- Swap to a write key scoped to
outputs/prefix; show the tool list grow.
Demo 8: throughput test (AI POCs)
# parallel download test from a GPU node
rclone copy b2s3:dataset/shards/ /local/nvme/ --transfers 128 --multi-thread-streams 16 --progress
# or s5cmd for high-parallel S3
s5cmd --endpoint-url $EP --numworkers 256 cp 's3://dataset/shards/*' /local/nvme/
Record GB/s, node count, object size, region pair. Compare to target from discovery. If short, discuss Overdrive.
POC scorecard
| Criterion | Target | Result | Pass |
|---|---|---|---|
| Ingest rate | X TB/day | ||
| Restore / read rate | Y GB/s | ||
| Immutability enforced | Delete fails | ||
| Integration works unmodified | Yes | ||
| Projected 3-yr TCO | ≤ $Z | ||
| Security review | Passed |
Failure modes to pre-empt
- Wrong endpoint region → ‘InvalidAccessKeyId’ or auth errors.
- Using master key with S3 API → works but bad practice; also some restrictions.
- SigV2 client → fails.
- Object Lock not enabled before Veeam connects → immutability option unavailable.
- Tiny objects with huge parallelism → measure ops, not GB/s.