Sthora
Reference

The installer

install.sh — its three modes, every variable it reads, and every file it touches.

install.sh              install, or repair an existing install
install.sh --upgrade    pull newer images and apply migrations
install.sh --uninstall  stop everything and remove the platform
install.sh --help       print this

Run as root. Idempotent and re-runnable: every step checks before it acts, so a second run repairs rather than duplicates.

Variables it reads

VariableDefaultUsed for
STHORA_HOSTNAMElocalhostThe portal's hostname; goes into PUBLIC_BASE_URL, AUTH_TRUSTED_ORIGINS, and the Traefik router rule
STHORA_ACME_EMAILEmptyPROXY_ACME_EMAIL
STHORA_BASE_DOMAINEmptyPROXY_BASE_DOMAIN
STHORA_EMAIL_FROMEmptyEMAIL_FROM_ADDRESS
STHORA_EMAIL_FROM_NAMEEmptyEMAIL_FROM_NAME
STHORA_SMTP_HOSTEmptyEMAIL_SMTP_HOST
STHORA_SMTP_PORT587EMAIL_SMTP_PORT
STHORA_SMTP_SECUREfalseEMAIL_SMTP_SECURE — implicit TLS. true for port 465; 587 upgrades with STARTTLS and wants false
STHORA_SMTP_USEREmptyEMAIL_SMTP_USER
STHORA_SMTP_PASSWORDEmptyEMAIL_SMTP_PASSWORD
STHORA_VERSIONlatestThe image tag
STHORA_IMAGE_REGISTRYghcr.io/asynccodersWhere images are pulled from
STHORA_REGISTRY_HOSTghcr.ioThe registry to sign in to, when a token is given
STHORA_REGISTRY_USEREmptyRegistry username. Required when a token is set
STHORA_REGISTRY_TOKENEmptyRegistry token. Unset means the pull is anonymous, which is what a public registry wants
STHORA_MODEself-hostedself-hosted or cloud — see below
STHORA_DEFAULT_PLANMode-dependentBILLING_DEFAULT_PLAN

Distribution mode

STHORA_MODE moves three settings together, because a deployment with two of the three set is in a state neither mode intends.

self-hostedcloud
SIGNUP_ENABLEDfalsetrue
BILLING_ENFORCE_LIMITSfalsetrue
AGENT_CHANNEL_ENABLEDfalsetrue
BILLING_DEFAULT_PLANself-hostedstarter

STHORA_DEFAULT_PLAN overrides the last row. In cloud mode the installer refuses self-hosted for it: that plan's limits are null everywhere, null means unlimited rather than zero, and open registration on top of it hands an unlimited account to anyone who finds the URL.

Both modes also get TRUSTED_PROXY_HOPS=1, which is what makes req.ip — the rate limiter's key, and every audit record's requester address — the client's address rather than Traefik's. The schema defaults it to 0 because guessing a hop count is worse than not throttling. The installer is not guessing; it wrote the topology.

Preflight

It refuses, rather than warns, on any of:

Floor
DistributionUbuntu or Debian — it reads /etc/os-release
Architectureamd64 or arm64
RAM2048 MB
Free disk on /20 GB

It warns below 4096 MB of RAM. Builds are the memory spike, and the warning is about builds rather than about the platform sitting idle.

What it changes on the machine

Startup

Pulls the images (falling back to what is on the box if a pull fails), brings the stack up, polls docker exec sthora-api curl -fsS localhost:4600/health for up to two minutes, then runs prisma migrate deploy.

If the API never becomes healthy it stops and tells you to read docker compose -p sthora logs api.

Upgrade mode

Dumps the database first, rewrites the compose file, pulls, brings up, waits for health, migrates, and prunes dangling images. See upgrading.

Uninstall mode

Asks you to type remove, then runs compose down. Data volumes and /var/lib/sthora are kept, and it prints the commands to remove them.

Building from source

Multi-arch images for backend, dashboard, agent, and website are published to GHCR by CI after the suite passes on main. Building the platform from source on the target box takes five to eight minutes and a lot of the RAM budget, which is why the installer pulls.

Not yet verified end to end

The installer has been syntax-checked and everything it orchestrates is exercised by the test suite, but it has not been run end to end on a fresh machine.

On this page