Grab Worker Deployment Guide#
Comprehensive deployment guide for the Aesthetic Computer screenshot/preview generation service.
✅ Current Status#
Deployed Version: 511d31bc-c2e8-4f00-aea7-9b9d95e08733
Worker URL: https://aesthetic-grab.aesthetic-computer.workers.dev
Custom Domain: grab.aesthetic.computer (⚠️ pending manual setup)
Test Commands:
# Test icon endpoint
curl -I "https://aesthetic-grab.aesthetic-computer.workers.dev/icon/128x128/prompt.png"
# Test preview endpoint
curl -I "https://aesthetic-grab.aesthetic-computer.workers.dev/preview/1200x630/prompt.png"
🚀 Quick Deploy with Automatic DNS#
Recommended: One-command deployment with automatic DNS configuration
cd /workspaces/aesthetic-computer/grab
./scripts/deploy-with-dns.fish
This will:
- ✅ Deploy worker to Cloudflare
- ✅ Automatically create/update CNAME record via API
- ✅ Wait for DNS propagation
- ✅ Verify deployment is accessible
Requirements:
CLOUDFLARE_EMAILin vault/.envCLOUDFLARE_API_TOKENin vault/.env (with Zone.DNS Edit permission)
See vault README for how to get API token: /aesthetic-computer-vault/grab/README.md
🌐 Custom Domain Setup#
Automated via deploy script (recommended): The deploy-with-dns.fish script handles this automatically.
Manual methods if needed:
Method 1: Direct DNS Configuration (2 minutes) ⚡#
Quick link: https://dash.cloudflare.com/a23b54e8877a833a1cf8db7765bce3ca/aesthetic.computer/dns/records
-
Open DNS Settings
- Navigate to: Cloudflare Dashboard → aesthetic.computer → DNS → Records
-
Add CNAME Record
- Click: "Add record"
- Type: CNAME
- Name:
grab - Target:
aesthetic-grab.aesthetic-computer.workers.dev - Proxy status: Proxied (orange cloud ☁️)
- Click: Save
-
Wait ~30 seconds for DNS propagation
-
Verify Setup
curl -I "https://grab.aesthetic.computer/icon/128x128/prompt.png"Should return:
HTTP/2 200 content-type: image/png cache-control: public, max-age=3600
✅ Done! Total time: ~2 minutes
Method 2: Workers Dashboard (Alternative)#
- Navigate to: https://dash.cloudflare.com/
- Go to: Workers & Pages → aesthetic-grab → Settings → Domains & Routes
- Click: "Add Custom Domain"
- Enter:
grab.aesthetic.computer - Save and wait 2-5 minutes
This automatically creates the same CNAME record as Method 1.
DNS Record Created#
Either method creates:
grab.aesthetic.computer → aesthetic-grab.aesthetic-computer.workers.dev (CNAME, Proxied)
Cloudflare automatically handles:
- ✅ SSL/TLS certificates
- ✅ Edge routing
- ✅ DDoS protection
- ✅ CDN caching
📋 Prerequisites#
- Cloudflare account with Workers enabled
- Browser Rendering enabled in your Cloudflare account
- Wrangler CLI installed globally (
npm i -g wrangler) - Access to
/aesthetic-computer-vault/grab/secrets - Node.js 20+ installed
🔐 Initial Setup#
1. Clone and Install#
cd /workspaces/aesthetic-computer/grab
npm install
2. Authenticate with Cloudflare#
wrangler login
This will open a browser to authenticate your Cloudflare account.
3. Verify Account Access#
wrangler whoami
Ensure:
- Account ID is correct
- Browser Rendering is in enabled features
- Workers permission is active
4. Enable Browser Rendering#
If not already enabled:
# Enable via Cloudflare Dashboard
# Workers & Pages > Browser Rendering > Enable
Or via API:
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/browser" \
-H "X-Auth-Email: me@jas.life" \
-H "X-Auth-Key: <API_KEY>" \
-H "Content-Type: application/json"
🚀 Deployment Steps#
Phase 1: Create Infrastructure#
1.1 Create Browser Binding#
cd /workspaces/aesthetic-computer/grab
./scripts/setup-browser-binding.fish
This script will:
- Create the browser binding named
BROWSER - Output the binding configuration
- Update the vault
.envfile
Alternatively, manually:
wrangler browser create BROWSER
1.2 Create Durable Object Namespace#
wrangler dispatch-namespace create SCREENSHOT_DO
Copy the output ID and update /aesthetic-computer-vault/grab/.env:
DO_NAMESPACE_ID=<paste-id-here>
1.3 Create R2 Bucket (Optional)#
For long-term caching:
wrangler r2 bucket create aesthetic-screenshots
Note the bucket name/ID for configuration.
Phase 2: Configure Wrangler#
2.1 Copy Production Config#
# Copy template from vault to working directory
cp /workspaces/aesthetic-computer/aesthetic-computer-vault/grab/wrangler.production.toml \
/workspaces/aesthetic-computer/grab/wrangler.toml
2.2 Update IDs in wrangler.toml#
Edit /workspaces/aesthetic-computer/grab/wrangler.toml:
# Update these with actual IDs from previous steps
[[durable_objects.bindings]]
name = "SCREENSHOT_DO"
class_name = "ScreenshotDO"
script_name = "aesthetic-grab"
# namespace_id = "<from step 1.2>" # If needed
[[r2_buckets]]
binding = "SCREENSHOTS"
bucket_name = "aesthetic-screenshots" # From step 1.3
⚠️ Important: Add wrangler.toml to .gitignore (it's environment-specific)
Phase 3: Set Secrets#
If you have additional secrets (optional):
cd /workspaces/aesthetic-computer/grab
wrangler secret put BROWSER_API_KEY
# Enter secret when prompted
Phase 4: Test Locally#
4.1 Start Dev Server#
npm run dev
4.2 Test Endpoints#
# Test icon
curl http://localhost:8787/icon/128x128/welcome.png -o test-icon.png
open test-icon.png
# Test preview
curl http://localhost:8787/preview/1200x630/prompt~wipe.png -o test-preview.png
open test-preview.png
# Test caching with ETag
curl -I http://localhost:8787/icon/128x128/welcome.png
# Note the ETag header
curl -H "If-None-Match: <etag-from-above>" \
http://localhost:8787/icon/128x128/welcome.png
# Should return 304 Not Modified
Phase 5: Deploy to Production#
5.1 Type Check#
npm run type-check
Fix any TypeScript errors before deploying.
5.2 Deploy#
npm run deploy:production
Or manually:
wrangler deploy --config wrangler.toml
5.3 Verify Deployment#
# Check worker is live
curl -I https://aesthetic-grab.<account>.workers.dev/icon/128x128/welcome.png
# View logs
npm run logs
Phase 6: Configure Custom Domain#
Via Cloudflare Dashboard (Recommended)#
- Go to https://dash.cloudflare.com/
- Select your account
- Navigate to Workers & Pages
- Click on aesthetic-grab
- Go to Settings > Domains & Routes
- Click Add Custom Domain
- Enter:
grab.aesthetic.computer - Click Add Domain
Cloudflare will automatically:
- Create DNS CNAME record
- Set up SSL/TLS
- Route traffic to worker
Via Wrangler (Alternative)#
wrangler route add grab.aesthetic.computer/* aesthetic-grab
Verify Domain#
# Test custom domain
curl -I https://grab.aesthetic.computer/icon/128x128/welcome.png
# Should return 200 with your screenshot
🔄 Migration from Netlify#
Step 1: Verify Worker is Working#
Test all resolutions:
# Icon (128x128)
curl https://grab.aesthetic.computer/icon/128x128/welcome.png -o test-icon.png
# OG Image (1200x630)
curl https://grab.aesthetic.computer/preview/1200x630/prompt~wipe.png -o test-og.png
# Twitter Card (1800x900)
curl https://grab.aesthetic.computer/preview/1800x900/prompt~wipe.png -o test-twitter.png
Step 2: Update index.mjs#
Edit /workspaces/aesthetic-computer/system/netlify/functions/index.mjs:
Find the icon and ogImage generation (around line 360-380):
Before:
const icon = pixelPerfect(
baseURL,
parsed?.host,
`/icon/128x128/${location}.png`,
meta,
"https:",
);
const ogImage = pixelPerfect(
baseURL,
parsed?.host,
`/preview/1200x630/${location}.png`,
meta,
"https:",
);
const twitterImage = pixelPerfect(
baseURL,
parsed?.host,
`/preview/1800x900/${location}.png`,
meta,
"https:",
);
After:
// Direct URLs to grab worker
const icon = `https://grab.aesthetic.computer/icon/128x128/${location}.png`;
const ogImage = `https://grab.aesthetic.computer/preview/1200x630/${location}.png`;
const twitterImage = `https://grab.aesthetic.computer/preview/1800x900/${location}.png`;
Step 3: Add Redirects (Optional)#
Edit /workspaces/aesthetic-computer/system/netlify.toml:
# Redirect legacy paths to grab worker
[[redirects]]
from = "/icon/*"
to = "https://grab.aesthetic.computer/icon/:splat"
status = 200
force = true
[[redirects]]
from = "/preview/*"
to = "https://grab.aesthetic.computer/preview/:splat"
status = 200
force = true
Step 4: Deploy and Test#
# Commit changes
git add system/netlify/functions/index.mjs
git commit -m "Switch to grab worker for screenshots"
git push
# Test on live site
curl -I https://aesthetic.computer/icon/128x128/welcome.png
# Should redirect to grab.aesthetic.computer
# Check HTML meta tags
curl https://aesthetic.computer/welcome | grep "og:image"
# Should show grab.aesthetic.computer URLs
Step 5: Monitor#
# Watch grab worker logs
npm run logs:production
# Check for errors
# Monitor traffic in Cloudflare Dashboard
Step 6: Clean Up (After 1 Week)#
Once confirmed working:
# Remove old screenshot function
rm /workspaces/aesthetic-computer/system/netlify/functions/screenshot.js
# Remove Puppeteer dependencies
cd /workspaces/aesthetic-computer/system
npm uninstall puppeteer-core @sparticuz/chromium
# Clean dev cache
rm -rf /workspaces/aesthetic-computer/.netlify/cache/screenshots
📊 Post-Deployment Monitoring#
Check Metrics#
In Cloudflare Dashboard:
- Workers & Pages > aesthetic-grab > Metrics
- Look for:
- Request rate
- Error rate
- P95 latency
- Cache hit rate
Set Up Alerts#
Configure notifications for:
- Error rate > 5%
- P95 latency > 10s
- Browser session failures
View Logs#
# Real-time logs
wrangler tail aesthetic-grab --env production
# Filter for errors
wrangler tail aesthetic-grab --env production --status error
Test Cache Performance#
# First request (cache miss)
time curl https://grab.aesthetic.computer/icon/128x128/welcome.png -o /dev/null
# Second request (cache hit)
time curl https://grab.aesthetic.computer/icon/128x128/welcome.png -o /dev/null
# Second should be much faster
🐛 Troubleshooting#
Deployment Fails#
Error: Browser rendering not enabled
Solution:
# Enable in dashboard or via API
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/browser" \
-H "X-Auth-Email: me@jas.life" \
-H "X-Auth-Key: <API_KEY>"
Error: Durable Object namespace not found
Solution:
# Recreate namespace
wrangler dispatch-namespace create SCREENSHOT_DO
# Update ID in wrangler.toml
Error: R2 bucket not found
Solution:
# Create bucket
wrangler r2 bucket create aesthetic-screenshots
# Or remove R2 binding from wrangler.toml if not using
Runtime Errors#
Error: Browser timeout
Solution:
- Increase
BROWSER_TIMEOUT_MSin wrangler.toml - Check network connectivity
- Verify aesthetic.computer is accessible
Error: Durable Object not found
Solution:
- Check namespace ID is correct
- Verify migration ran successfully
- Check worker name matches in binding
Error: Rate limit exceeded
Solution:
- Increase
MAX_REQUESTS_PER_MINUTE - Implement IP-based throttling
- Check for abuse/spam
Performance Issues#
Problem: Slow screenshot generation
Solutions:
- Check browser rendering time in logs
- Optimize page load (reduce assets)
- Increase viewport/timeout limits
- Pre-warm Durable Objects
Problem: High costs
Solutions:
- Review cache hit rate
- Increase cache TTL
- Implement R2 long-term cache
- Reduce browser timeout
- Monitor usage dashboard
🔄 Rollback Procedure#
If issues occur:
1. Quick Rollback (Domain)#
In Cloudflare Dashboard:
- Workers & Pages > aesthetic-grab
- Domains & Routes
- Remove custom domain temporarily
- Update index.mjs to point back to Netlify
2. Full Rollback#
# Revert index.mjs changes
git revert <commit-hash>
git push
# Remove redirects from netlify.toml
# Keep grab worker for testing/debugging
3. Emergency Fallback#
Add to index.mjs:
const useGrabWorker = false; // Emergency kill switch
const icon = useGrabWorker
? `https://grab.aesthetic.computer/icon/128x128/${location}.png`
: pixelPerfect(baseURL, parsed?.host, `/icon/128x128/${location}.png`, meta, "https:");
📝 Deployment Checklist#
- Authenticated with Cloudflare
- Browser Rendering enabled
- Browser binding created
- Durable Object namespace created
- R2 bucket created (optional)
- wrangler.toml configured
- Secrets set (if needed)
- Local testing passed
- TypeScript check passed
- Deployed to production
- Custom domain configured
- Domain DNS propagated (check with
dig) - All resolutions tested
- Cache behavior verified
- index.mjs updated
- Redirects added (optional)
- Changes committed and pushed
- Monitoring set up
- Alerts configured
- Documentation updated
- Team notified
🎯 Success Criteria#
After deployment, verify:
✅ All three resolutions work ✅ Cache hit rate > 80% ✅ P95 latency < 5s ✅ Error rate < 1% ✅ Browser rendering cost reasonable ✅ No 500 errors in logs ✅ Custom domain works ✅ ETag caching works ✅ CDN caching works
📞 Support#
For issues:
- Check logs:
npm run logs:production - Review Cloudflare Dashboard metrics
- Check GitHub issues
- Contact Cloudflare support if infrastructure issues
Status: 📝 Ready for Deployment
Last Updated: 2025-01-09