#13586·appwrite

Bug Report: VCS deployment sourceSize reports the whole cloned repo (.git included), inflating totalSize and deployments-storage metrics

Author: AlimFreightCreated Sep 9, 2026Updated Sep 10, 2026
Labelsproduct / storageproduct / databasesproduct / functionsproduct / self-hostedproduct / messagingproduct / vcs

Reproduction steps

  1. Self-hosted Appwrite 2.0.0, PostgreSQL. Any Git-connected function whose providerRootDirectory is a subfolder of a repository (a monorepo makes this obvious, but any repo shows it).
  2. Push a commit and let the VCS build run.
  3. Look at the deployment in the Console, or GET /v1/functions/{id}/deployments.

Observed on a repo with 19 Git-connected functions, each with its own providerRootDirectory:

function                root dir                                   sourceSize  buildSize
contact-email           appwrite-functions/contact-email             4.33 MB     2.49 MB
document-parser         appwrite-functions/document-parser           4.33 MB     2.43 MB
motive-sync             appwrite-functions/motive-sync               4.33 MB     2.94 MB
ifta-report             appwrite-functions/ifta-report               4.33 MB     3.01 MB
arrival-nudger          appwrite-functions/arrival-nudger            4.33 MB     2.89 MB
… all 19 …                                                          4.33 MB     (varies)

sourceSize is byte-identical across every function, regardless of root directory or how much code the function contains. It equals the size of the whole repository: git ls-files -z | xargs -0 tar czf - on the same commit is 4.46 MB.

The same function deployed manually (a tarball of just that folder) reports sourceSize 0.07 MB, and produces a functionally identical build — 2.86 MB vs 2.89 MB.

Expected behavior

sourceSize should describe that deployment's source — the content under providerRootDirectory that was actually built — as it did in 1.9.x, and as the manual-upload path still does.

Actual behavior

For VCS deployments, sourceSize is the size of the temporary clone directory, which is dominated by .git. git fetch --depth=1 downloads every blob of the commit's tree into the object store; core.sparseCheckout only limits which files are written to the working tree. So the number reflects the repository, not the deployment, and is the same for every function built from that repo.

Two consequences:

1. The Console's total is inflated. totalSize = sourceSize + buildSize, so a deployment showing 7.22 MB is really a 2.89 MB artifact plus 4.33 MB of a clone that was deleted seconds later. Users reasonably read this as their function having doubled in size. It also makes the Git and manual paths incomparable, which is what led us here.

2. It is counted as stored bytes when nothing was stored. sourcePath is NULL for every VCS deployment — no source archive is kept. Yet StatsResources.php reports:

['metric' => METRIC_DEPLOYMENTS_STORAGE, 'value' => (int) $dbForProject->sum('deployments', 'sourceSize'), …]

On this instance that is 525 MB of "deployments storage" that does not exist on disk (127 VCS deployments × ~4.2 MB), against 1.9 MB genuinely stored by the 21 manual deployments. On Cloud, this metric would over-report — and if it informs plan limits or billing, over-charge — by roughly the repository size on every Git build.

Builds themselves are correct: sparse checkout works, the right folder is built, and the artifact is right. This is purely the reported size.

This is a 1.9 → 2.0 regression, with before/after from the same instance

Our previous host still had its pre-upgrade database. Same repository, same functions, same providerRootDirectory values:

period version VCS sourceSize source stored (sourcePath set)
Aug 2026 1.9.x 0.09 MB avg (min 0.01, max 0.52) 19 of 21
Sep 2026 2.0.0-rc.2 4.19 MB 0 of 1

Per-function on 1.9: dashboard_read 0.08 MB, invoice_admin 0.08 MB, ifta_report 0.10 MB — all Git builds, all in the same range as a manual tarball (0.06 MB).

So 1.9 packaged the sparse checkout as a source archive, stored it, and reported that. 2.0 stopped storing a source archive for VCS builds and began reporting the raw clone instead.

Where it comes from

src/Appwrite/Platform/Modules/Functions/Workers/Jobs.phpsourceSize arrives as a reported artifact and is written straight through:

// A stat artifact reports the file's byte size as its 'content'.
$size = (int) ($data['content'] ?? 0);
…
'sourceSize' => $size,
'totalSize'  => $size + (int) $deployment->getAttribute('buildSize', 0),

The clone that gets measured is built by generateCloneCommand in utopia-php/vcs (GitHub.php), which sets core.sparseCheckout and then git pull --depth=1 / git fetch --depth=1 — worktree limited, object store not.

Suggested fixes, cheapest first:

  • Exclude .git when measuring (the same rsync -av --exclude '.git' the template path in Builds.php already uses), so the number reflects the checked-out source.
  • Or measure only providerRootDirectory.
  • Or, if nothing is stored for VCS builds, report sourceSize as 0 and keep it out of METRIC_DEPLOYMENTS_STORAGE, so the storage metric counts only bytes that exist.

Your Environment

  • Appwrite 2.0.0 self-hosted (Docker Compose), PostgreSQL 18.3 (appwrite/postgres:0.1.0)
  • Executor openruntimes/executor:0.29.0, orchestrator ghcr.io/open-runtimes/orchestrator:1.9.2
  • Go functions (go-1.26), monorepo, one providerRootDirectory per function
  • Previous host for the 1.9 comparison: same repo, Appwrite 1.9.x on MariaDB 10.11

Additional information

Happy to supply the full per-deployment table, or to test a patch. Related report from the same instance: #13485.