loops.yaml

Working examples

Start with a command
you can inspect.

Each recipe is a small, readable starting point. Copy one into loops.yaml, review its paths and credentials, run it once on demand, then add a schedule when you trust the result.

18 / 18 shown

  1. 01

    Nightly SQLite → R2

    backup 0 3 * * *

    Snapshot a SQLite DB and push it to an R2 bucket. Offsite backup in two lines.

    sqlite3 app.db ".backup /tmp/app-$(date +%F).db" && \
      wrangler r2 object put backups/app-$(date +%F).db --file /tmp/app-$(date +%F).db

    needs: sqlite3 · wrangler

  2. 02

    Mirror a folder to R2

    backup 0 */6 * * *

    Rsync-style upload of a working directory to R2 so a wiped VPS costs you nothing.

    tar czf /tmp/site.tgz ./site && \
      wrangler r2 object put backups/site-$(date +%FT%H).tgz --file /tmp/site.tgz

    needs: tar · wrangler

  3. 03

    Postgres dump to R2

    backup 30 2 * * *

    Compressed pg_dump straight into object storage. Keep the last 7 by date in the key.

    pg_dump "$DATABASE_URL" | gzip | \
      wrangler r2 object put db/pg-$(date +%F).sql.gz --pipe

    needs: pg_dump · gzip · wrangler

  4. 04

    TLS cert expiry check

    health 0 9 * * *

    Alert before a certificate expires. Catches the outage everyone forgets about.

    END=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null \
      | openssl x509 -noout -enddate | cut -d= -f2); \
      echo "cert for $DOMAIN expires $END"

    needs: openssl · curl

  5. 05

    Endpoint heartbeat

    health */5 * * * *

    Hit a URL, fail loud on non-200. The whole monitoring stack you need on day one.

    curl -fsS -o /dev/null -w "%{http_code}" https://example.com/health \
      || echo "DOWN at $(date)"

    needs: curl

  6. 06

    Disk space guard

    health */30 * * * *

    Warn when the VPS disk crosses 85%. Cheap insurance against a full-disk 500.

    df -P / | awk 'NR==2 && $5+0 > 85 {print "disk " $5 " on " $6}'

    needs: df · awk

  7. 07

    Purge Cloudflare cache

    cloudflare on-demand

    Bust the cache on deploy or schedule. One API call, no dashboard clicking.

    curl -fsS -X POST \
      "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
      -H "Authorization: Bearer $CF_API_TOKEN" \
      -H "Content-Type: application/json" \
      --data '{"purge_everything":true}'

    needs: curl

  8. 08

    Dynamic DNS updater

    cloudflare */15 * * * *

    Point a Cloudflare DNS record at your current IP. Self-hosted from a dynamic address.

    IP=$(curl -fsS https://api.ipify.org); \
      curl -fsS -X PATCH \
      "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \
      -H "Authorization: Bearer $CF_API_TOKEN" \
      --data "$(jq -n --arg ip "$IP" '{type:"A",content:$ip}')"

    needs: curl · jq

  9. 09

    Scheduled Worker deploy

    cloudflare on-demand

    Pull latest, deploy a Worker. A one-box CD pipeline with no CI service.

    git -C ./app pull --ff-only && \
      cd ./app && wrangler deploy

    needs: git · wrangler

  10. 10

    Summarize a log with Workers AI

    cloudflare 0 18 * * *

    Pipe the day’s log through a model and get a plain-English summary. Your agent in cron.

    TAIL=$(tail -c 6000 app.log | jq -Rs .); \
      curl -fsS "https://api.cloudflare.com/client/v4/accounts/$ACCT/ai/run/@cf/meta/llama-3.1-8b-instruct" \
      -H "Authorization: Bearer $CF_API_TOKEN" \
      --data "{\"prompt\":\"Summarize today's errors: \"$TAIL}"

    needs: curl · jq

  11. 11

    RSS → webhook digest

    notify 0 8 * * 1-5

    Fetch a feed, post new items to a chat webhook. Stay current without opening a tab.

    curl -fsS https://example.com/feed.json \
      | jq -r '.items[0:5][] | "• \(.title) — \(.url)"' \
      | curl -fsS "$CHAT_WEBHOOK" --data-binary @-

    needs: curl · jq

  12. 12

    Watch a page for change

    watch 0 */2 * * *

    Hash a page, ping you when it changes. Price drops, stock, job posts — anything.

    NEW=$(curl -fsS https://example.com/item | sha1sum | cut -c1-12); \
      OLD=$(cat .item-hash 2>/dev/null); \
      [ "$NEW" != "$OLD" ] && echo "changed" && echo "$NEW" > .item-hash

    needs: curl · sha1sum

  13. 13

    Watch a repo for releases

    watch 0 * * * *

    Poll a GitHub repo’s latest release; notify on a new tag. No webhooks to host.

    TAG=$(curl -fsS https://api.github.com/repos/$REPO/releases/latest | jq -r .tag_name); \
      [ "$TAG" != "$(cat .last-tag 2>/dev/null)" ] && \
      echo "new release $TAG" && echo "$TAG" > .last-tag

    needs: curl · jq

  14. 14

    Trim runaway logs

    chores 0 4 * * *

    Keep the last 5k lines of a log file. The unglamorous chore that prevents a 3am page.

    tail -n 5000 app.log > app.log.tmp && mv app.log.tmp app.log

    needs: tail · mv

  15. 15

    Sweep old temp files

    chores 0 5 * * *

    Delete files older than 7 days from a scratch dir. Boring, essential, two seconds.

    find /tmp/scratch -type f -mtime +7 -delete

    needs: find

  16. 16

    Load a CSV into D1

    data 0 1 * * *

    Pull a CSV and import it into a D1 table. A nightly ETL in one command.

    curl -fsS https://example.com/export.csv -o /tmp/in.csv && \
      wrangler d1 execute mydb --command \
      ".import /tmp/in.csv records"

    needs: curl · wrangler

  17. 17

    Snapshot an API to JSON

    data 0 0 * * *

    Save a daily copy of an API response. Cheap history you can diff later.

    curl -fsS https://api.example.com/stats \
      | jq . > snapshots/stats-$(date +%F).json

    needs: curl · jq

  18. 18

    Batch-make thumbnails

    chores */10 * * * *

    Resize every new image in a folder. ffmpeg/ImageMagick + cron beats a SaaS.

    for f in inbox/*.jpg; do \
      [ -f "thumbs/$(basename "$f")" ] || convert "$f" -resize 400x "thumbs/$(basename "$f")"; \
      done

    needs: ImageMagick