Three platform changes announced on 17 September land between 19 October and 2 November, and none of them needs you to touch a line of YAML to break a build. A public repo's pull_request_target workflow stops running, an ubuntu-latest job picks up a new OS mid-sprint, or a release script that quietly scraped the GitLab API without a token starts collecting 429s.
What follows: the dates, how to find affected workflows, and the fix for each. Our walkthrough of a CI/CD pipeline from commit to production covers where each stage sits, and our DevOps section has the wider pipeline coverage.

The deadline table
Change | Date | Who's affected | What breaks | What to do |
|---|---|---|---|---|
GitLab.com preview windows | 7 and 14 Oct, 15:00–19:00 UTC | Free plan users and unauthenticated callers | New limits switch on for four hours, then off | Run your heaviest pipelines inside a window and watch for 429s |
GitLab.com tier-aware rate limits (Free + unauthenticated) | 19 Oct 2026 | Anonymous API/web callers; Free-plan users, bots and service accounts | Anonymous traffic capped at 60 requests/hour per IP; Free users at 5,000/hour with a 100/minute burst | Authenticate everything, back off on |
| Gradual, 19 Oct – 19 Nov 2026 | Every job with | Builds that depend on OS package names, library versions or kernel behaviour | Test on |
Default rule disabling | Evaluate mode now; enforced 2 Nov 2026 | Public repositories with no existing event policy | Workflows triggered by | Migrate to |
GitLab.com limits for Premium and Ultimate | January 2027 | Paid plans | Premium at 15,000/hour (1,250/minute burst), Ultimate at 25,000/hour (2,000/minute) | Same fixes, with more headroom and a later deadline |
GitHub's pull_request_target default block
GitHub made workflow execution protections generally available on 17 September. The feature is an allowlist: actor rules decide who can trigger a workflow, event rules decide which events can start one, and you can set both at enterprise, organisation or repository level. GA added per-workflow-file targeting, an Insights view, a REST API for rules as code, and an evaluate mode that records what would have been blocked without blocking it.
The deadline is the new default. Public repositories that don't already have an event policy get a rule that disables pull_request_target. It's running in evaluate mode today, and from 2 November it's enforced. Private and internal repositories aren't covered by the default, and neither is any public repo where you've already defined an event policy.
pull_request_target runs in the context of the base repository, with its secrets and a write-capable token, while being triggered by someone else's fork. The moment such a workflow checks out and executes the PR head, you've handed a stranger your credentials. We covered one variant of this, cache poisoning, in our piece on secret-scanning merge blocks and Actions cache-mode.
Find every pull_request_target workflow
Code search is the fastest first pass, though it only sees the default branch:
# List repos in the org with a workflow that uses pull_request_target
gh search code "pull_request_target" --owner your-org \
--json repository,path --limit 200 \
| jq -r '.[] | "\(.repository.nameWithOwner) \(.path)"' | grep '.github/workflows'
For a complete answer that also restricts to public repos, clone shallowly and grep:
gh repo list your-org --visibility public --no-archived --limit 1000 --json nameWithOwner -q '.[].nameWithOwner' |
while read -r repo; do
dir=$(mktemp -d)
gh repo clone "$repo" "$dir" -- --depth 1 --quiet 2>/dev/null || continue
git -C "$dir" grep -l -E 'pull_request_target' -- .github/workflows 2>/dev/null | sed "s|^|$repo |"
rm -rf "$dir"
done
Then check the org's Insights view: evaluate mode gives you six weeks of real "would have blocked" data before anything is blocked.
The replacement pattern
Most of these workflows exist to do one privileged thing, like posting a comment or applying a label, after running untrusted code. Split them in two. The untrusted half runs on pull_request with no secrets and a read-only token, and uploads its output as an artifact:
# .github/workflows/pr-test.yml (runs fork code, no secrets)
on: pull_request
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- run: make test > results.txt
- uses: actions/upload-artifact@v4
with: { name: results, path: results.txt }
The privileged half runs on workflow_run, which executes the base branch's copy of the workflow and never checks out the fork:
# .github/workflows/pr-report.yml (has write access, never runs fork code)
on:
workflow_run:
workflows: ["pr-test"] # must match the name: of the first workflow (the file name if name: is omitted)
types: [completed]
permissions:
pull-requests: write
actions: read
jobs:
report:
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@v4
with:
name: results
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}
# Treat results.txt as attacker-controlled input: no eval, no shell interpolation
The cost is latency and a second workflow to maintain. If one really can't be split, allow-list that single file rather than switching the default off for the repo.
ubuntu-latest moves to 26.04
The Ubuntu 26.04 hosted runners went GA the same day, as ubuntu-26.04 and ubuntu-26.04-arm. The ubuntu-latest label moves from 24.04 to 26.04 gradually between 19 October and 19 November. For a month the same workflow can land on either image, so a failure that vanishes on re-run may be the label, not flakiness.
The software comparison in runner-images issue #14747 is reassuring at the tool layer. The CLIs it lists, including AWS, Azure and Google Cloud CLIs, Docker Buildx, Minikube, Rust and Firefox, sit at identical versions on both images, and Java 17 stays the default. It lists no removals as of today. What does change is underneath: Ubuntu 24.04.5 becomes 26.04.1, the kernel goes from 6.17 to 7.0, and systemd from 255 to 259. The builds at risk are the ones that apt-get install packages by name, link against system libraries, build kernel modules, or assume a particular systemd or cgroup behaviour.

Audit and test
Reuse the clone loop above with a different pattern (drop --visibility public, since this change hits private repos too):
git -C "$dir" grep -n -E 'runs-on:.*ubuntu-latest|ubuntu-latest' -- .github/workflows
Matrix entries count too. Then run important workflows against both images before 19 October:
jobs:
build:
strategy:
fail-fast: false # report both images, even if one fails
matrix:
os: [ubuntu-24.04, ubuntu-26.04]
runs-on: ${{ matrix.os }}
If 26.04 fails and the fix isn't trivial, pin runs-on: ubuntu-24.04 explicitly. That turns a month of random failures into an upgrade you schedule. Our view: pin release and deploy pipelines to explicit versions permanently and let only lint and unit-test jobs ride ubuntu-latest.
GitLab rate limits by plan
GitLab announced that GitLab.com limits become tier-aware, with Free and unauthenticated traffic moving on 19 October and Premium and Ultimate in January 2027. The numbers are in the rate limits documentation, not the blog post. They apply to API requests, web requests and authenticated Git over HTTPS, per user:
Caller | Sustained | Burst |
|---|---|---|
Unauthenticated (per IP) | 60/hour | none listed |
Free (per user) | 5,000/hour | 100/minute |
Premium (per user) | 15,000/hour | 1,250/minute |
Ultimate (per user) | 25,000/hour | 2,000/minute |
Today's limits are 500 a minute for unauthenticated traffic per IP and 2,000 a minute for authenticated API traffic per user. Anonymous callers lose almost everything; a Free-plan bot's burst budget drops twentyfold.
Two details matter. First, unauthenticated Git over HTTPS doesn't count against the 60/hour limit, so anonymous clones of public projects keep working under the existing per-IP limits. Second, the per-IP limit is per egress IP. If fifty runners sit behind one NAT gateway and each pulls a release asset or a package from /api/v4 without a token, they share 60 requests an hour between them.
The anonymous traffic usually hides in build scripts that curl a public project's releases API, package downloads from GitLab registries (under /api/v4), mirroring jobs, dependency bots, and AI coding agents running with no credentials configured, wherever they run.
Authenticate, back off, cache
In GitLab CI, use the job token where the endpoint accepts it and a scoped access token elsewhere. Give each bot or mirror its own service account so one noisy job doesn't burn a human's budget:
# Git over HTTPS from a GitLab CI job
git clone "https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/your-group/your-project.git"
# API call from anywhere else, with a read_api-scoped token
curl --fail -sS --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \
"https://gitlab.com/api/v4/projects/12345/releases/permalink/latest"
Throttled responses carry Retry-After, and every response carries RateLimit-Limit and RateLimit-Remaining. Honour them instead of retrying in a tight loop:
gl_get() {
local url=$1 attempt=0 wait
while (( attempt < 5 )); do
code=$(curl -sS -o body.json -D headers.txt -w '%{http_code}' \
--header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" "$url")
[[ $code != 429 ]] && { cat body.json; return 0; }
wait=$(grep -i '^retry-after:' headers.txt | tr -dc '0-9')
sleep "${wait:-$(( 2 ** attempt * 5 ))}" # fall back to exponential backoff
attempt=$(( attempt + 1 ))
done
return 1
}
Then cut volume: cache release metadata and packages, paginate instead of calling per item, and use webhooks instead of polling. With a 100/minute burst, a Free-plan account that fans out a hundred parallel calls hits the wall on the first run.
The preview windows on 7 and 14 October, 15:00–19:00 UTC, are the cheapest test you'll get: GitLab switches the new limits on for four hours, then off again. Schedule your busiest pipelines inside one and grep the logs for 429s.
Dated checklist
This week - Run the pull_request_target and ubuntu-latest searches across every org you own. - Open the Actions policies Insights view and note which workflows the default rule is flagging. - List every system that calls GitLab.com and whether it sends a token.
By 7 October - Add the ubuntu-24.04/ubuntu-26.04 matrix to release-critical workflows. - Issue scoped tokens and service accounts for GitLab bots, mirrors and agents. - Schedule heavy pipelines into the 15:00–19:00 UTC preview window.
By 19 October - Pin ubuntu-24.04 on anything that failed on 26.04 and file the fix as a ticket with an owner. - Confirm zero unauthenticated GitLab API calls from CI, and backoff in every client you control.
By 2 November - Every public-repo pull_request_target workflow is either split into pull_request + workflow_run or explicitly allow-listed, with a written reason.
Decision rule
If a workflow runs fork code, it gets no secrets, full stop; move the privileged step to workflow_run. If a job deploys or ships, it names its OS version explicitly. If a script talks to GitLab.com, it authenticates and reads Retry-After. Anything that fails one of those three tests is on your list for the next six weeks, whether or not it's red today.





