Skip to content

perf(selfhost): document retention/concurrency sizing, fix stale runner docs - #4142

Merged
JSONbored merged 1 commit into
mainfrom
perf/selfhost-resource-tuning-gaps-1828
Jul 8, 2026
Merged

perf(selfhost): document retention/concurrency sizing, fix stale runner docs#4142
JSONbored merged 1 commit into
mainfrom
perf/selfhost-resource-tuning-gaps-1828

Conversation

@JSONbored

@JSONbored JSONbored commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary

Behavior change, not just documentation: the Prometheus retention default drops from 180d to
90d. Any fresh self-host deployment that has not set PROMETHEUS_RETENTION_TIME explicitly will get
90 days of metrics history instead of 180 on its next docker compose up for that service. Existing
running deployments are unaffected until the prometheus container is recreated with the new image
(Prometheus does not retroactively delete data on a live container from a compose-file-only change);
recreation will prune anything older than 90 days. Operators who want to keep 180d (or any other
value) can set PROMETHEUS_RETENTION_TIME=180d in their .env to opt back in — the var was already
override-able before this PR, only the unset-fallback value changes.

Issue #1828's earlier work already shipped the per-service memory limits, the resource-profile
matrix, the image-size audit, the dashboard, and the Redis/Loki/Tempo sizing. This closes out the
three remaining gaps found on a direct re-check of the current docker-compose.yml/.env.example/docs:

  • docker-compose.yml's Prometheus --storage.tsdb.retention.time=${PROMETHEUS_RETENTION_TIME:-180d}
    had no sizing-rationale comment, unlike the sibling Loki (336h/14d) and Tempo (168h/7d) settings.
    Rather than just add a comment defending 180d as-is, I traced its history
    (git log -S "PROMETHEUS_RETENTION_TIME" -- docker-compose.yml): it was a deliberate 30d default
    until feat(selfhost): wire Codex reviews and secure observability #1678 silently turned it into an override-able var defaulted to 180d with zero rationale.
    Metrics genuinely can justify outliving logs/traces — Prometheus's TSDB compresses numeric samples
    to ~1-2 bytes each after compaction, so retaining them longer costs far less disk per day than
    Loki's raw log lines or Tempo's full span trees — but six months with no stated reasoning is drift,
    not a decision. Lowered the default to 90d (a full quarter for the capacity-planning trend window
    the docs already describe) and added a comment explaining both the "why longer than Loki/Tempo" and
    the "why not 180d" reasoning.
  • .env.example's QUEUE_CONCURRENCY (src/selfhost/sqlite-queue.ts, src/selfhost/pg-queue.ts,
    fallback 4) and QUEUE_BACKGROUND_CONCURRENCY (src/selfhost/queue-common.ts,
    DEFAULT_BACKGROUND_CONCURRENCY = 1) had no core-count-based sizing guidance, unlike the existing
    PGPOOL_MAX comment. Added guidance anchored to vCPU count (a 2-vCPU host is comfortable around the
    default of 4; 4+ vCPUs can reasonably go to 8-12) and scaled QUEUE_BACKGROUND_CONCURRENCY relative
    to QUEUE_CONCURRENCY (roughly a quarter to a third of it) rather than as an independent number.
  • apps/gittensory-ui/src/routes/docs.self-hosting-operations.tsx claimed in two places that the
    runner service "ships with no CPU/memory limit at all." That became false once RUNNER_MEM_LIMIT
    (default 2g, docker-compose.yml line ~820) was added for fix(selfhost): add default memory/CPU limits to the runner compose service #3893. Fixed both occurrences plus a
    third, directly adjacent table cell making the same now-stale claim ("Unbounded by default" in the
    Memory column) so the row is internally consistent — all three now correctly describe a default
    memory ceiling with CPU-priority pinning remaining opt-in via docker-compose.override.yml.example.

This is a docs + config-comment change; no src/**/packages/** lines were touched, so there is no
Codecov obligation on this diff (verified: git diff --name-only against origin/main shows only
docker-compose.yml, .env.example, and the one apps/gittensory-ui route file).

Closes #1828.

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked a currently open issue this PR resolves (Closes #1828) — verified open/unassigned via gh issue view 1828 --json state,assignees.

Validation

  • git diff --check
  • npm run actionlint (via npm run test:ci; no workflow files touched, ran anyway as part of the full gate)
  • npm run typecheck
  • npm run test:coverage locally — 582 passed / 2 skipped, unsharded. No src/**/packages/** lines changed, so no new coverage obligation; ran it anyway to confirm nothing broke.
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate — 0 vulnerabilities
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries — not applicable: no src/**/packages/** code changed, only prose/comments in docker-compose.yml, .env.example, and one docs route.

Additionally ran the full npm run test:ci gate (all steps: db:migrations:check, db:schema-drift:check,
selfhost:env-reference:check, selfhost:validate-observability, cf-typegen:check, test:coverage,
test:workers, build:mcp/test:mcp-pack, build:miner/test:miner-pack, rees:test,
ui:openapi:check/ui:openapi:settings-parity, ui:version-audit, docs:drift-check,
command-reference:check, ui:lint/ui:typecheck/ui:test/ui:build) — all green. Also validated
docker-compose.yml directly with docker compose -f docker-compose.yml config --quiet (exit 0) and
confirmed .env.example's new comment lines don't collide with selfhost:env-reference:check's
96-reference count (unchanged — these are comment-only edits to already-documented vars).

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. (N/A — no such changes.)
  • API/OpenAPI/MCP behavior is updated and tested where needed. (N/A — no such changes; ui:openapi:check confirms no drift.)
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. (N/A — static docs prose only.)
  • Visible UI changes include a UI Evidence section below with JPG/JPEG or PNG screenshots arranged as organized, captioned, clickable thumbnails. SVG screenshots are not used as review evidence. Review-only screenshots or recordings are not committed to the repository. (No visual/layout change — corrected prose within an existing table cell and an existing paragraph; see UI Evidence below.)
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs.

UI Evidence

Not applicable: this PR only corrects prose text inside an existing table cell and an existing
paragraph on an already-shipped docs page (docs.self-hosting-operations.tsx) — no new component,
layout, or styling. The rendered structure is identical to what is already live; only the words
describing the runner service's memory/CPU limits changed to match current behavior.

Notes

  • Verified against source, not restated from comments: docker-compose.yml's runner service
    (RUNNER_MEM_LIMIT default 2g, added for fix(selfhost): add default memory/CPU limits to the runner compose service #3893) and docker-compose.override.yml.example's
    cpu_shares/cpus CPU-priority mitigation (confirms CPU pinning is still opt-in); .env.example's
    existing PGPOOL_MAX comment (the style mirrored for the new QUEUE_CONCURRENCY/
    QUEUE_BACKGROUND_CONCURRENCY guidance); src/selfhost/sqlite-queue.ts and
    src/selfhost/pg-queue.ts (QUEUE_CONCURRENCY fallback of 4) and src/selfhost/queue-common.ts
    (DEFAULT_BACKGROUND_CONCURRENCY = 1); loki/loki-config.yml (retention_period: 336h) and
    tempo/tempo.yaml (block_retention: 168h) for the sibling retention comparison; and
    git log -S "PROMETHEUS_RETENTION_TIME" -- docker-compose.yml for the 30d→180d history behind the
    new 90d default.
  • No script or test asserts a specific Prometheus retention value (checked
    scripts/validate-observability-configs.mjs and test/), so lowering the default to 90d needed no
    test updates. Operators who want the old 180d (or the original 30d) can still set
    PROMETHEUS_RETENTION_TIME explicitly.
  • Double-checked both historical issue/PR numbers cited in this PR's comments, since they're asserted
    as fact in shipped docs/config: #3893 (docker-compose.yml's pre-existing RUNNER_MEM_LIMIT
    comment, which this PR does not change, only mirrors into the docs route) is a closed issue —
    gh api graphql confirms closedByPullRequestsReferences points at merged PR fix(selfhost): add default memory limit to the runner compose service #3913, "add default
    memory limit to the runner compose service." #1678 is merged PR "wire Codex reviews and secure
    observability" — git show on its docker-compose.yml hunk confirms it is the exact commit that
    changed the hardcoded 30d to ${PROMETHEUS_RETENTION_TIME:-180d}. Both check out.

…er docs (#1828)

Prometheus's 180d retention default had no sizing rationale and no comment,
unlike the explicitly-justified 336h/14d (Loki) and 168h/7d (Tempo) siblings;
git history shows it silently replaced a deliberate 30d default when it
became an override-able env var in #1678. Add a comment justifying why
metrics can reasonably outlive logs/traces (far cheaper per-day TSDB
storage) while lowering the default to 90d, a considered middle ground
rather than an unexplained six-month default.

QUEUE_CONCURRENCY/QUEUE_BACKGROUND_CONCURRENCY had no core-count-based
sizing guidance, unlike PGPOOL_MAX's existing "raise if you see X, watch Y"
treatment. Add equivalent guidance anchored to vCPU count.

The self-hosting-operations docs claimed the runner service "ships with no
CPU/memory limit at all" in two places; this became false once
RUNNER_MEM_LIMIT (default 2g) was added for #3893. Fix both occurrences,
plus the adjacent table cell making the same now-incorrect claim, to
describe the current state: a default memory ceiling with CPU-priority
pinning remaining opt-in via docker-compose.override.yml.example.
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
gittensory-ui e16e74f Commit Preview URL

Branch Preview URL
Jul 08 2026, 07:40 AM

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 8, 2026
@loopover-orb

loopover-orb Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Warning

🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨

⏸️ Gittensory review result - manual review recommended

Review updated: 2026-07-08 07:44:13 UTC

3 files · 1 AI reviewer · no blockers · readiness 100/100 · CI green · clean

⏸️ Suggested Action - Manual Review

  • Touches a guarded path — held for manual review: This PR changes guardrail-protected path(s): apps/gittensory-ui/src/routes/docs.self-hosting-operations.tsx (matched apps/gittensory-ui/src/routes/**).

Review summary
Small, well-scoped doc/config PR closing out issue #1828: it corrects stale runner-limit docs (RUNNER_MEM_LIMIT now documented as bounding memory while CPU stays unlimited), adds core-count sizing guidance for QUEUE_CONCURRENCY/QUEUE_BACKGROUND_CONCURRENCY in .env.example, and lowers the undocumented PROMETHEUS_RETENTION_TIME default from 180d to 90d with a comment explaining the Loki/Tempo contrast. The docs table edit is internally consistent (correctly splits the prior single 'unbounded' cell into a bounded-memory cell and a still-unbounded-CPU cell), and the retention default change only affects fresh deployments that don't override the env var, matching the stated rationale.

Nits — 8 non-blocking
  • docs.self-hosting-operations.tsx:342 attributes RUNNER_MEM_LIMIT's addition to 'fix(selfhost): add default memory/CPU limits to the runner compose service #3893' and the docker-compose.yml comment cites 'feat(selfhost): wire Codex reviews and secure observability #1678' for the original 180d override — worth double-checking these PR numbers are accurate before merge since they're asserted as historical fact in user-facing docs.
  • No test-path changes accompany this PR, but that's expected here since the diff is comments/docs/env-default only with no branching logic to cover.
  • The retention default drop from 180d to 90d is a real behavioral change for any fresh self-host deploy that doesn't set PROMETHEUS_RETENTION_TIME explicitly — worth calling out explicitly in the PR description as a behavior change rather than pure documentation, even though it's justified inline.
  • Confirm the fix(selfhost): add default memory/CPU limits to the runner compose service #3893/feat(selfhost): wire Codex reviews and secure observability #1678 issue/PR references in docker-compose.yml and docs.self-hosting-operations.tsx resolve to the commits they describe, since they're now load-bearing historical claims in shipped docs.
  • Consider adding a one-line CHANGELOG or release-note mention that the Prometheus retention default changed from 180d to 90d, since existing self-hosters who haven't set the var will silently get shorter retention on their next `docker compose up`.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
  • Code changes lack test evidence — Add focused regression tests or explain why existing coverage is sufficient.
  • Touches a guarded path — held for manual review — A maintainer must review and merge this change.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #1828
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 52 registered-repo PR(s), 43 merged, 496 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 52 PR(s), 496 issue(s).
Gate result ⚠️ Not blocking Advisory; not blocking this PR.
Linked issue satisfaction

Partially addressed
The PR adds documentation/comments (Prometheus retention rationale, queue concurrency sizing guidance, and doc text fixes about RUNNER_MEM_LIMIT) but does not perform the broader profiling work the issue asks for—no CPU/memory/disk/startup measurements across profiles, no new resource-profile matrix, and no image-size audit, since the PR explicitly states that prior work already covered those and

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, TypeScript, JavaScript, Ruby, Go, Kotlin, MDX, Shell
  • Official Gittensor activity: 52 PR(s), 496 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 8, 2026
@JSONbored

Copy link
Copy Markdown
Owner Author

Went through all 8 nits from the review:

1/4. #3893/#1678 citation accuracy — verified both, not just re-asserted:

  • #3893 (pre-existing docker-compose.yml comment, unchanged by this PR, only mirrored into the
    docs route) is a closed issue whose closedByPullRequestsReferences (via gh api graphql) points
    at merged PR fix(selfhost): add default memory limit to the runner compose service #3913, "add default memory limit to the runner compose service" — accurate.
  • #1678 is merged PR "wire Codex reviews and secure observability"; git show on its
    docker-compose.yml hunk is the exact commit that changed the hardcoded 30d retention to
    ${PROMETHEUS_RETENTION_TIME:-180d} — accurate.
  1. Retention default is a real behavior change, not pure docs — agreed, added an explicit
    "Behavior change" callout at the top of the Summary (was previously only in Notes) spelling out
    which deployments are affected and how to opt back into 180d.

  2. CHANGELOG/release-note mention — not doing this one: CHANGELOG.md is explicitly out of scope
    for a normal PR per this repo's own contributing rules (only edited for release-prep PRs), and
    that's now also stated as a checked box in this PR's own Safety section. The explicit "Behavior
    change" callout added to the Summary (see nit 3) covers the same operator-facing need without
    touching the changelog.

  3. Self-authored linked issueperf(selfhost): profile stack resources and tune defaults #1828 is a maintainer-only roadmap sub-issue (no gittensor:*
    labels, assigned to the parent roadmap roadmap(selfhost): production hardening and first release readiness #1819, not community-sourced discovery work), and the
    review's own "Contributor next steps" section already classifies this as maintainer-lane rather
    than normal contributor-lane activity. No rationale gap here.

2/7. No tests / test-path changes — as the review itself notes for nit 2, expected: the diff is
comments/docs/env-default only, no src/**/packages/** branching logic to cover (verified via
git diff --name-only against origin/main). Already explained in the Validation section.

  1. Guarded-path hold — correct and expected (apps/gittensory-ui/src/routes/**); leaving this PR
    open for a separate manual merge pass rather than merging it myself.

On the "linked issue satisfaction: partially addressed" note: correct as far as it goes for what this
diff alone does — it doesn't re-run the profiling/measurement work. That work (per-service memory
limits, the resource-profile matrix, the image-size audit, the dashboard, Redis/Loki/Tempo sizing) is
already shipped on main from earlier PRs under this same issue; this PR closes the three remaining
gaps identified on a direct re-check of current docker-compose.yml/.env.example/docs against the
issue's acceptance criteria.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Development

Successfully merging this pull request may close these issues.

perf(selfhost): profile stack resources and tune defaults

1 participant