3 changed files with 15 additions and 8 deletions
+13 -6
View File
@@ -1,6 +1,6 @@
# supaspin # supaspin
Version 1.6.2 Version 1.6.3
Create, list, show and delete throwaway [Supabase](https://supabase.com) instances on a Create, list, show and delete throwaway [Supabase](https://supabase.com) instances on a
single-node [microk8s](https://microk8s.io) server, from a terminal menu. single-node [microk8s](https://microk8s.io) server, from a terminal menu.
@@ -33,7 +33,9 @@ of instances at once. Numbers are reused: create picks the lowest free one.
- A Debian/Ubuntu server with **microk8s** (or any Kubernetes with `kubectl` and `helm` on the - A Debian/Ubuntu server with **microk8s** (or any Kubernetes with `kubectl` and `helm` on the
path), with an ingress controller installed: **ingress-nginx** or **Traefik** (the microk8s path), with an ingress controller installed: **ingress-nginx** or **Traefik** (the microk8s
`ingress` addon is either, depending on the version). `ingress` addon is either, depending on the version). With **Traefik**, its CRDs must be present
on the cluster — they come with the Traefik installation itself; supaspin creates `Middleware`
objects and does not install cluster-wide types.
- With **Let's Encrypt** enabled: **cert-manager** installed, DNS `A` records for every hostname - With **Let's Encrypt** enabled: **cert-manager** installed, DNS `A` records for every hostname
slot pointing at the server, and ports 80 and 443 forwarded to it (Let's Encrypt fetches a test slot pointing at the server, and ports 80 and 443 forwarded to it (Let's Encrypt fetches a test
file over port 80 to issue each certificate). Without it: no cert-manager, no public DNS — each file over port 80 to issue each certificate). Without it: no cert-manager, no public DNS — each
@@ -77,7 +79,7 @@ sudo supaspin
| Server LAN IP | address shown for the Postgres and Studio ports (detected) | | Server LAN IP | address shown for the Postgres and Studio ports (detected) |
| Port bind | `lan` = LAN IP only (default; keeps Postgres and the login-less Studio off a public interface), `all` = every interface incl. localhost (for a box behind NAT running its own apps), `local` = localhost only | | Port bind | `lan` = LAN IP only (default; keeps Postgres and the login-less Studio off a public interface), `all` = every interface incl. localhost (for a box behind NAT running its own apps), `local` = localhost only |
| Ingress class | usually `public` on microk8s | | Ingress class | usually `public` on microk8s |
| Ingress controller | `traefik` or `nginx`; Traefik instances get a Middleware that caps uploads at 50 MiB, nginx gets the equivalent annotations | | Ingress controller | `traefik` or `nginx`; every instance's Ingress carries the nginx annotations (50 MiB upload cap, long timeouts) and, in Traefik mode, additionally points at a `Middleware` object with the same 50 MiB cap - so the cap is enforced no matter which of the two actually routes the hostname |
| Let's Encrypt | `y` fetches a real certificate per hostname (needs public DNS, ports 80/443, cert-manager); `n` gives every instance a self-signed certificate and creates no cert-manager objects | | Let's Encrypt | `y` fetches a real certificate per hostname (needs public DNS, ports 80/443, cert-manager); `n` gives every instance a self-signed certificate and creates no cert-manager objects |
| Let's Encrypt e-mail | for certificate expiry notices (asked only when Let's Encrypt is on) | | Let's Encrypt e-mail | for certificate expiry notices (asked only when Let's Encrypt is on) |
| Hostname slots | comma-separated; a name with a number in it keys that slot (`supa4.didgibot.com` = slot 4, so instance 4 gets it with ports 5444/3004), a plain name takes its position in the list | | Hostname slots | comma-separated; a name with a number in it keys that slot (`supa4.didgibot.com` = slot 4, so instance 4 gets it with ports 5444/3004), a plain name takes its position in the list |
@@ -115,12 +117,12 @@ sudo supaspin env N # the full .env (contains secrets)
sudo supaspin tunnel N # the tunnel commands, step by step sudo supaspin tunnel N # the tunnel commands, step by step
sudo supaspin push-env N TARGET [--port P] [--key FILE] [--dry-run] # see "Copying an instance's values" sudo supaspin push-env N TARGET [--port P] [--key FILE] [--dry-run] # see "Copying an instance's values"
sudo supaspin delete N [--yes] sudo supaspin delete N [--yes]
sudo supaspin upgrade [N] # apply this version's memory caps to running instances sudo supaspin upgrade [N] # apply this version's memory caps and log retention to running instances
sudo supaspin suspend N # stop an instance, keep its data (see "Suspending an instance") sudo supaspin suspend N # stop an instance, keep its data (see "Suspending an instance")
sudo supaspin resume N # start it again sudo supaspin resume N # start it again
sudo supaspin mail # change the saved default mail settings sudo supaspin mail # change the saved default mail settings
sudo supaspin setup # change settings sudo supaspin setup # change settings
supaspin --version # prints "supaspin 1.3.1" supaspin --version # prints "supaspin X.Y.Z"
``` ```
### Connecting your app ### Connecting your app
@@ -300,7 +302,9 @@ every delivery with an error, and vector was killed against its 1024 MiB ceiling
minutes with the undeliverable backlog. The fix is log retention: every instance gets a pg_cron minutes with the undeliverable backlog. The fix is log retention: every instance gets a pg_cron
job (set up on create, upgrade and resume) that deletes analytics logs older than **2 days** every job (set up on create, upgrade and resume) that deletes analytics logs older than **2 days** every
night. pg_cron in this image only runs jobs from the `postgres` database, so the job lives there night. pg_cron in this image only runs jobs from the `postgres` database, so the job lives there
and reaches the log tables in `_supabase` over a local dblink connection. That bounds both the and reaches the log tables in `_supabase` over a local dblink connection. The dblink extension
and the helper function live in the `extensions` schema, where the app's database owner has no
rights, so instance code can never call them. That bounds both the
db image and vector's backlog, so vector is back to the 512 MiB ceiling the production supabase db image and vector's backlog, so vector is back to the 512 MiB ceiling the production supabase
charts carry. charts carry.
@@ -317,6 +321,9 @@ pods are replaced service by service and nothing else about the instance changes
- **Certificate stays pending.** The hostname's DNS record must point at the server and port 80 must - **Certificate stays pending.** The hostname's DNS record must point at the server and port 80 must
reach the ingress: `kubectl get certificate,order,challenge -n supaspin-N`. reach the ingress: `kubectl get certificate,order,challenge -n supaspin-N`.
- **"no free hostname slot".** Delete an instance or add a slot in Settings (and its DNS record). - **"no free hostname slot".** Delete an instance or add a slot in Settings (and its DNS record).
- **Create failed** with `no matches for kind "Middleware" in version "traefik.io/v1alpha1"`. The
config says Traefik but the cluster has no Traefik CRDs: either install Traefik (its CRDs come
with it) or set the ingress controller to `nginx` in Settings.
- **Create failed.** `sudo supaspin delete N`, then create again. - **Create failed.** `sudo supaspin delete N`, then create again.
- **Port already in use.** Postgres and Studio ports are fixed per number (`5440+N`, `3000+N`); - **Port already in use.** Postgres and Studio ports are fixed per number (`5440+N`, `3000+N`);
another program is using it. Free the port or use another instance number. another program is using it. Free the port or use another instance number.
+1 -1
View File
@@ -1 +1 @@
1.6.2 1.6.3
+1 -1
View File
@@ -39,7 +39,7 @@ import tty
import yaml import yaml
VERSION = "1.6.2" VERSION = "1.6.3"
CONFIG_PATH = os.environ.get("SUPASPIN_CONFIG", "/etc/supaspin/config.yaml") CONFIG_PATH = os.environ.get("SUPASPIN_CONFIG", "/etc/supaspin/config.yaml")
STATE_DIR = os.environ.get("SUPASPIN_STATE", "/var/lib/supaspin") STATE_DIR = os.environ.get("SUPASPIN_STATE", "/var/lib/supaspin")
CHART_REPO = "https://supabase-community.github.io/supabase-kubernetes" CHART_REPO = "https://supabase-community.github.io/supabase-kubernetes"