Stale PR and Issue Cleanup with Scheduled Automation
Dead issues and abandoned PRs quietly rot a repository. Use scheduled stale automation to mark, then close, so your queue stays honest and focused.
Netbay Infrastructure Team
Netbay Engineering
On this page
Backlogs rot from the bottom. The issue opened in April and never answered, the feature request "still relevant maybe," the pull request that went to draft and never came back. The rot costs real attention: old issues resurface in search with stale context, abandoned PRs convince new contributors to rebase ghosts, and the queue slowly teaches everyone to ignore it because it is mostly noise. Scheduled stale automation gives the queue a self-respecting definition of done.
What "stale" means, precisely
Stale has to be defined by activity, not age. An issue with a busy discussion is not stale at three months of open time; a PR untouched for three weeks, cluttering the list, is. The rule of thumb: activity means comments and state changes, not mere existence in the repository. That distinction is what keeps automation from declaring "open since 2024" as somehow an emergency.
Run older-than thresholds on both kinds of work, with different clocks. Pull requests decay faster and deserve tighter numbers than issues — theirs is a short-lived artifact tied to a diff that is steadily drifting from main.
The stale workflow
The reference implementation is the actions/stale workflow, triggerable on a schedule. A scheduler fires a single cron thread on owned runner, and the workflow dispatches itself:
name: stale
on:
schedule:
- cron: '30 6 * * 1' # 06:30 UTC every Monday
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
days-before-issue-stale: 60
days-before-issue-close: 14
stale-issue-message: >-
This issue has had no activity in 60 days. It will be closed
in 14 days unless something changes.
close-issue-message: Closing due to inactivity. Comment to reopen.
days-before-pr-stale: 30
days-before-pr-close: 7
stale-pr-label: stale-pr
exempt-issue-labels: 'security,roadmap,pinned,good first issue'
exempt-pr-labels: 'security,dependencies,draft'
operations-per-run: 400The two clocks are the entire design: first the stale label appears with a message, then a second threshold closes the thing. Nobody wakes up to a shutdown on a fresh ticket, and nobody presides over an execution by hand.
Exemptions are the policy work
The labeled exemptions are where a stale policy is really written. Bad automation closes the wrong things because no list protects them. Decide those lists once, deliberately: security-related issues and PRs for dependency bumps should not die on a clock, roadmap items are owned by planning, and good-first-issue entries are valuable as inventory, not as a queue. The "draft" exemption for PRs matters because a draft is already a self-managed state; automation should not knock on it. Match the lists to how your team actually works — labels that mark an intentional conclusion, like "blocked" or "waiting-on-third-party", are exactly the ones worth shielding, because they describe a decision the clock simply cannot see.
Watch the first week of runs
Stale automation has a sharp start. The first run will mark the oldest ten percent of your backlog, and the semannual cleanup will surface every ticket that should have been closed two years ago. That is the feature — but only if you treat the first run as a dry run:
# the backlog's bottom, oldest-first, before trusting a close pattern
gh issue list --state open --json number,title,updatedAt --jq 'sort_by(.updatedAt)[0:10][] | "(.number) (.updatedAt[:10]) (.title)"'Run with "debug-only: true" on the branch first run to see what would happen without doing it; tune thresholds for a cycle; then let it run and keep the Cron schedule honest. A workflow that pings only when humans broke something is how stale arrives without grief.
The trend line is the real deliverable. Track how many items the sweep labels and closes each month: a healthy queue shows an early spike as the backlog clears itself, then a low, flat line as automation becomes plain maintenance. If the close count stays high month after month, that is not the bot misbehaving — it is the repository reporting that issues are being filed but not owned, and the ownership conversation, not the cron line, is where the effort belongs.
The habit, not the tool
The finish line is a queue where every item has one of three states: someone owns it, it is parked with a label, or it is closed. Stale automation does the bookkeeping; the team decides park versus own. Done right, search results get more truthful by the quarter, and new contributors stop inheriting ghosts.
Takeaway: stale automation is two clocks and an exemption list, running on a schedule, with a dry-run first week. Give your queue a definition of done — and run the scheduler on something that stays on — a Lucknow DC01 VPS from Netbay, deployable in under 60 seconds at netbayhosts.in.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance