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 #
๐ฏ 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