2 Commits
Author SHA1 Message Date
Igor K.andClaude Sonnet 5 b86863e6f0 fix: give the vector log collector a 512 MiB memory limit
- vector reads the log files of every pod on the node, so its memory need follows the node's log volume. At
  256 MiB it was killed for running out of memory 122 times on instance 1 and used 89% on instance 2.
  The limit is now 512 MiB (request unchanged at 128 MiB) for new instances; the total limit per instance
  goes from 5.625 GiB to 5.875 GiB, requests stay at 1.875 GiB.
- README memory table and the test for the totals updated
- version 1.1.1 -> 1.1.2

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 12:56:35 +03:00
Igor K.andClaude Sonnet 5 45b02a85af fix: installer prints the name once; add CLAUDE.md with the versioning rule
- install.sh said "Installed supaspin supaspin 1.1.0." because --version now includes the name; it now says
  "Installed supaspin 1.1.1."
- CLAUDE.md: tells AI agents to read Igor's global rules first, and states the versioning rule (number in VERSION,
  code and README together, semantic versioning, major bumps only with Igor's yes, a git tag vX.Y.Z after every merge)
- version 1.1.0 -> 1.1.1

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 12:27:11 +03:00
6 changed files with 54 additions and 19 deletions
+29
View File
@@ -0,0 +1,29 @@
# supaspin - rules for AI agents
Read and follow the global rules first. They are in Igor's global CLAUDE.md, which in turn pulls in
`~/.agents/AGENTS.md`:
@~/.claude/CLAUDE.md
The rules below are specific to this repository and add to the global ones.
## Versioning
The number is `MAJOR.MINOR.PATCH` (semantic versioning: `1.1.1` is major 1, minor 1, patch 1).
1. **Pick the number.** A bug fix or wording fix raises PATCH (1.1.0 -> 1.1.1). A new feature raises MINOR and resets
PATCH (1.1.1 -> 1.2.0). MAJOR is Igor's decision only: never raise it, never write `!` after the commit type
(`feat!:`) and never write a `BREAKING CHANGE:` line without asking him first (see the global rules).
2. **Write it in all three places, in the same commit as the change.** The file `VERSION`, the `VERSION = "x.y.z"`
line near the top of `supaspin`, and the line `Version x.y.z` at the top of `README.md`. A unit test
(`tests/test_supaspin.py`, class `Version`) fails if they differ. `supaspin --version` prints `supaspin x.y.z`.
3. **One plan = one branch = one commit = one pull request.** The version bump is part of that one commit.
4. **Tag every release.** After the pull request is merged, create the git tag `vX.Y.Z` (for example `v1.1.1`) on
the commit on `main` that contains that number in `VERSION`. The tag name must equal `VERSION` with a `v` in
front. A version without a tag is not released. Never move or delete a tag that was already published; if a
release is wrong, ship a new PATCH instead. With the Gitea API: `POST /api/v1/repos/igor/supaspin/tags` with
`tag_name`, `target` (the commit) and `message`.
5. **Check the result.** Reinstall from the merged `main` (`sudo bash install.sh`), then run `supaspin --version` and
compare it with `VERSION` and the tag.
6. If you find a version in `VERSION` that has no tag, or a tag that does not match `VERSION`, tell Igor before doing
anything else.
+11 -8
View File
@@ -1,6 +1,6 @@
# supaspin
Version 1.1.0
Version 1.1.2
Create, list, show and delete throwaway [Supabase](https://supabase.com) instances on a
single-node [microk8s](https://microk8s.io) server, from a terminal menu.
@@ -17,7 +17,7 @@ endangering them. Nothing in it is specific to any one app.
| Postgres | `<LAN IP>:544N` (instance 1 → 5441, 2 → 5442, …), bound on the LAN address only |
| Studio (admin UI) | `http://<LAN IP>:300N` (3001, 3002, …), not on the public hostname |
| Disk | fixed-size ext4 image files, mounted at boot: **db 2 GB, storage 1 GB, every other volume 256 MB** |
| Memory | limits add up to **5.625 GiB** per instance, requests to 1.875 GiB (table below) |
| Memory | limits add up to **5.875 GiB** per instance, requests to 1.875 GiB (table below) |
| Priority | pods run at priority -100 (`supaspin-low`); when the server runs short of memory they are evicted first and never evict anyone else |
| Secrets | generated fresh per instance: JWT secret, anon and service keys, Postgres password, Studio password, and the internal keys |
| Uploads | stored in the instance's own storage image (no MinIO); deleted with the instance |
@@ -108,7 +108,7 @@ sudo supaspin push-env N TARGET [--port P] [--key FILE] [--dry-run] # see "Cop
sudo supaspin delete N [--yes]
sudo supaspin mail # change the saved default mail settings
sudo supaspin setup # change settings
supaspin --version # prints "supaspin 1.1.0"
supaspin --version # prints "supaspin 1.1.2"
```
### Connecting your app
@@ -215,12 +215,15 @@ run `supaspin delete N` to clean up.
| analytics | 128 MiB | 768 MiB |
| kong | 128 MiB | 512 MiB |
| realtime | 128 MiB | 384 MiB |
| auth, functions, imgproxy, meta, rest, storage, studio, vector | 128 MiB each | 256 MiB each |
| **Total** | **1.875 GiB** | **5.625 GiB** |
| vector | 128 MiB | 512 MiB |
| auth, functions, imgproxy, meta, rest, storage, studio | 128 MiB each | 256 MiB each |
| **Total** | **1.875 GiB** | **5.875 GiB** |
Most limits are half of what a production setup would use. Analytics and realtime are higher: at
half of production they were killed for running out of memory in a loop (measured on a fresh
instance: analytics peaks at 624 MiB, realtime at 280 MiB). Image resizing (imgproxy) reads the
Most limits are half of what a production setup would use. Analytics, realtime and vector are higher:
at half of production they were killed for running out of memory in a loop (measured on a fresh
instance: analytics peaks at 624 MiB, realtime at 280 MiB). Vector is the log collector: it reads the
log files of every pod on the server, not only its own instance's, so its memory need follows the
server's total log volume; at 256 MiB it was killed in a loop on one instance and used 89% on another. Image resizing (imgproxy) reads the
uploads from the storage volume, so it has no image of its own.
## Troubleshooting
+1 -1
View File
@@ -1 +1 @@
1.1.0
1.1.2
+1 -1
View File
@@ -64,6 +64,6 @@ install -m 0644 "$src/templates/values.yaml.tpl" /usr/share/supaspin/templates/v
install -m 0755 "$src/uninstall.sh" /usr/share/supaspin/uninstall.sh
install -d -m 0700 /etc/supaspin /var/lib/supaspin
echo "Installed supaspin $(/usr/local/bin/supaspin --version)."
echo "Installed $(/usr/local/bin/supaspin --version)."
echo "Next: run 'sudo supaspin' - the first run asks for your settings."
echo "To remove it later: sudo bash /usr/share/supaspin/uninstall.sh"
+7 -5
View File
@@ -32,7 +32,7 @@ import tty
import yaml
VERSION = "1.1.0"
VERSION = "1.1.2"
CONFIG_PATH = os.environ.get("SUPASPIN_CONFIG", "/etc/supaspin/config.yaml")
STATE_DIR = os.environ.get("SUPASPIN_STATE", "/var/lib/supaspin")
CHART_REPO = "https://supabase-community.github.io/supabase-kubernetes"
@@ -53,12 +53,14 @@ VOLUMES = {"db": 2048, "storage": 1024, "functions": 256,
DB_OWNED = ("db", "pgsodium")
DB_UID, DB_GID = 100, 101
# Memory per service: (request MiB, limit MiB). Half of maralago.ru production, except analytics
# and realtime: at half of production they were killed for running out of memory in a loop
# (measured on a fresh instance: analytics peaks at 624 MiB, realtime at 280 MiB).
# Memory per service: (request MiB, limit MiB). Half of maralago.ru production, except analytics,
# realtime and vector: at half of production they were killed for running out of memory in a loop
# (measured on a fresh instance: analytics peaks at 624 MiB, realtime at 280 MiB; vector reads the log
# files of every pod on the node, not only its own instance's, and was killed at 256 MiB on 2026-09-19).
SERVICES = ["db", "analytics", "kong", "auth", "functions", "imgproxy", "meta",
"realtime", "rest", "storage", "studio", "vector"]
MEMORY = {"db": (512, 2048), "analytics": (128, 768), "kong": (128, 512), "realtime": (128, 384)}
MEMORY = {"db": (512, 2048), "analytics": (128, 768), "kong": (128, 512), "realtime": (128, 384),
"vector": (128, 512)}
DEFAULT_MEMORY = (128, 256)
DEFAULT_MAIL = {"host": "smtp.example.com", "port": 587, "user": "", "password": "",
+5 -4
View File
@@ -90,13 +90,14 @@ class Values(unittest.TestCase):
def test_memory_caps_add_up(self):
req, lim = sp.total_memory_mib()
# db 2048, analytics 768, kong 512, realtime 384, the other eight 256 each
self.assertEqual(lim, 2048 + 768 + 512 + 384 + 8 * 256)
self.assertEqual(lim / 1024, 5.625)
# db 2048, analytics 768, kong 512, realtime 384, vector 512, the other seven 256 each
self.assertEqual(lim, 2048 + 768 + 512 + 384 + 512 + 7 * 256)
self.assertEqual(lim / 1024, 5.875)
self.assertEqual(req, 512 + 11 * 128) # 1.875 GiB
# analytics and realtime were killed for running out of memory at half of production
# analytics, realtime and vector were killed for running out of memory at half of production
self.assertGreaterEqual(sp.MEMORY["analytics"][1], 768)
self.assertGreaterEqual(sp.MEMORY["realtime"][1], 384)
self.assertGreaterEqual(sp.MEMORY["vector"][1], 512)
self.assertEqual(req / 1024, 1.875)
for svc in sp.SERVICES:
self.assertEqual(self.v["deployment"][svc]["priorityClassName"], "supaspin-low")