GoRunner docs
Search or askSearch⌘K

Your own database, Redis and storage

Use a PostgreSQL, Redis and S3 bucket you already run, instead of the built-in ones.

10 min · Self-hosted

For each store, the setup asks: built in, a container on this server, or your own. Several servers need all three of your own.

StoreBuilt inYour own
DatabasePostgreSQL, in the pgdata volumePostgreSQL 15 or newer: RDS, Cloud SQL, Azure, your own
RedisRedis, set up as GoRunner needsRedis 6.2 or newer: ElastiCache, Upstash, Memorystore
File storageSeaweedFSAny S3 bucket: AWS, R2, MinIO, Backblaze, Wasabi. Or none: step data stays in the database.

.env names each choice in a word: GORUNNER_POSTGRES and GORUNNER_REDIS are bundled or external; GORUNNER_STORAGE also takes none. A deleted line stops GoRunner, rather than starting an empty built-in store.

Give the address

As separate fields, a password needs no escaping; as one address (DATABASE_URL, REDIS_URL), it does, and wins when both are set. The host is a name or an address alone, never with its port.

.env
GORUNNER_POSTGRES=external
POSTGRES_HOST=gorunner-db.c9x2.eu-west-1.rds.amazonaws.com
POSTGRES_PORT=5432
POSTGRES_DB=gorunner
POSTGRES_USER=gorunner
POSTGRES_PASSWORD=<password>
POSTGRES_CA_FILE=/certs/rds.pem

Your own PostgreSQL

Version 15 or newer
GoRunner’s tables use it.
A user that owns GoRunner’s tables
And can create tables in public. If another user owns them, the check prints the SQL that hands them over.
uuid-ossp, citext
Added at the first start. The user needs CREATE on the database, or add them yourself.
pgcrypto
Needed at the first start only: GoRunner adds it, then removes it because nothing uses it. If you or your provider added it, it may stay, unused. An update never needs it.
Enough connections
The check works out each process’s pool, DB_MAX_CONNS, and keeps 5 free for backups and an admin (DB_ADMIN_HEADROOM).
No pooler in transaction mode
PgBouncer in transaction mode can freeze updates; the check refuses it. Session mode works.

TLS. POSTGRES_SSLMODE is require by default, or verify-full with your provider’s CA file in certs/, named as /certs/rds.pem. With DATABASE_URL, add sslrootcert=/certs/rds.pem&sslmode=verify-full. A file it can’t use stops it. See The database CA file.

How do I add the extensions myself?

For a new database. One that has started once needs only uuid-ossp and citext. As the database’s owner or your provider’s admin user, connected to GoRunner’s database, once:

sql
GRANT CREATE ON DATABASE gorunner TO gorunner;
-- or add them yourself:
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS citext;

Your own Redis

Version 6.2 or newer
The check refuses older ones.
maxmemory-policy noeviction
Redis must never delete keys to make room. Managed services often default to volatile-lru: change it in the parameter group.
Room under maxmemory
At the limit, Redis refuses new writes: new runs wait instead of starting, schedules don’t fire and sign-in fails until memory is freed. Health warns from 70 %.
Saved to disk
AOF or snapshots. Without it, a restart signs everyone out. The check warns.
Not in cluster mode
The check refuses it. ElastiCache Serverless and MemoryDB only run in cluster mode.
A database of its own
REDIS_DB, 0 by default.

For TLS, set REDIS_TLS=true or use a rediss:// address. For a user as well as a password, set REDIS_USER.

What Redis holds

Wake-ups for the workers, which worker runs which run, what each run in progress has finished, sessions and short-lived codes. Runs and the Storage and Queue steps’ data are in PostgreSQL. Losing Redis signs everyone out, and a run in progress that could change something outside fails with Progress unknown. No run is lost.

Using Amazon ElastiCache?
Cluster mode disabled
One primary, replicas if you like. GoRunner uses the primary endpoint.
A parameter group
Your own, with maxmemory-policy set to noeviction.
Encryption in transit and AUTH
REDIS_TLS=true; the token as REDIS_PASSWORD, with REDIS_USER for role-based access.
Snapshots
Kept a day or more. There is no AOF on current engines.

ElastiCache doesn’t allow CONFIG GET, so the check may warn it couldn’t confirm the policy. That is expected: check the parameter group yourself.

Your own S3 bucket

S3_ENDPOINT
Such as https://s3.eu-central-1.amazonaws.com, or your MinIO or R2 address.
S3_BUCKET, S3_REGION
gorunner by default; auto for R2. GoRunner creates the bucket if the key may.
S3_ACCESS_KEY, S3_SECRET_KEY
A key that can write, read and delete objects. The check tries all three.
S3_FORCE_PATH_STYLE
true for MinIO and SeaweedFS.

The setup keeps step data up to 4 KB in the database (STORAGE_INLINE_THRESHOLD_KB). With none, all of it stays there; turning storage off later is refused while step data still lives in storage.

Use the server’s AWS role instead of a key?

Leave both keys empty. The containers read the role from the instance metadata service, one network hop further than the server, so set the hop limit to 2:

bash
aws ec2 modify-instance-metadata-options --instance-id <id> \
  --http-put-response-hop-limit 2 --http-tokens required

Workflow steps never reach the metadata service, whatever the hop limit. A bucket that keeps versions needs s3:DeleteObjectVersion too, and a lifecycle rule for old versions.

Certificates

Every container trusts the .pem files in the install’s certs/ folder, beside the public authorities. On Kubernetes, mount them at /certs (Helm values).

Changing later

Run ./setup.sh again and change the answer. A moved store or a changed address is listed, and you type change. The other store doesn’t hold your data: move the data first, with your database’s dump and restore. The built-in stores’ passwords stay in .env.previous-stores.

Changed a store word in .env by hand? The update then needs --confirm-store-change.

Checked against 12d85b3b