Skip to content

Instantly share code, notes, and snippets.

@reatlat
Created January 11, 2026 09:58
Show Gist options
  • Select an option

  • Save reatlat/0caeaa62996758ac57abebf6124de93f to your computer and use it in GitHub Desktop.

Select an option

Save reatlat/0caeaa62996758ac57abebf6124de93f to your computer and use it in GitHub Desktop.
Bash script to bulk delete Cloudflare Pages/Workers deployments when the dashboard won't let you

Delete Cloudflare Pages/Workers Deployments

A simple bash script to bulk delete Cloudflare Pages or Workers deployments via API.

Why?

Cloudflare won't let you delete a project with too many deployments. Their official solution requires Node.js and npm install. This script just needs curl and jq.

Usage

./delete-cf-deployments.sh \
  --project=my-site \
  --account=your-account-id \
  --token=your-api-token

Options:

  • --project Cloudflare Pages/Workers project name (required)
  • --account Your Cloudflare account ID (required)
  • --token Cloudflare API token (required)
  • --limit=N Delete only N deployments (for testing)
  • --keep=N Keep the last N deployments (cleanup mode)

Examples

Test with 3 deployments first

./delete-cf-deployments.sh --project=my-site --account=abc123 --token=xyz789 --limit=3

Keep last 10 deployments, delete the rest

./delete-cf-deployments.sh --project=my-site --account=abc123 --token=xyz789 --keep=10

Delete all and remove project

./delete-cf-deployments.sh --project=my-site --account=abc123 --token=xyz789

Getting credentials

  • API Token: Cloudflare Dashboard → My Profile → API Tokens → Create Token → "Edit Cloudflare Pages"
  • Account ID: Cloudflare Dashboard → any site → Overview → right sidebar under "API"

Full Article

👉 https://alex.zappa.dev/blog/cloudflare-pages-to-workers-migration/

MIT License

#!/bin/bash
# Delete Cloudflare Pages/Workers deployments via API
# Usage: ./delete-cf-deployments.sh --project=xxx --account=xxx --token=xxx [--limit=N] [--keep=N]
PROJECT=""
ACCOUNT_ID=""
TOKEN=""
LIMIT=""
KEEP=""
for arg in "$@"; do
case $arg in
--project=*) PROJECT="${arg#*=}" ;;
--account=*) ACCOUNT_ID="${arg#*=}" ;;
--token=*) TOKEN="${arg#*=}" ;;
--limit=*) LIMIT="${arg#*=}" ;;
--keep=*) KEEP="${arg#*=}" ;;
esac
done
if [ -z "$PROJECT" ] || [ -z "$ACCOUNT_ID" ] || [ -z "$TOKEN" ]; then
echo "Usage: $0 --project=<name> --account=<id> --token=<api-token> [options]"
echo ""
echo "Required:"
echo " --project=NAME Cloudflare Pages project name"
echo " --account=ID Your Cloudflare account ID"
echo " --token=TOKEN Cloudflare API token"
echo ""
echo "Options:"
echo " --limit=N Delete only N deployments (for testing)"
echo " --keep=N Keep the last N deployments (cleanup mode)"
echo ""
echo "Examples:"
echo " $0 --project=my-site --account=abc123 --token=xyz789 --limit=3"
echo " $0 --project=my-site --account=abc123 --token=xyz789 --keep=10"
echo " $0 --project=my-site --account=abc123 --token=xyz789"
echo ""
echo "Get API token: Cloudflare Dashboard → My Profile → API Tokens"
echo " → Create Token → Edit Cloudflare Pages"
echo ""
echo "Get Account ID: Cloudflare Dashboard → any site → Overview"
echo " → right sidebar under 'API'"
exit 1
fi
API="https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/pages/projects/$PROJECT"
echo "Deleting deployments for: $PROJECT"
[ -n "$LIMIT" ] && echo "Limit: $LIMIT"
[ -n "$KEEP" ] && echo "Keeping last: $KEEP deployments"
echo ""
COUNT=0
PAGE=1
# For --keep mode, we need to skip the first N deployments
# API returns deployments sorted by date (newest first)
# So we paginate past the ones we want to keep
if [ -n "$KEEP" ]; then
# Calculate which page to start from
PER_PAGE=25
SKIP_PAGES=$((KEEP / PER_PAGE))
SKIP_IN_PAGE=$((KEEP % PER_PAGE))
PAGE=$((SKIP_PAGES + 1))
echo "Skipping first $KEEP deployments (starting from page $PAGE)..."
echo ""
fi
while true; do
# Fetch deployments with pagination
RESPONSE=$(curl -s "$API/deployments?per_page=25&page=$PAGE" \
-H "Authorization: Bearer $TOKEN")
IDS=$(echo "$RESPONSE" | jq -r '.result[].id // empty' 2>/dev/null)
if [ -z "$IDS" ]; then
echo "No more deployments."
break
fi
# Convert to array
IDS_ARRAY=($IDS)
START_INDEX=0
# On the first page in --keep mode, skip partial entries
if [ -n "$KEEP" ] && [ "$PAGE" -eq "$((SKIP_PAGES + 1))" ] && [ "$SKIP_IN_PAGE" -gt 0 ]; then
START_INDEX=$SKIP_IN_PAGE
echo "Skipping first $SKIP_IN_PAGE on this page..."
fi
for ((i=START_INDEX; i<${#IDS_ARRAY[@]}; i++)); do
ID="${IDS_ARRAY[$i]}"
echo "Deleting $ID..."
curl -s -X DELETE "$API/deployments/$ID?force=true" \
-H "Authorization: Bearer $TOKEN" > /dev/null
COUNT=$((COUNT + 1))
if [ -n "$LIMIT" ] && [ "$COUNT" -ge "$LIMIT" ]; then
echo ""
echo "Reached limit of $LIMIT. Deleted $COUNT deployments."
exit 0
fi
sleep 0.3
done
# In normal mode (no --keep), always fetch page 1 since we're deleting
# In --keep mode, move to next page
if [ -z "$KEEP" ]; then
PAGE=1
else
PAGE=$((PAGE + 1))
fi
done
echo ""
echo "Done! Deleted $COUNT deployments."
echo ""
echo "To delete the project entirely, run:"
echo " npx wrangler pages project delete $PROJECT"
echo ""
echo "Or delete it via Cloudflare Dashboard:"
echo " Workers & Pages → $PROJECT → Settings → Delete project"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment