Files
Igor K.andClaude Sonnet 5 cfbc4f1612 feat: supaspin, on-demand throwaway Supabase instances on microk8s
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>
2026-09-18 23:02:40 +03:00

40 lines
1.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Remove supaspin. Run on the server: sudo bash uninstall.sh
# Asks before deleting instances (their data is gone for good) and before removing the
# shared cluster objects that the first-run setup created.
set -uo pipefail
[ "$(id -u)" -eq 0 ] || { echo "Run as root: sudo bash uninstall.sh" >&2; exit 1; }
if [ -x /snap/bin/microk8s.kubectl ]; then K=/snap/bin/microk8s.kubectl; else K=kubectl; fi
# Ask on the terminal (works when the script itself arrives through a pipe), else on stdin.
ask() {
if { : </dev/tty; } 2>/dev/null; then read -r -p "$1" a </dev/tty; else read -r -p "$1" a; fi
}
if [ -x /usr/local/bin/supaspin ] && [ -f /etc/supaspin/config.yaml ]; then
# Same discovery as `supaspin list`: namespaces named supaspin-<N>.
nums="$($K get ns -o name 2>/dev/null | sed -n 's#^namespace/supaspin-\([0-9][0-9]*\)$#\1#p' | sort -n)"
if [ -n "$nums" ]; then
echo "Instances found: $(echo $nums)"
ask "Delete ALL of them, with their databases and files? Type yes: "
if [ "$a" = "yes" ]; then
for n in $nums; do /usr/local/bin/supaspin delete "$n" --yes; done
else
echo "Keeping the instances; they will keep running without the supaspin command."
fi
fi
ask "Also remove the shared cluster objects (StorageClass supaspin-local-sc, issuer supaspin-letsencrypt, PriorityClass supaspin-low, namespace supaspin-certs with saved certificates)? Type yes: "
if [ "$a" = "yes" ]; then
$K delete storageclass supaspin-local-sc --ignore-not-found
$K delete clusterissuer supaspin-letsencrypt --ignore-not-found
$K delete priorityclass supaspin-low --ignore-not-found
$K delete namespace supaspin-certs --ignore-not-found
fi
fi
rm -f /usr/local/bin/supaspin
rm -rf /usr/share/supaspin /etc/supaspin /var/lib/supaspin
echo "supaspin removed."