chore(forgejo): track compose file with retention/cleanup settings #3543

Merged
bot-backend merged 1 commit from chore/forgejo-retention-settings into main 2026-09-17 12:54:01 +00:00
Owner

Summary

app.ini on prod had no [cron.archive_cleanup] section and no retention keys under [actions] -- everything ran on Forgejo defaults (archive cache swept once/24h, no cap on Actions log/artifact age). That's how the repo-archive cache grew to ~48GB/145GB disk before the daily sweep caught up (Caddy now blocks that URL path in #3534 -- this PR is the second line of defense if that rule is ever lifted).

Forgejo is not part of the automated deploy pipeline, and its compose file only ever existed on the host (/home/gendesign/forgejo/docker-compose.yml -- confirmed it's a plain directory, not a git checkout). So there was nowhere for this config to live, survive a redeploy, or be reviewed. This PR adds a tracked copy at ops/forgejo/docker-compose.yml, matching the existing pattern of ops/backup.sh / ops/restore.sh for host-level infra not covered by docker-compose.prod.yml.

What's managed, and how

Config is driven entirely by FORGEJO__* env vars in the compose file (confirmed -- the live compose already used this for database/server/service/actions), not by hand-editing app.ini. Env vars are declarative/versioned/redeploy-safe, vs. editing the bind-mounted app.ini directly which survives container recreation via the volume but isn't reviewable or reproducible.

New keys added:

FORGEJO__actions__LOG_RETENTION_DAYS: "30"
FORGEJO__actions__ARTIFACT_RETENTION_DAYS: "14"
FORGEJO__CRON_0x2E_ARCHIVE_CLEANUP__ENABLED: "true"
FORGEJO__CRON_0x2E_ARCHIVE_CLEANUP__RUN_AT_START: "true"
FORGEJO__CRON_0x2E_ARCHIVE_CLEANUP__SCHEDULE: "@every 1h"
FORGEJO__CRON_0x2E_ARCHIVE_CLEANUP__OLDER_THAN: "1h"

Dotted section name ([cron.archive_cleanup]) needs the _0x2E_ escape for . -- per the container's own environment-to-ini --help, and confirmed empirically with a dry-run (environment-to-ini --config /dev/null --out /tmp/... inside the container, scratch path only, no prod file touched) that produced exactly [cron.archive_cleanup] with the expected keys.

How key names were verified

Not from memory -- a wrong key is silently dropped and creates a false sense of safety. Verified by extracting Go struct ini:"..." tags directly from the running binary (codeberg.org/forgejo/forgejo:10, Forgejo 10.0.3+gitea-1.22.0):

  • ArtifactRetentionDays int64 ini:"ARTIFACT_RETENTION_DAYS", LogRetentionDays int64 ini:"LOG_RETENTION_DAYS" -- explicit tags, found literally in the binary under the [actions] struct (same struct as the existing ENABLED/DEFAULT_ACTIONS_URL keys).
  • cron.OlderThanConfig / cron.BaseConfig (used by registerArchiveCleanup) have no explicit ini tags -- Gitea/Forgejo's global AllCapsUnderscore name mapper applies, the well-established convention for every [cron.*] section (Enabled->ENABLED, RunAtStart->RUN_AT_START, Schedule->SCHEDULE, OlderThan->OLDER_THAN).

Retention values vs. current workflows

Checked .forgejo/workflows/ and .github/workflows/ (ci.yml, deploy.yml, deploy-obsidian.yml) for artifact usage -- none upload or download artifacts today, so 14-day artifact retention can't break a cross-job dependency. 30-day log retention keeps a full month of CI run logs for failure triage. If a workflow ever adds actions/upload-artifact with a longer inter-job gap, bump ARTIFACT_RETENTION_DAYS accordingly.

Not applied to prod

This PR only adds the tracked file. To take effect on the VM:

scp ops/forgejo/docker-compose.yml gendesign:/home/gendesign/forgejo/docker-compose.yml
ssh gendesign "cd /home/gendesign/forgejo && docker compose up -d --force-recreate forgejo"

up -d --force-recreate is required, not restart -- Forgejo regenerates app.ini from FORGEJO__* env vars via environment-to-ini at container start, and restart doesn't re-read a changed environment: block.

After restart, sanity-check with (read-only):

ssh gendesign "docker exec forgejo grep -A5 '\[cron.archive_cleanup\]' /home/gendesign/forgejo/data/forgejo/gitea/conf/app.ini"

Test plan

  • python -c "import yaml; yaml.safe_load(...)" -- YAML parses
  • Dry-run of environment-to-ini inside the container confirmed the _0x2E_ escape produces the correct [cron.archive_cleanup] section (scratch path, not prod config)
  • Manual: apply on host per instructions above, confirm app.ini picks up new sections, watch /data/gitea/repo-archive size and actions_log dir over the next 24h

🤖 Generated with Claude Code

https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG

## Summary `app.ini` on prod had no `[cron.archive_cleanup]` section and no retention keys under `[actions]` -- everything ran on Forgejo defaults (archive cache swept once/24h, no cap on Actions log/artifact age). That's how the repo-archive cache grew to ~48GB/145GB disk before the daily sweep caught up (Caddy now blocks that URL path in #3534 -- this PR is the second line of defense if that rule is ever lifted). Forgejo is **not** part of the automated deploy pipeline, and its compose file only ever existed on the host (`/home/gendesign/forgejo/docker-compose.yml` -- confirmed it's a plain directory, not a git checkout). So there was nowhere for this config to live, survive a redeploy, or be reviewed. This PR adds a tracked copy at `ops/forgejo/docker-compose.yml`, matching the existing pattern of `ops/backup.sh` / `ops/restore.sh` for host-level infra not covered by `docker-compose.prod.yml`. ## What's managed, and how Config is driven entirely by `FORGEJO__*` env vars in the compose file (confirmed -- the live compose already used this for `database`/`server`/`service`/`actions`), **not** by hand-editing `app.ini`. Env vars are declarative/versioned/redeploy-safe, vs. editing the bind-mounted `app.ini` directly which survives container recreation via the volume but isn't reviewable or reproducible. New keys added: ```yaml FORGEJO__actions__LOG_RETENTION_DAYS: "30" FORGEJO__actions__ARTIFACT_RETENTION_DAYS: "14" FORGEJO__CRON_0x2E_ARCHIVE_CLEANUP__ENABLED: "true" FORGEJO__CRON_0x2E_ARCHIVE_CLEANUP__RUN_AT_START: "true" FORGEJO__CRON_0x2E_ARCHIVE_CLEANUP__SCHEDULE: "@every 1h" FORGEJO__CRON_0x2E_ARCHIVE_CLEANUP__OLDER_THAN: "1h" ``` Dotted section name (`[cron.archive_cleanup]`) needs the `_0x2E_` escape for `.` -- per the container's own `environment-to-ini --help`, and confirmed empirically with a dry-run (`environment-to-ini --config /dev/null --out /tmp/...` inside the container, scratch path only, no prod file touched) that produced exactly `[cron.archive_cleanup]` with the expected keys. ## How key names were verified Not from memory -- a wrong key is silently dropped and creates a false sense of safety. Verified by extracting Go struct `ini:"..."` tags directly from the running binary (`codeberg.org/forgejo/forgejo:10`, Forgejo 10.0.3+gitea-1.22.0): - `ArtifactRetentionDays int64 ini:"ARTIFACT_RETENTION_DAYS"`, `LogRetentionDays int64 ini:"LOG_RETENTION_DAYS"` -- explicit tags, found literally in the binary under the `[actions]` struct (same struct as the existing `ENABLED`/`DEFAULT_ACTIONS_URL` keys). - `cron.OlderThanConfig` / `cron.BaseConfig` (used by `registerArchiveCleanup`) have no explicit ini tags -- Gitea/Forgejo's global `AllCapsUnderscore` name mapper applies, the well-established convention for every `[cron.*]` section (`Enabled`->`ENABLED`, `RunAtStart`->`RUN_AT_START`, `Schedule`->`SCHEDULE`, `OlderThan`->`OLDER_THAN`). ## Retention values vs. current workflows Checked `.forgejo/workflows/` and `.github/workflows/` (`ci.yml`, `deploy.yml`, `deploy-obsidian.yml`) for artifact usage -- none upload or download artifacts today, so 14-day artifact retention can't break a cross-job dependency. 30-day log retention keeps a full month of CI run logs for failure triage. If a workflow ever adds `actions/upload-artifact` with a longer inter-job gap, bump `ARTIFACT_RETENTION_DAYS` accordingly. ## Not applied to prod This PR only adds the tracked file. To take effect on the VM: ```bash scp ops/forgejo/docker-compose.yml gendesign:/home/gendesign/forgejo/docker-compose.yml ssh gendesign "cd /home/gendesign/forgejo && docker compose up -d --force-recreate forgejo" ``` `up -d --force-recreate` is required, not `restart` -- Forgejo regenerates `app.ini` from `FORGEJO__*` env vars via `environment-to-ini` at container start, and `restart` doesn't re-read a changed `environment:` block. After restart, sanity-check with (read-only): ```bash ssh gendesign "docker exec forgejo grep -A5 '\[cron.archive_cleanup\]' /home/gendesign/forgejo/data/forgejo/gitea/conf/app.ini" ``` ## Test plan - [x] `python -c "import yaml; yaml.safe_load(...)"` -- YAML parses - [x] Dry-run of `environment-to-ini` inside the container confirmed the `_0x2E_` escape produces the correct `[cron.archive_cleanup]` section (scratch path, not prod config) - [ ] Manual: apply on host per instructions above, confirm `app.ini` picks up new sections, watch `/data/gitea/repo-archive` size and `actions_log` dir over the next 24h 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG
lekss361 added 1 commit 2026-09-16 19:35:33 +00:00
chore(forgejo): track compose file with retention/cleanup settings
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m38s
CI / backend-tests (pull_request) Successful in 6m32s
635ad2df35
app.ini on prod has no [cron.archive_cleanup] section and no retention
keys under [actions] — everything ran on Forgejo defaults (archive
cache swept once/24h, no cap on Actions log/artifact age). An external
crawler hitting per-commit archive URLs grew repo-archive cache to
~48GB/145GB before the daily sweep caught up; Caddy now blocks that
path (#3534), but this is the second line of defense if that rule is
ever lifted.

Forgejo isn't part of the automated deploy pipeline and its compose
file only existed on the host (not a git checkout), so there was
nowhere for this config to survive a redeploy or even be reviewed.
Adds a tracked copy at ops/forgejo/docker-compose.yml, alongside the
existing ops/backup.sh and ops/restore.sh host scripts, with:

- [cron.archive_cleanup]: ENABLED/RUN_AT_START=true, SCHEDULE=@every
  1h, OLDER_THAN=1h — hourly sweep instead of daily, 1h grace window.
- [actions] LOG_RETENTION_DAYS=30, ARTIFACT_RETENTION_DAYS=14 — checked
  against .forgejo/workflows and .github/workflows first: no workflow
  uploads/downloads artifacts today, so nothing to break.

Key names verified against the running image (Forgejo 10.0.3) by
extracting Go struct ini-tags from the binary and dry-running
environment-to-ini in a container scratch dir — not from memory, since
a wrong key is silently dropped.

Not applied to prod. Needs manual scp + `up -d --force-recreate
forgejo` per the file's header comment — see PR description.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG
bot-backend merged commit d633c0d1c3 into main 2026-09-17 12:54:01 +00:00
bot-backend deleted branch chore/forgejo-retention-settings 2026-09-17 12:54:01 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3543
No description provided.