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.
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.
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.