Sthora
Getting started

Install

One script, run as root on a fresh Ubuntu or Debian machine. What it does, what it generates, and what to back up afterwards.

Run it

Fetch the script onto a fresh machine, then run it as root:

curl -fsSL https://sthora.com/install.sh -o install.sh
chmod +x install.sh

STHORA_HOSTNAME=cloud.example.com \
STHORA_ACME_EMAIL=ops@example.com \
./install.sh

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

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. Expect to find something, and please say what you found.

What it does, in order

Preflight. Reads /etc/os-release and refuses anything that is not Ubuntu or Debian; refuses an architecture that is not amd64 or arm64; refuses below 2 GB of RAM or 20 GB of free disk on /, and warns below 4 GB of RAM.

Docker. Installs Docker Engine from get.docker.com and enables it, unless both the engine and the Compose v2 plugin are already present.

Swap. Creates a 2 GB swapfile and adds it to /etc/fstab, unless swap already exists. Swap does not make a build fast; it makes the difference between a build that is slow at its peak and one the kernel kills.

Docker daemon. Writes /etc/docker/daemon.json with json-file logging capped at 10 MB across 3 files, live-restore, and a 10.100.0.0/16 address pool at /24, then restarts Docker.

Firewall. Installs ufw if missing, allows 22, 80, and 443, sets default-deny inbound, and enables it. Port 22 is allowed before the firewall is enabled, because doing it the other way round locks you out of the machine you are installing on.

Credentials. Generates the PostgreSQL password, the Redis password, and AUTH_SECRET with openssl rand, and writes them to /etc/sthora/.env at mode 0600. Generates the master encryption key into /etc/sthora/master.key at 0600.

Compose. Installs the platform's compose file at /etc/sthora/docker-compose.yml, writes /etc/sthora/compose.env with the image registry, version, and hostname, and creates the external sthora-proxy network.

Start and migrate. Pulls the images, brings the stack up, waits up to two minutes for the API to answer /health, then applies the database migrations with prisma migrate deploy — which applies committed migrations and can never generate one.

It finishes by printing the portal URL and the first-run URL.

What it reads from the environment

VariableRequiredMeaning
STHORA_HOSTNAMEYes in practiceThe hostname the portal is served on. Falls back to localhost, which is only useful for a local experiment.
STHORA_ACME_EMAILYes for certificatesWhere Let's Encrypt sends expiry warnings. Certificates are not issued until it is set.
STHORA_BASE_DOMAINNoWildcard base domain. Every application also answers on <slug>.<baseDomain>.
STHORA_EMAIL_FROMNoSender address for alerts and password resets.
STHORA_SMTP_HOSTNoSMTP relay. Without it, email is skipped and logged.
STHORA_VERSIONNoImage tag to install. Defaults to latest.
STHORA_IMAGE_REGISTRYNoWhere images are pulled from. Defaults to ghcr.io/asynccoders.

What it writes

.env
master.key
docker-compose.yml
compose.env

/etc/sthora/.env and master.key are mode 0600; the directory is 0700.

Back up the master key

Back up /etc/sthora/master.key off the box

Every environment variable, every deploy key, every webhook secret, and every managed-service credential is encrypted under it. Losing it does not lose the platform — it loses every secret the platform is holding, unrecoverably.

Then

Open https://<your hostname>/setup and claim the first account.

Removing it

./install.sh --uninstall

Stops everything and removes the platform. Applications you deployed are separate compose projects; read uninstalling before you run it.

On this page