diff --git a/scripts/cloudron-memlimits.sh b/scripts/cloudron-memlimits.sh new file mode 100755 index 0000000..f778361 --- /dev/null +++ b/scripts/cloudron-memlimits.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# cloudron-memlimits.sh - right-size app memory limits [#685] +# Reads CLOUDRON_URL + CLOUDRON_TOKEN from ~/.creds/cloudron.env +# Usage: cloudron-memlimits.sh [canary|apply|plan] +# plan (default) show current vs proposed, change nothing +# canary apply axiosheartstudios.com only, then verify +# apply apply all proposals +set -eu + +ENV_FILE="$HOME/.creds/cloudron.env" +[ -f "$ENV_FILE" ] || { echo "missing $ENV_FILE"; exit 1; } +. "$ENV_FILE" +: "${CLOUDRON_URL:?}" "${CLOUDRON_TOKEN:?}" + +MODE="${1:-plan}" + +# fqdn:new-limit-MB (from #685 census 2026-09-02, steady-state based) +PROPOSALS=" +axiosheartstudios.com:512 +community.turnsys.com:4096 +nextcloud.knownelement.com:2048 +bookmarks.knownelement.com:2048 +photos.knownelement.com:3072 +share.knownelement.com:512 +hfnfc.net:512 +notes.knownelement.com:1024 +office-onlyoffice.knownelement.com:1536 +paperless.knownelement.com:2048 +file-transfer.knownelement.com:512 +digital-assets-manager.knownelement.com:512 +pwvault.turnsys.com:512 +finance.turnsys.com:512 +finance.reachableceo.com:512 +cmdb.knownelement.com:512 +" + +api() { + curl -s -m 30 -H "Authorization: Bearer $CLOUDRON_TOKEN" "$@" +} + +APPS=$(api "$CLOUDRON_URL/api/v1/apps?per_page=200") +echo "$APPS" | jq -e '.apps' >/dev/null 2>&1 || { echo "API auth/list failed"; exit 1; } + +reclaimed=0 +for line in $PROPOSALS; do + fqdn=${line%%:*}; newmb=${line##*:} + match=$(echo "$APPS" | jq -r --arg f "https://$fqdn" \ + '.apps[] | select(.fqdn==$f or (.domain==$f and (.location=="" or .location=="@"))) | "\(.id) \(.memoryLimit)"' | head -1) + [ -z "$match" ] && { echo "SKIP $fqdn (not an app)"; continue; } + appid=${match%% *}; curmb=$(( ${match##* } / 1048576 )) + [ "$curmb" -le "$newmb" ] && { echo "SKIP $fqdn (already $curmbMB <= $newmb)"; continue; } + + case "$MODE" in + plan) + echo "PLAN $fqdn: ${curmb}MB -> ${newmb}MB (reclaim $((curmb - newmb))MB)" + ;; + canary) + [ "$fqdn" != "axiosheartstudios.com" ] && continue + echo "CANARY $fqdn: ${curmb}MB -> ${newmb}MB" + api -X POST "$CLOUDRON_URL/api/v1/apps/$appid" -H 'Content-Type: application/json' \ + -d "{\"memoryLimit\": $((newmb * 1048576))}" | jq -r '.status // .message // .' + ;; + apply) + echo "APPLY $fqdn: ${curmb}MB -> ${newmb}MB" + api -X POST "$CLOUDRON_URL/api/v1/apps/$appid" -H 'Content-Type: application/json' \ + -d "{\"memoryLimit\": $((newmb * 1048576))}" | jq -r '.status // .message // .' + sleep 8 + ;; + esac + reclaimed=$((reclaimed + curmb - newmb)) +done + +echo "---" +echo "mode=$MODE total-limit-reclaim=${reclaimed}MB"