Git Maintenance
Every git-enabled container (a bundle, project, or workspace whose resources are synced to a git repository — see Git Integration) keeps a bare git repository on the workspace host. As you edit resources, git-sync writes new commits into that bare repo. Over time the repository accumulates loose objects — the small, individually-stored, not-yet-packed objects git creates on every write — and grows on disk. A large, never-packed bare repository (the CI/CD or content-heavy bundle of an active workspace can reach tens of gigabytes) is a common cause of host disk pressure, which can eventually block deploys and upgrades.
Git maintenance runs the standard git housekeeping commands
(git prune, git gc/repack) on a bare repository to reclaim that space — safely,
while the workspace keeps running. You do not have to stop the workspace or take
the container offline.
This reclaims space inside the git bare repositories used by git-sync. To analyse
and reclaim space in the workspace database (bloat, VACUUM FULL, largest tables
and files), see Storage Analysis instead.
Why it is safe to run online
Git-sync writes to the same bare repository live from inside the workspace. Running
a plain git gc at the same time as a concurrent push can corrupt the repository, so
maintenance cannot simply shell out to git. Instead it enforces a small set of
load-bearing safety invariants:
- Quiesce. Maintenance acquires the container's git-sync lock before it touches the bare repo. Every workspace process shares this lock, so while maintenance holds it, all git-sync activity on that container pauses — no push can land mid-maintenance.
- Heartbeat. A long
gccan run for many minutes. Maintenance refreshes the lock periodically so peer processes never mistake it for a stale/abandoned lock and reap it out from under a runninggc. The lock is always released cleanly at the end, even if the run is cancelled. - Integrity check.
git fsck --fullruns before maintenance (it aborts if the repository is already broken) and again after (to confirm integrity). The tiny ref files are snapshotted first as a cheap safety net. - Only unreachable objects are removed. Pruning removes only objects that are no longer reachable from any ref. Your reachable history is never touched.
- Deprioritised I/O. The heavy
gc/prune git process runs at idle I/O priority and lowest CPU priority (ionice/nice) so it does not starve the co-located workspace.
Automatic background maintenance
A workspace runs git maintenance automatically in the background, so bundle bare repositories are kept from silently accumulating loose objects without anyone having to trigger a run by hand. The routine runs on the primary workspace process only. On each pass it walks the git-synced containers and, for each one whose loose-object count is worth reclaiming, quiesces that container's git-sync (using the same lock and the same safety invariants described above) and reclaims space.
By default the background routine performs the lightweight prune-only reclaim
(git prune), which drops already-unreachable loose objects without a repack and is safe
to run online at any time. The heavier full git gc repack is opt-in
(GIT_MAINTENANCE_DO_GC); when enabled it is confined to a configurable quiet window
so its disk-I/O spike lands at a low-traffic time of day.
The background routine and the on-demand REST endpoint are independent: disabling the routine leaves on-demand maintenance available, and vice versa.
Configuration
The routine is tuned through workspace configuration keys in the Git integration category. The defaults are safe for an unattended workspace — the routine reclaims loose objects daily with negligible host impact, and never runs a heavy repack unless you opt in.
| Key | Default | Effect |
|---|---|---|
GIT_MAINTENANCE_ENABLED | true | Master switch for the background routine. Set to false to disable it entirely; on-demand maintenance via the REST endpoint / UI action is unaffected. |
GIT_MAINTENANCE_INTERVAL_MINUTES | 1440 (daily) | How long to wait between background passes over the bundle bare repositories. A pass also runs promptly whenever any GIT_MAINTENANCE_* value changes. |
GIT_MAINTENANCE_DO_GC | false | When false, each pass runs a lightweight git prune only (safe online, negligible impact). When true, it runs a full memory-bounded git gc --prune=now repack, confined to the quiet window below. |
GIT_MAINTENANCE_LOOSE_OBJECT_THRESHOLD | 10000 | Skip a repository whose loose-object count (per git count-objects) is below this value, to avoid pointlessly quiescing repositories with little to reclaim. Set to 0 to always run regardless of count. |
GIT_MAINTENANCE_QUIET_WINDOW_START_HOUR | 0 | Start hour (UTC, 0–23) of the window during which a full git gc (GIT_MAINTENANCE_DO_GC=true) is allowed to run. The lightweight prune-only reclaim is always allowed regardless of this window. |
GIT_MAINTENANCE_QUIET_WINDOW_END_HOUR | 0 | End hour (UTC, 0–23, exclusive) of the quiet window. When start and end are equal there is no restriction; when end is less than start the window wraps past midnight. |
Keep GIT_MAINTENANCE_DO_GC=false for most workspaces — the daily prune-only pass is
enough to hold disk usage down on a busy bundle. Enable it only when a repository needs
periodic deep compaction, and set GIT_MAINTENANCE_QUIET_WINDOW_START_HOUR /
_END_HOUR to a low-traffic window (for example 2–4 UTC) so the repack's disk-I/O
spike lands when no integration/CI suite or other heavy workload is running.
Access
Maintenance is exposed as two REST endpoints on the git container. They require a Cloudomation identity (your user, or an API key / webhook identity) with permission on the container:
| Endpoint | Purpose | Required permission |
|---|---|---|
POST /api/latest/{kind}/{id}/git/maintenance | Start a maintenance run | update on the container |
GET /api/latest/{kind}/{id}/git/maintenance | Read the status of the last/current run | read on the container |
{kind}is one ofbundle,project, orworkspace.{id}is the record id of the container (aby=namequery parameter is also supported to address it by name).
Because maintenance mutates the on-disk repository, the POST is gated on update
permission (the same permission as committing/pushing), while reading status only needs
read. See Role Based Access Control for how permissions are assigned.
From the Console
On a git-enabled bundle, the Git maintenance action appears in the bundle's action bar. It offers the same two safe reclaim modes as the REST endpoint, without writing any code:
- Git maintenance (the button itself) runs a full
git gc/repack with prune. Because this is the heavier operation, it opens a confirmation dialog first, which states that the run can take several minutes and briefly pauses git-sync writes to the bundle while it works, and that it is safe and non-destructive — committed history and records are untouched. - Prune loose objects only (light) (in the button's dropdown menu) runs the cheap
online
prune_onlyreclaim directly, with no confirmation step.
A notification reports when the run starts and again when it finishes, including the space
reclaimed. The action appears only on git-enabled bundles, and is disabled while another
git-sync write on the bundle holds the lock. Starting a run requires update permission
on the bundle, the same as the REST POST.
Modes
The POST body is a JSON object; every field is an optional boolean. The mode is
chosen by precedence: dry_run > prune_only > do_gc.
| Field | Effect |
|---|---|
dry_run | Acquire the lock + heartbeat + run fsck only — no objects are removed. Validates the quiesce/integrity path with zero risk. |
prune_only | Run git prune --expire=now: remove unreachable loose objects without a repack. No large pack write, no sustained disk-I/O spike — safe to run online at any time. This is the recommended routine reclaim. |
do_gc | Run a (memory-bounded) git gc — a full repack. Add prune: true to pass --prune=now. This writes a new pack and causes a disk-I/O spike; run it only in a quiet maintenance window. |
If you POST an empty body {}, do_gc defaults to true and a full gc/repack
runs immediately. To avoid an unintended heavy operation, always send the flags you
want explicitly — e.g. { "dry_run": true } or { "prune_only": true }.
The run is detached: a full gc can outlive the request, so the POST returns
immediately (with the initial status) and the work continues in the background. Poll the
GET endpoint to follow progress. If a run is already in flight, the POST returns the
current status with "already_running": true instead of starting a second run.
Status
The GET endpoint returns the current status as JSON (or { "state": "NONE" } if the
container has never been maintained). Key fields:
state— one ofNONE,STARTING,LOCKING,RUNNING,DONE,ERROR.phase— whileRUNNING:snapshot_refs,fsck_pre,prune/gc,fsck_post,done.size_before_bytes,size_after_bytes,reclaimed_bytes— repository size and space reclaimed.dry_run,do_gc,prune,prune_only— the effective mode of this run.error—nullunlessstateisERROR.requested_by,container,started_at,updated_at,finished_at— bookkeeping.
Recommended procedure
- Check status.
GETthe endpoint to see whether a run is already in flight and how large the repository is. - Validate first (optional).
POST { "dry_run": true }. This exercises the lock, heartbeat, andfsckpath without removing anything, and confirms maintenance can quiesce the container cleanly. - Routine reclaim.
POST { "prune_only": true }. This is safe to run online and reclaims whatever loose objects are already unreachable, with negligible impact. Prefer a quieter moment, but it does not require a maintenance window. - Full compaction (occasionally).
POST { "do_gc": true, "prune": true }to fully repack and prune. Because this causes a disk-I/O spike, run it in a quiet window — when no integration/CI suite or other heavy workload is running — and make sure the host has enough free disk for the new pack.
Poll GET between steps until state is DONE (or ERROR).
When to run
Run maintenance when a container's bare repository has grown large or accumulated many
loose objects — typically the busiest, most-frequently-synced containers (large content
bundles, CI/CD bundles). prune_only is cheap enough to run regularly; reserve the full
do_gc repack for occasional deep compaction in a quiet window.
Example: trigger maintenance from a flow
You can automate maintenance with a Cloudomation flow using the REST
connector against your own workspace. The
example below starts a prune_only run on a bundle and then polls until it finishes.
Replace host, the bundle record_id, and the API key with your own.
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution, inputs: dict):
host = 'your-workspace.cloudomation.io'
kind = 'bundle' # 'bundle' | 'project' | 'workspace'
record_id = inputs['record_id']
api_key = inputs['api_key'] # an API key with 'update' permission on the container
auth = {'Authorization': f'Bearer {api_key}'}
route = f'/{kind}/{record_id}/git/maintenance'
# Start a safe, online prune-only reclaim.
this.connect(
connector_type='REST',
name='start git maintenance',
schema_version='10.0',
scheme='https',
host=host,
base_path='/api/latest',
path=route,
headers=auth,
mode={
'mode_name': 'post',
'body': {'body_mode': 'json', 'json': {'prune_only': True}},
},
expected_status_code=[200, 202],
)
# Poll the status endpoint until the run reaches a terminal state.
while True:
this.sleep(15, name='wait between polls')
status = this.connect(
connector_type='REST',
name='poll git maintenance',
schema_version='10.0',
scheme='https',
host=host,
base_path='/api/latest',
path=route,
headers=auth,
mode={'mode_name': 'get'},
).get('output_value')['json']
if status.get('state') in ('DONE', 'ERROR'):
break
if status.get('state') == 'ERROR':
return this.error(f"git maintenance failed: {status.get('error')}")
return this.success(
f"reclaimed {status.get('reclaimed_bytes')} bytes "
f"({status.get('size_before_bytes')} -> {status.get('size_after_bytes')})"
)
Start with {'dry_run': True} the first time you automate maintenance for a container,
to confirm the quiesce and integrity path succeed, before switching to prune_only (or
an occasional do_gc) for real reclaim.