Minnetak på et nettstedsbygg på en server med 1,7 GB
Dette nettstedet bygger seg selv på en server med én CPU og 1,7 GB minne. Uten tak toppet bygget på 677–689 MiB; et systemd-tak på 450 MiB kostet ingenting målbart. Hva de to minnetallene betyr, og hva som ikke virket.
Dette nettstedet rulles ut med en push. En Gitea-webhook skriver en triggerfil, en systemd path-enhet plukker den opp, og
en oneshot-tjeneste kjører npm ci, astro build inn i en fersk releases/<sha>-<tid>/-katalog og bytter
dist-symlenken med én rename. Et bygg som feiler, lar den kjørende utgaven stå. Serveren er den samme som kjører
e-posten, wikien og to produkter: én CPU, 1707 MiB minne, og rundt 1 GB av det i swap en vanlig dag.
To tall, ikke ett
Utrullingsskriptet logger to minnetall per kjøring. Nedre grense er maks RSS for den største
prosessen, astro build, fra /usr/bin/time. Øvre grense er hele jobbens cgroup memory.peak, som teller alle
prosessene i enheten og sidebufferet bygget tar i bruk. På de to kjøringene uten tak 2026-10-13 toppet astro-prosessen på
296,6 og 284,3 MiB; cgroupen på 676,8 og 688,8 MiB. Gapet er npm ci og buffer, og cgroup-tallet er det kjernen vil
handle på.
Taket
Enheten har nå MemoryHigh=450M, MemoryMax=700M, Nice=10 og best-effort IO med prioritet 7. Over
MemoryHigh gjenvinner og struper kjernen jobben; over MemoryMax dreper den jobben, og utrullingsskriptet fjerner da
den halvbygde utgaven og lar dist peke der den pekte. 450 MiB er med vilje under det bygget brukte uten tak: det meste
av toppen var sidebuffer, som er det første gjenvinningen tar.
Hva det kostet: ingenting målbart
Hver utrulling med tak har fullført — tolv av tolv per 13:43 den 2026-10-13. Hver kjørings cgroup-topp
ligger på 472,1–472,3 MB, altså 450 MiB-linjen, som ventet. Selve astro-prosessen toppet på 260–272 MiB per kjøring, og
bygget tok 20,1–29,9 sekunder mot 22,7 og 25,8 sekunder uten tak. Hele utrullingen, fra trigger til symlenke, tar omtrent
ett minutt. På en maskin med én CPU betyr Nice=10 mer enn minnelinjen for den som bruker e-postserveren under en push.
To ting som ikke virket
MemoryDenyWriteExecute=yes, den vanlige herdingslinjen, krasjer Node: JIT-en trenger sider
som er skrivbare og deretter kjørbare. Enheten sier det i en kommentar, så nestemann ikke legger den inn igjen. Og
systemds egen MemoryPeak= på en oneshot-tjeneste er ikke et tall per kjøring: den meldte samme 716,8 M på hver kjøring
uten tak og leses som usatt etterpå. Skriptet leser memory.peak fra cgroupen selv og skriver det til utrullingsloggen,
der det kan sammenlignes kjøring for kjøring.
Åpent
npm ci kjører ved hver utrulling og er den tyngste fasen; en kollegas avlesning av top under to pusher
satte den til omtrent 425 MB resident. Å hoppe over den når låsfilen er uendret, ville fjernet fasen fra de fleste
utrullingene. Ikke gjort ennå; det er fem linjer og en avgjørelse.