skills/tigrisdata/skills/tigris-egress-optimizer

tigris-egress-optimizer

SKILL.md

Tigris Egress Optimizer

Diagnose and fix excessive storage egress (network data transfer) costs. Follow this 4-step framework to identify anti-patterns, apply fixes, and verify savings.

Most high egress bills come from the application fetching more data than it uses — not from infrastructure issues.

Prerequisites

Before doing anything else, install the Tigris CLI if it's not already available:

tigris help || npm install -g @tigrisdata/cli

If you need to install it, tell the user: "I'm installing the Tigris CLI (@tigrisdata/cli) so we can work with Tigris object storage."


Step 1: Diagnose

Identify which buckets and access patterns consume the most bandwidth.

Check Bandwidth Metrics

# Check bucket usage
tigris usage

# List objects by size (find large files being served frequently)
tigris ls t3://my-bucket --recursive -l | sort -k3 -rn | head -20

Questions to Answer

  • Which buckets have the highest bandwidth?
  • Are large files being downloaded repeatedly?
  • Are objects being accessed through the app server or directly?
  • Are there objects being downloaded that are never displayed to users?

Step 2: Analyze Codebase

Check your application code for these common anti-patterns:

Anti-Pattern Checklist

Anti-Pattern Symptom Impact
Downloading full objects for metadata Using get() when head() would suffice High — downloads entire file
Re-downloading immutable assets No caching layer for static content High — repeated bandwidth
Proxying through app server Server downloads then re-serves to client 2x bandwidth (Tigris→server + server→client)
Missing Cache-Control headers Browsers re-fetch on every page load Very high — multiplied by users
Not using CDN Private bucket for public content Medium — single origin, no edge caching
Downloading full objects for thumbnails Fetching 5MB image to show 100px thumbnail High — 50x more data than needed

Code Search

Look for these patterns in your codebase:

# Downloads that should be head() calls
grep -rn "get(" --include="*.ts" --include="*.js" | grep -v "test"

# Missing cache headers on put()
grep -rn "put(" --include="*.ts" --include="*.js" | grep -v "cacheControl\|Cache-Control"

# Server-side file proxying
grep -rn "pipe\|stream\|createReadStream" --include="*.ts" --include="*.js"

Step 3: Fix

Fix 1: Use head() Instead of get() for Metadata

// Before — downloads entire file just to check if it exists
const result = await get("avatars/user-123.jpg", "file");
if (result.error) console.log("not found");

// After — only fetches metadata (size, contentType, modified)
const result = await head("avatars/user-123.jpg");
if (result.error) console.log("not found");
console.log(result.data?.size, result.data?.contentType);

Fix 2: Add Cache-Control Headers

// Set cache headers on upload
await put("assets/logo.png", file, {
  access: "public",
  contentType: "image/png",
});

Recommended cache values:

Content Type Cache-Control
Hashed assets (JS, CSS) public, max-age=31536000, immutable
Images (avatars, uploads) public, max-age=86400 (1 day)
Dynamic content private, no-cache
Fonts public, max-age=31536000, immutable

Fix 3: Use Public Buckets for CDN

Tigris public buckets automatically serve from the nearest global edge — no separate CDN setup needed.

# Make bucket public
tigris buckets create my-public-assets --public

# Or update existing bucket
tigris buckets update my-bucket --public

Public bucket URLs are served from Tigris's global edge network. This eliminates the need for CloudFront, Cloudflare, or other CDN layers for basic static asset delivery.

Fix 4: Use Presigned URLs (Skip Server Proxy)

// Before — server downloads file, then sends to client (2x egress)
app.get("/download/:path", async (req, res) => {
  const result = await get(req.params.path, "stream");
  result.data.pipe(res);
});

// After — redirect client to download directly from Tigris (1x egress)
app.get("/download/:path", async (req, res) => {
  const result = await getPresignedUrl(req.params.path, {
    operation: "get",
    expiresIn: 300,
  });
  res.redirect(result.data!.url);
});

Fix 5: Store Thumbnails Separately

// Before — serving 5MB original for a 100px avatar
<img src="/api/files/avatars/user-123.jpg" width="100" />

// After — generate and store thumbnail on upload
import sharp from "sharp";

const thumb = await sharp(originalBuffer)
  .resize(100, 100, { fit: "cover" })
  .jpeg({ quality: 80 })
  .toBuffer();

await put("avatars/user-123-thumb.jpg", thumb, {
  access: "public",
  contentType: "image/jpeg",
});
// Serve the 5KB thumbnail instead of the 5MB original

Fix 6: Client-Side Caching with ETags

// Server returns ETag on first request
app.get("/api/config", async (req, res) => {
  const result = await head("config/app.json");
  const etag = result.data?.modified?.toISOString();

  if (req.headers["if-none-match"] === etag) {
    return res.status(304).end(); // No body sent, no egress
  }

  const file = await get("config/app.json", "string");
  res.set("ETag", etag);
  res.json(JSON.parse(file.data));
});

Fix 7: Regional Pinning

Keep data close to compute to reduce cross-region transfer:

# Pin bucket to specific regions
tigris buckets create my-bucket --locations us-east-1,eu-west-1

Step 4: Verify

After applying fixes:

  1. Monitor bandwidth — check Tigris dashboard for bandwidth reduction
  2. Compare before/after — track bandwidth for 1-2 weeks
  3. Set alerts — configure monitoring for unexpected bandwidth spikes
  4. Review periodically — new features can introduce new anti-patterns

Quick Wins Summary

Fix Effort Impact
Add Cache-Control headers Low High
Switch to public bucket (CDN) Low High
Use presigned URLs instead of proxying Medium High
Replace get() with head() for checks Low Medium
Store thumbnails separately Medium High
Regional pinning Low Medium

Critical Rules

Always: Use public buckets for content served to users | Set Cache-Control headers on every upload | Use presigned URLs for private file downloads | Use head() for existence/metadata checks

Never: Proxy files through your app server when presigned URLs work | Serve original images when thumbnails suffice | Skip caching for immutable assets | Ignore bandwidth metrics


Related Skills

  • tigris-image-optimization — Generate thumbnails and variants
  • tigris-static-assets — CDN delivery with cache headers
  • tigris-lifecycle-management — Move cold data to cheaper tiers

Official Documentation

Weekly Installs
1
GitHub Stars
2
First Seen
7 days ago
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
warp1