把 Vercel 部署儲存空間從 13.96 GB 降到 20.27 MB — 關閉預覽、縮短保留期

Vercel Hobby 方案的部署儲存空間超過了 10 GB 上限。原因是由部署次數造成的:每次部署都會增加 147 MB,三十天內部署了 189 次。

黑色卡片上的白色 Vercel 三角形標誌與字樣
本頁目錄

引言

Vercel 的用量頁面在 Deployment Storage 旁邊顯示了一個紅圈:免費額度 10 GB,用掉 13.96 GB。同一個帳戶裡,其他項目沒有一項超過上限的 20%。

標題為「Exceeded free resources」的 Vercel 用量面板,Deployment Storage 為 10 GB 中的 13.96 GB 並標上紅圈,Functions Storage 為 10 GB 中的 2 GB,Edge Requests 為 1M 中的 45K,Speed Insights Events 為 10K 中的 267

我第一個想知道的是線上網站會不會受影響。答案是不會。不過一直超過上限還是有代價的:Vercel 會不再遵守 30 天的保留期,會直接刪掉符合條件的部署,也有可能在回到額度以內之前拒絕新的部署。

我一開始懷疑的是 repo,畢竟這裡每篇文章都帶著圖表和截圖。然而 repo 打包後只有 9.79 MiB,所有影像和影片加起來也才 9.4 MB。這個網站部署一次用掉的空間是 147 MB,而三十天內共部署了 189 次。

關掉預覽建置、把保留期縮到一天,並且在發布流程的開頭加上清理處理,最後成功讓 Deployment Storage 變成了 20.27 MB。

讀懂配額:一次建置 147 MB,repo 9.79 MiB

Deployment Storage 量的是每次部署所保留的建置產物,在這個網站上大約是 repo 本身的 15 倍大。

兩個數字各用一行指令就能拿到。

終端機視窗
輸入
git count-objects -vH | grep size-pack
輸出
size-pack: 9.79 MiB
終端機視窗
輸入
du -sm .vercel/output
輸出
147 .vercel/output

配額算的是後者。.vercel/output 是 vercel build 產生、vercel deploy --prebuilt 上傳的東西。部署端的檔案樹有 2847 個檔案,本機是 2843 個,傳輸過程中沒有多出什麼實質內容。

147 MB 分布在哪裡

每次部署有一半是伺服器 bundle,而這一半裡面有 20 MB 是它自己的第二份複本。

這個網站一次建置的目錄組成五條依比例縮放的橫條,由上而下依序是:functions/_render.func 的 54 MB、_functions 的 20 MB、static/_astro 的 33 MB、三種語系 HTML 目錄合計 31 MB,以及 static/pagefind 的 3.7 MB。分隔線下方是一條短得多的橫條,代表打包後 9.79 MiB 的 repo。一次部署 147 MB,其中最大的五個目錄functions/_render.funcAstro 伺服器 bundle,其中 29 MB 是 node_modules54 MB_functions/同一份伺服器建置的第二份複本20 MBstatic/_astro產生出來的 893 個影像衍生檔33 MBstatic/ja + zh-tw + blog三種語系的 HTML31 MBstatic/pagefind搜尋索引3.7 MBgit count-objects產生這一切的儲存庫(打包後)9.79 MiB
這個網站一次建置的目錄組成。伺服器 bundle 出現了兩次,static/_astro 則是三種語系共 893 個影像衍生檔。

_render.func 裡面有 29 MB 的 node_modules,也就是 Astro 的伺服器執行環境,加上文章頁在算繪時會引入的一切。static/_astro 是 Astro 寫出處理後影像的位置,那 893 個檔案,是同一批原始圖片在三種語系下產生的衍生檔。

把影像搬到外部為什麼只能省下 6%

這個 repo 裡所有影像和影片加起來是 9.4 MB,所以就算全部搬去物件儲存的 bucket,能省下的空間也只有一次部署的十六分之一。

終端機視窗
輸入
find src -type f \( -name '*.mp4' -o -name '*.png' -o -name '*.jpg' -o -name '*.webp' -o -name '*.avif' \) -exec du -ch {} + | tail -1
輸出
9.4M total

配額算的是 static/_astro,那些衍生檔是建置時從原始圖片產生的。把原圖改由 bucket 提供,只是把 9.4 MB 移出 git,33 MB 的衍生檔原封不動留在部署包裹裡。

就算我能夠把 147 MB 砍成一半,佔用 Deployment Storage 的空間還是會隨著部署次數一路疊加上去,因此管理單次部署的大小並非解決問題的關鍵。

三十天內 189 次部署

儲存空間等於建置大小乘以部署次數,而我之前從來沒去關注過部署次數這一項。

建置大小乘以部署次數,對上 Hobby 方案的上限兩個方框相乘:每次建置 147 MB,三十天內 189 次部署,其中 122 次是預覽、67 次是正式部署。乘積為 27.8 GB。下方有兩條同尺度的橫條,比較 27.8 GB 與 Hobby 方案的 10 GB 上限,超出將近三倍。把配額填滿的是什麼147 MB每次建置vercel deploy 上傳的內容×189三十天內的部署數預覽 122 次、正式 67 次=27.8 GB一個月消耗掉的量實際產生的大小27.8 GBHobby 方案上限10 GB10 GB
把配額填滿的乘積。看得見的槓桿是 147 MB,真正在起作用的是 189。

GitHub 會為每次部署留下紀錄,所以次數是從 repo 而不是從 Vercel 取得的。

終端機視窗
gh api "repos/<owner>/<repo>/deployments?per_page=100" --paginate

把最近三十天的紀錄依建立者分組,vercel[bot] 對 Preview 環境建立了 122 次,這個 repo 自己的 CI 對 production 建立了 67 次。

以每次 147 MB 計算,189 次部署等於一個月內往 10 GB 的額度裡灌進 27.8 GB。

沒人打開過的 122 個預覽

這些部署有三分之二是早已合併並刪除的分支所留下的預覽,而且每一個都是自動建立的。

留下來的四個部署,每一個都是某個分支的最新預覽。那些分支都已經在 squash 合併後被刪掉,git ls-remote --heads origin 一個也列不出來。

即使這些分支早已被刪除,Vercel 仍然保留了這些分支的最新預覽,而並沒有察覺這些分支已經不存在了。

關掉預覽、縮短保留期,再清理一次

三個設定,分別對著乘積裡的一項:部署被建立的頻率、被保留的時間,以及此刻還擺在那裡的數量。

關掉 Git 整合

Vercel 的 Git 整合預設會替每一次 push 都會建立一個部署,不分分支。因此每個部署都佔用 147 MB 的 Deployment Storage。

這個網站不需要那些自動部署:正式環境是由 GitHub Actions 部署的,其他分支的部署也都不需要。

vercel.json
{
"git": {
"deploymentEnabled": false
}
}

這會停掉包含 main 以及所有其他分支的 Git 整合所自動產生的部署。

保留期是 Project Settings 裡的四個下拉選單

保留期設定在 Vercel 的控制台,位於 Project Settings 的 Build and Deployment 底下,名稱是 Deployment Retention Policy。這個帳戶裡的每個專案都停在方案的預設值。

部署類型變更前變更後
Canceled30 天1 天
Errored30 天1 天
Pre-Production30 天1 天
Production30 天1 天

30 天是 Hobby 方案的上限,所以預設值同時也是最慢的那個設定。四項全部改成最短的值。

Vercel 的 Deployment Retention Policy 面板,Canceled Deployments、Errored Deployments、Pre-Production Deployments、Production Deployments 四個下拉選單都顯示「1 day」

負責預覽的是 Pre-Production Deployments 那一列。這裡沒有設定要保留幾個部署的欄位。

在 Hobby 方案上,vercel usage 什麼都報不出來。

終端機視窗
輸入
pnpm dlx vercel@58 usage
輸出
Error: Billing cost data is unavailable for 2026-09-01 through 2026-09-21.

Hobby 方案沒有帳務紀錄,CLI 也就無從讀起。GB 這個數字只存在於控制台。

--safe 保住什麼,又刪掉什麼

這裡我們使用 vercel remove <project> --safe,會指定 --safe 是因為如果不加的話會刪掉整個專案。

終端機視窗
輸入
pnpm dlx vercel@58 remove oharu-tech-blog --safe --yes
輸出
> Found 8 deployments for removal in oharu121s-projects [1s]
> Success! Removed 8 deployments [909ms]

然而 --safe 會跳過持有別名的部署,因此有四個持有分支別名的預覽也一起被留了下來。

整理這個專案需要兩次三欄方塊。執行前共 13 次部署:1 個線上正式部署、4 個仍持有分支別名的預覽、8 個沒有別名。執行 vercel remove --safe 後,8 個沒有別名的被刪除,剩下 5 個,因為 --safe 會保留所有持有別名的部署。接著以 URL 指定刪除後,只剩下線上的正式部署。分成兩次的原因:--safe 保留的不只有正式部署執行前13 次部署vercel remove --safe刪除 8 個沒有別名的指定 URL 刪除刪除 4 個孤立的預覽→→線上正式部署,持有網域別名持有分支別名的預覽,分支已刪除沒有別名
--safe 刪掉的八個,和它放過的四個。預覽的分支別名活得比它指向的分支還久,所以第二次要逐一指名部署。

十三個部署刪掉八個,剩下五個:線上那個正式部署,加上四個分支早就沒了的預覽。確認過分支真的不在之後,這四個只能用 URL 逐一指名刪除。

終端機視窗
輸入
pnpm dlx vercel@58 remove <url> <url> <url> <url> --yes
輸出
> Found 4 deployments for removal in oharu121s-projects [934ms]
> Success! Removed 4 deployments [731ms]

清理該放在發布流程的哪裡

清理要放在發布流程的開頭。放在結尾的話,正式環境才剛換上新版本,舊的那個部署就被刪掉了,想回滾也沒得回。

在部署前整理會保留回滾目標,在部署後整理則會刪掉它同一次發布的兩條時間軸。第一條把整理放在檢查與建置、部署之前,前一次的正式部署一直保留到部署後可能需要回滾的區間。第二條把整理放在部署之後,同一個部署正好在該區間開始的瞬間被刪除。同一次發布,整理放在前面與放在後面可能需要回滾的時間區間先整理整理檢查與建置部署前一次正式部署仍然存在後整理檢查與建置部署整理前一次正式部署已刪除
在部署前清理,前一次的正式部署會在最可能需要回滾的那段區間裡一直存在。在部署後清理,它正好在那個時候被刪掉。

從 13.96 GB 到 20.27 MB

Vercel Deployment Storage 三十天總量的折線圖,從不到 7 GB 開始,在 9 月 8 日前後升到接近 17 GB,到 9 月 21 日回落至 14 GB,接著在 9 月 22 日垂直掉到總計 20.27 MB

右側那道垂直的下墜就是清理。在那之前的斜率,是三十天內累積的 189 次部署。

我做了三件事讓 Deployment Storage 從 13.96 GB 降到了 20.27 MB:關掉 Git 整合的自動部署、把四種保留期都改成一天,再用 vercel remove 把堆積的部署清掉。

總結

部署儲存空間等於建置大小乘以部署次數,而比較容易判斷錯的是次數。這個帳戶有十個專案、185 個存活的部署、147 MB 的建置,而 repo 小到對總量沒有影響。

要分辨這是儲存空間的問題還是 repo 的問題,有三個檢查。du -sm .vercel/output 給出一次部署的大小,平台自己的部署紀錄給出次數,兩者的乘積對上額度,就能決定應該要把部署變小,還是要減少部署次數。

如果問題出在次數,我們能做三件事:關閉 Vercel 的自動部署: git.deploymentEnabled: false、縮短舊部署的保留時間,以及手動刪除已經不需要的舊部署: vercel remove --safe。能立竿見影的方法只有最後一個,而且我們應該把它放在整個發布流程的開頭,而不是結尾。

參考連結

分享這篇文章