Managed services
One-click PostgreSQL, Redis, and MinIO — pinned, published on no port, and reachable by container name.
A managed service is a database the platform runs for you, in its own compose project, on the shared network your applications are already on.
The three kinds
| Image | Default version | Port | Connection URL | |
|---|---|---|---|---|
| PostgreSQL | postgres | 17-alpine | 5432 | postgresql://… |
| Redis | redis | 7.4-alpine | 6379 | redis://… |
| MinIO | minio/minio | RELEASE.2025-04-22T22-12-26Z | 9000 (console 9001) | http://… |
Every image tag is pinned. latest on a database is how an on-disk format changes
underneath you.
You can pin a different version at creation. You cannot change it afterwards by editing a field — see changing versions below.
No published ports
A managed service publishes nothing to the host
A managed database reachable from the internet is the single most common way a self-hosted deployment is lost. Applications reach a service by container name on the shared network, never through a host port.
The names are fixed rather than left to Compose's <project>-<service>-1 scheme, because they
get pasted into an application's configuration and must not change when the compose file is
regenerated:
| Hostname | sthora-svc-<slug> |
| Compose project | st-svc-<slug> |
| Data volume | sthora-svc-<slug>-data |
Connecting an application
GET /services/:id/connection is the only route that discloses generated credentials, and
it writes an audit row every time it is read. The list and detail routes have no field that
could carry one.
It returns the host, port, username, password, database, a ready-to-paste URL, and the variable names an application conventionally expects them under:
| Kind | Suggested variables |
|---|---|
| PostgreSQL | DATABASE_URL |
| Redis | REDIS_URL |
| MinIO | S3_ENDPOINT, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY |
Copy them into the application's environment variables and redeploy.
Settings
| Setting | Range | |
|---|---|---|
name | 1–64 chars | |
version | 1–64 chars | Pinned per kind when omitted. Never latest. |
environmentId | Optional. Scopes the service to one environment. | |
cpuLimit | 0.1–64 | |
memoryLimitMb | 64–262144 | |
backupSchedule | Five-field cron | See backups. |
backupTarget | LOCAL | S3 | |
backupRetentionCount | 1–365 |
Status and lifecycle
NEW → PROVISIONING → RUNNING, or STOPPED / FAILED.
Start, stop, and restart are three explicit routes, never one :action segment. Services run
through their own queue at concurrency 1 (SERVICES_CONCURRENCY).
What each kind is configured with
PGDATA is set to a subdirectory of the mounted volume. Postgres refuses to
initialise into a directory that is not empty, and a mounted volume has a
lost+found on some filesystems; the subdirectory is the documented way
around it.
Started with --requirepass, --appendonly yes, and --maxmemory-policy noeviction — for the same reason the platform's own Redis uses it. An LRU
policy silently drops data an application believed was stored.
Started as server /data --console-address :9001. The console port is not
published either; reach it the same way an application does.
Changing a version
There is no in-place upgrade. A volume that already holds data from a different major version will stop the service from starting at all — Postgres refuses an on-disk format it did not write, and says so explicitly in the log.
The path is: back up, create a new service on the new version, restore into it, and repoint your applications.