supaspin: a terminal app that creates, lists, shows and deletes numbered throwaway Supabase instances on a single-node microk8s server, each with capped disk and memory, an HTTPS hostname, and fixed Postgres and Studio ports. Generic: nothing in it is specific to one app.
Verified live on the target server (Debian 13, microk8s, alongside production Supabase)
6 loop-mounted images per instance at their caps (db 2 GB, storage 1 GB, four at 256 MB); all 12 pods ready; priority supaspin-low; memory limits 5.625 GiB, requests 1.875 GiB
HTTPS with a valid Let's Encrypt certificate; / (Studio) and /pg/ (pg-meta) return 404 on the public hostname; auth, rest, graphql, realtime, storage and functions answer
Upload, public fetch and image resize through /storage/v1/render/image/...; the file lands inside the instance's storage image
Postgres 17.6 on <LAN IP>:544N only (127.0.0.1 refuses); Studio on <LAN IP>:300N
Three instances side by side; number 1 reused after delete; saved certificate reused with no new Let's Encrypt order
Per-instance mail settings reach the auth pod; saved defaults changed from the menu
maralago.ru prisma migrate deploy (12 migrations) and the seed ran against an instance; login works; server-side upload works
Delete leaves no namespace, volume record, fstab line, mount or folder; half-created and older-layout instances clean up too; a failed create offers the cleanup
mount -a dry run and real run clean; production pods' restart counts unchanged; supa.maralago.ru still answers
30 unit tests pass (python3 -m unittest discover -s tests)
Differences from the plan
analytics and realtime memory limits are 768 MiB and 384 MiB, not half of production (512 and 256 MiB): at half they were killed for running out of memory in a loop. Total limits are 5.625 GiB, not 5.25 GiB.
imgproxy mounts the storage volume and has no image of its own (6 images, not 7); otherwise image resizing cannot see uploaded files.
The one-line install needs a token or a checkout because git.didgibot.com requires sign-in even for public repositories.
## What
`supaspin`: a terminal app that creates, lists, shows and deletes numbered throwaway Supabase instances on a single-node microk8s server, each with capped disk and memory, an HTTPS hostname, and fixed Postgres and Studio ports. Generic: nothing in it is specific to one app.
## Verified live on the target server (Debian 13, microk8s, alongside production Supabase)
- 6 loop-mounted images per instance at their caps (db 2 GB, storage 1 GB, four at 256 MB); all 12 pods ready; priority `supaspin-low`; memory limits 5.625 GiB, requests 1.875 GiB
- HTTPS with a valid Let's Encrypt certificate; `/` (Studio) and `/pg/` (pg-meta) return 404 on the public hostname; auth, rest, graphql, realtime, storage and functions answer
- Upload, public fetch and image resize through `/storage/v1/render/image/...`; the file lands inside the instance's storage image
- Postgres 17.6 on `<LAN IP>:544N` only (`127.0.0.1` refuses); Studio on `<LAN IP>:300N`
- Three instances side by side; number 1 reused after delete; saved certificate reused with no new Let's Encrypt order
- Per-instance mail settings reach the auth pod; saved defaults changed from the menu
- maralago.ru `prisma migrate deploy` (12 migrations) and the seed ran against an instance; login works; server-side upload works
- Delete leaves no namespace, volume record, fstab line, mount or folder; half-created and older-layout instances clean up too; a failed create offers the cleanup
- `mount -a` dry run and real run clean; production pods' restart counts unchanged; `supa.maralago.ru` still answers
- 30 unit tests pass (`python3 -m unittest discover -s tests`)
## Differences from the plan
- analytics and realtime memory limits are 768 MiB and 384 MiB, not half of production (512 and 256 MiB): at half they were killed for running out of memory in a loop. Total limits are 5.625 GiB, not 5.25 GiB.
- imgproxy mounts the storage volume and has no image of its own (6 images, not 7); otherwise image resizing cannot see uploaded files.
- The one-line install needs a token or a checkout because git.didgibot.com requires sign-in even for public repositories.
Pinned chart: supabase-kubernetes 0.8.0 (Postgres 17.6.1.136).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds a terminal app that creates, lists, shows and deletes numbered Supabase
instances on a single-node microk8s server, for testing and staging copies of
any app. Nothing in it is specific to one app.
- supaspin: single-file Python app (menu and commands: create, list, show,
env, tunnel, delete, mail, setup). Instance N gets namespace supaspin-N,
hostname slot N, Postgres on <LAN IP>:544N and Studio on <LAN IP>:300N.
Lowest free number is reused; a refused create says why.
- Disk: fixed-size ext4 image files, loop-mounted from /etc/fstab. db 2 GB,
storage 1 GB, functions/snippets/deno/pgsodium 256 MB each. imgproxy mounts
the storage volume (the chart's own imgproxy volume cannot see uploads, so
image resizing failed). Empty mount points are made read-only until mounted
so a pod started early after a reboot cannot initialise a database there.
- Memory: limits 5.625 GiB, requests 1.875 GiB per instance, all 12 services.
Half of a production setup, except analytics (768 MiB) and realtime
(384 MiB), which were killed for running out of memory at half of it.
- Priority class supaspin-low (-100, never preempts) on every instance pod.
- Public HTTPS through the existing nginx ingress with Let's Encrypt. The
Studio and pg-meta routes are cut out of the Kong gateway config by a
post-renderer, which also pins the db and studio pods to fixed host ports.
- Certificates: delete keeps the TLS secret (with its cert-manager issuer
annotations) in namespace supaspin-certs and create restores it, so
create/delete cycles do not hit Let's Encrypt's 5-per-week limit.
- Secrets generated per instance: JWT secret, anon and service keys, Postgres
and Studio passwords, realtime/meta/logflare keys. The rendered values file
is removed after install; secrets are never written through kubectl apply.
- Instance .env (mode 600) with the usual Supabase variable names, plus an SSH
tunnel command and DATABASE_URL help.
- Delete is idempotent and finds the real mounts instead of trusting a list,
so half-created and older instances clean up completely. A failed create
offers to run it.
- Mail settings: saved defaults changeable from the menu; create asks to use
them or enter different ones for that instance.
- install.sh: installs the app; accepts a Gitea token or a local checkout
because git.didgibot.com requires sign-in even for public repositories.
uninstall.sh: removes the app and, after confirmation, every instance.
- tests: 30 unittest cases (numbering, ports, JWT signing, values, .env,
fstab handling, post-renderer, certificate secret handling).
- README: install, first-run setup, menu, tunnel, troubleshooting, uninstall.
Pinned chart: supabase-community/supabase-kubernetes 0.8.0 (Postgres 17.6.1.136).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
igor
merged commit cfbc4f1612 into main2026-09-18 20:03:22 +00:00
igor
deleted branch feat/initial-supaspin2026-09-18 20:03:22 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
supaspin: a terminal app that creates, lists, shows and deletes numbered throwaway Supabase instances on a single-node microk8s server, each with capped disk and memory, an HTTPS hostname, and fixed Postgres and Studio ports. Generic: nothing in it is specific to one app.Verified live on the target server (Debian 13, microk8s, alongside production Supabase)
supaspin-low; memory limits 5.625 GiB, requests 1.875 GiB/(Studio) and/pg/(pg-meta) return 404 on the public hostname; auth, rest, graphql, realtime, storage and functions answer/storage/v1/render/image/...; the file lands inside the instance's storage image<LAN IP>:544Nonly (127.0.0.1refuses); Studio on<LAN IP>:300Nprisma migrate deploy(12 migrations) and the seed ran against an instance; login works; server-side upload worksmount -adry run and real run clean; production pods' restart counts unchanged;supa.maralago.rustill answerspython3 -m unittest discover -s tests)Differences from the plan
Pinned chart: supabase-kubernetes 0.8.0 (Postgres 17.6.1.136).
🤖 Generated with Claude Code