Fixed cluster
A fixed cluster runs each Golem service from its own image on Docker Hub , with a fixed number of worker executors. All services share one Postgres database and one S3 bucket. This page describes the configuration that every platform needs. For the steps on a platform, see VM or AWS ECS. The production-cluster example contains all settings of this page in a Docker Compose file.
Services
| Service | Image | Replicas | Ports in the examples |
|---|---|---|---|
| Registry service | golemservices/registry-service | 1 or more | HTTP 8083 (management API), gRPC 9008 |
| Shard manager | golemservices/golem-shard-manager | Exactly 1 | HTTP 8081, gRPC 9002 |
| Worker service | golemservices/golem-worker-service | 1 or more | HTTP 9005 (management API), custom requests 9006, MCP 9007, gRPC 9094 |
| Component compilation service | golemservices/golem-component-compilation-service | 1 or more | HTTP 8084, gRPC 9010 |
| Worker executor | golemservices/golem-worker-executor | N | HTTP 8082, gRPC 9000 |
Every service reads a TOML file in its image, and GOLEM__<FIELD> environment variables override it. For a nested field, join the names with __: GOLEM__DB__CONFIG__HOST sets db.config.host. A list is written in TOML syntax, for example GOLEM__ROUTE_RESOLVER__TRUSTED_INGRESS_ADDRESSES='["10.0.0.5/32"]'. The Deployment page links the configuration file and the environment variables of each service.
Storage
Postgres
Each store has its own schema in the same database. These are the schema names that the all-in-one server uses, so a database can move between the two:
| Service | Settings | Schema |
|---|---|---|
| Registry service | GOLEM__DB__TYPE=Postgres, GOLEM__DB__CONFIG__* | golem_registry_service |
| Shard manager | GOLEM__PERSISTENCE__TYPE=Postgres, GOLEM__PERSISTENCE__CONFIG__* | golem_shard_manager |
| Worker executor | GOLEM__KEY_VALUE_STORAGE__TYPE=Postgres, GOLEM__KEY_VALUE_STORAGE__CONFIG__* | golem_worker_executor |
| Worker executor | GOLEM__INDEXED_STORAGE__TYPE=Postgres, GOLEM__INDEXED_STORAGE__CONFIG__* | golem_worker_executor_indexed |
| Worker executor | GOLEM__SCHEDULER_STORAGE__TYPE=Postgres, GOLEM__SCHEDULER_STORAGE__CONFIG__* | golem_worker_executor_scheduler |
| Worker service | GOLEM__GATEWAY_SESSION_STORAGE__TYPE=Postgres, GOLEM__GATEWAY_SESSION_STORAGE__CONFIG__* | golem_worker_service_session_store |
Each CONFIG takes HOST, PORT, DATABASE, USERNAME, PASSWORD, MAX_CONNECTIONS and SCHEMA. The session storage also needs PENDING_LOGIN_EXPIRATION (for example 1h) and CLEANUP_INTERVAL (for example 5m). With the sessions in Postgres, the cluster needs no Redis.
S3
The registry service, the worker service, the component compilation service and the worker executor use the same blob storage. With one bucket:
GOLEM__BLOB_STORAGE__TYPE=S3
GOLEM__BLOB_STORAGE__CONFIG__REGION=us-east-1
GOLEM__BLOB_STORAGE__CONFIG__OBJECT_PREFIX=""
GOLEM__BLOB_STORAGE__CONFIG__NAMESPACE_PREFIXES=true
GOLEM__BLOB_STORAGE__CONFIG__COMPILATION_CACHE_BUCKET=golem
GOLEM__BLOB_STORAGE__CONFIG__CUSTOM_DATA_BUCKET=golem
GOLEM__BLOB_STORAGE__CONFIG__OPLOG_PAYLOAD_BUCKET=golem
GOLEM__BLOB_STORAGE__CONFIG__COMPRESSED_OPLOG_BUCKETS='["golem"]'
GOLEM__BLOB_STORAGE__CONFIG__INITIAL_AGENT_FILES_BUCKET=golem
GOLEM__BLOB_STORAGE__CONFIG__COMPONENTS_BUCKET=golem
GOLEM__BLOB_STORAGE__CONFIG__FILESYSTEM_SNAPSHOTS_BUCKET=golem
GOLEM__BLOB_STORAGE__CONFIG__RETRIES__MAX_ATTEMPTS=3
GOLEM__BLOB_STORAGE__CONFIG__RETRIES__MIN_DELAY=100ms
GOLEM__BLOB_STORAGE__CONFIG__RETRIES__MAX_DELAY=1s
GOLEM__BLOB_STORAGE__CONFIG__RETRIES__MULTIPLIER=3.0
GOLEM__BLOB_STORAGE__CONFIG__RETRIES__MAX_JITTER_FACTOR=0.15For an S3-compatible service, add GOLEM__BLOB_STORAGE__CONFIG__AWS_ENDPOINT_URL and, if the service needs it, GOLEM__BLOB_STORAGE__CONFIG__AWS_PATH_STYLE=true. The S3 client reads its credentials from the standard AWS sources, such as AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY or the role of the task. See Blob storage on S3.
Secrets
The images do not check their secrets, and their defaults are public. Set these on every replica of the service:
| Service | Environment variable | Value |
|---|---|---|
| Registry service | GOLEM__INITIAL_ACCOUNTS__ROOT__TOKEN | The admin token, for example from openssl rand -hex 32 |
| Registry service | GOLEM__INITIAL_ACCOUNTS__MARKETING__TOKEN | Another token |
| Worker service | GOLEM__WEBHOOK_CALLBACK_HANDLER__HMAC_KEY | 32 random bytes, base64 |
| Worker executor | GOLEM__AGENT_WEBHOOKS_SERVICE__HMAC_KEY | The same value as the worker service’s webhook HMAC key |
| Worker service | GOLEM__INVOCATION_SESSION_TOKENS__ACTIVE_KEY__ID | A name for the key, for example key-1 |
| Worker service | GOLEM__INVOCATION_SESSION_TOKENS__ACTIVE_KEY__KEY | 32 random bytes, base64 |
The registry service’s default login is GitHub OAuth2 with placeholder credentials. Set GOLEM__LOGIN__TYPE=Disabled to allow only the tokens of the initial accounts, or configure GOLEM__LOGIN__CONFIG__GITHUB__*.
Plans
The registry service creates the default plan from initial_plans.default at every start, and updates the plan when the values change. The defaults of the image limit applications, components, storage and uploads; set GOLEM__INITIAL_PLANS__DEFAULT__<LIMIT> to change them.
With the default Temporary agent filesystem storage, an agent whose plan has a finite max_disk_space_per_worker does not start: the invocation fails with failed to install limits without quota authority. Set GOLEM__INITIAL_PLANS__DEFAULT__MAX_DISK_SPACE_PER_WORKER=1000000000000000000, the unlimited value, unless the executors use ManagedXfs storage. Executors cache the limits they have read, so restart them after you change the plan.
Built-in plugins and tools
At every start, the registry service provisions Golem’s built-in plugins and tools from its cache, and downloads from the golem-builtins releases on GitHub each one that the cache does not have or that does not match its checksum. If a download fails, the registry service does not start. In the image, the cache is /app/builtin-artifacts, inside the container, so every new registry container downloads them again, which takes about a minute and needs outgoing HTTPS connections to GitHub. To keep the cache, mount a persistent volume at /app/builtin-artifacts. The volume must be writable by uid 65534, the user of the image; see User and Permissions.
Shard manager
Run one shard manager replica in SQL mode (GOLEM__PERSISTENCE__TYPE=Postgres), and configure the platform to stop the old replica before it starts the new one. Two shard managers on one database can run the same agent twice. See One shard manager.
While the shard manager restarts, the executors keep their shards until their lease runs out (1 minute by default), but new executors cannot register. Raise GOLEM__SHARD_MANAGER__RETRIES__MAX_ATTEMPTS on the executors, so that an executor that starts during a shard manager restart retries its registration until the shard manager is back.
etcd mode (GOLEM__PERSISTENCE__TYPE=Etcd) allows more than one replica, but only one of them, the leader, opens its gRPC port. Use it only where the platform sends traffic to the ready replica alone, such as Kubernetes with a readiness probe on the gRPC port. Platforms that replace a task that fails its health check, such as ECS, would replace every standby. etcd must run on volumes and on the private network, because the shard manager talks to it over plain HTTP. See Shard Manager.
Do not move the shard manager to another store, such as from Postgres to etcd, in a cluster that already runs agents. A new store starts the shard epochs over, and the executors refuse to write the oplogs of existing agents until the epochs are cleared as Shard Manager describes.
Worker executors
Set the number of executors with the replica count of the platform. Each executor registers with the shard manager when it starts and gets a share of the shards. A stopped executor deregisters and its shards move to the other executors. An executor that disappears without stopping loses its shards when its lease runs out.
The shard manager records the source IP address of each executor’s registration, and the worker service and the shard manager dial the executor’s gRPC port at that address. On a network that translates source addresses, set GOLEM__GRPC__ADVERTISED_IP on each executor to its own reachable IP address. On an IPv6 network, set GOLEM__BIND_ADDRESS=:: on every service. See Network Addresses.
The executor’s agent files are in temporary directories by default, and nothing of an executor needs a volume. To keep agent files across executors, see Filesystem snapshots.
TLS between the services
The services talk to each other over gRPC. Each gRPC server can require TLS, and each gRPC client can use it. The services always use mutual TLS: a server with TLS enabled requires a client certificate that its client CA signed, and a client checks the server certificate against its server CA.
Every service has a server setting grpc.tls (GOLEM__GRPC__TLS__TYPE). Every client of a service has its own setting <section>.tls, with the same keys on both sides. The sections are:
| Service | Client sections |
|---|---|
| Registry service | component_compilation.config.tls |
| Worker service | registry_service.tls, shard_manager.tls, worker_executor.tls |
| Shard manager | registry_service.tls, worker_executors.tls |
| Worker executor | registry_service.tls, shard_manager.tls, public_worker_api.tls |
| Component compilation service | compile_worker.tls |
Set TYPE to Enabled and give the settings of the table below. Each certificate, key and CA comes from a PEM file (the _file setting) or from an inline value (the PEM in standard Base64 on one line), not both. A service refuses a configuration that sets both, or neither, for an item.
Server (grpc.tls.config.) | Client (<section>.tls.config.) |
|---|---|
server_cert_file or server_cert | client_cert_file or client_cert |
server_key_file or server_key | client_key_file or client_key |
client_ca_cert_file or client_ca_cert | server_ca_cert_file or server_ca_cert |
server_domain_name (optional) |
The environment name of a setting is GOLEM__GRPC__TLS__CONFIG__SERVER_CERT_FILE on the server, and for example GOLEM__REGISTRY_SERVICE__TLS__CONFIG__CLIENT_CERT_FILE on the client of the registry service. Without server_domain_name, a client checks the server certificate against the host of the address it connects to.
# In the pod template of a service that serves gRPC and calls the registry service
containers:
- name: golem-worker-service
env:
- name: GOLEM__GRPC__TLS__TYPE
value: Enabled
- name: GOLEM__GRPC__TLS__CONFIG__SERVER_CERT_FILE
value: /etc/golem-grpc-tls/tls.crt
- name: GOLEM__GRPC__TLS__CONFIG__SERVER_KEY_FILE
value: /etc/golem-grpc-tls/tls.key
- name: GOLEM__GRPC__TLS__CONFIG__CLIENT_CA_CERT_FILE
value: /etc/golem-grpc-tls/ca.crt
- name: GOLEM__REGISTRY_SERVICE__TLS__TYPE
value: Enabled
- name: GOLEM__REGISTRY_SERVICE__TLS__CONFIG__CLIENT_CERT_FILE
value: /etc/golem-grpc-tls/tls.crt
- name: GOLEM__REGISTRY_SERVICE__TLS__CONFIG__CLIENT_KEY_FILE
value: /etc/golem-grpc-tls/tls.key
- name: GOLEM__REGISTRY_SERVICE__TLS__CONFIG__SERVER_CA_CERT_FILE
value: /etc/golem-grpc-tls/ca.crt
volumeMounts:
- name: golem-grpc-tls
mountPath: /etc/golem-grpc-tls # the whole Secret as a directory, never subPath
readOnly: true
volumes:
- name: golem-grpc-tls
secret:
secretName: golem-grpc-tls # the cert-manager Certificate's SecretRenew certificates
A service reloads renewed files without a restart. It polls the files of every _file setting every 60 seconds, which matches how often the kubelet updates a mounted Secret. When the bytes of a certificate, key or CA change, the service checks the new set and then uses it:
- A server uses the new set for every TLS handshake that starts after the reload. It requires a client certificate in every handshake, and drops a handshake that takes longer than 10 seconds.
- A client uses the new set for every new connection. A connection that is open keeps the old certificate until it closes, so a renewal does not interrupt a call or a stream.
- The check parses the certificate, the key and the CA, and refuses a set in which the key does not belong to the certificate, the certificate is expired or not valid yet, or every CA certificate has expired.
A set that fails the check is not used. The service goes on with the old one, logs the error “Changed TLS files cannot be used; keeping the current certificate” once for each distinct set of files, and counts it in golem_tls_certificate_reload_failures_total. It checks the same files again on every poll. A successful reload logs “TLS certificate reloaded” with the subject, the end of validity and the SHA-256 fingerprint.
A client whose files cannot be loaded at startup, for example because the Secret is not mounted yet, starts anyway. Its calls fail with a TLS configuration error, and it tries to load the files again, at most every 60 seconds, until they load. A server refuses to start with a bad set.
Two metrics help to watch the renewal. golem_tls_certificate_not_after_seconds{use} is the Unix time at which the certificate in use expires; alert when it comes close to the current time. golem_tls_certificate_reload_failures_total{use} rises when changed files cannot be used. The use label names the user of the certificate: grpc server, <target> client for a gRPC client (for example registry client or worker_executor client), and etcd client for the shard manager’s etcd connection. See Metrics.
Mount the Secret as a directory, not with subPath: with subPath the files in the pod never change. An inline value changes only with a restart.
Ingress
The management API is split between two services. A reverse proxy or load balancer sends the management host to them by path:
| Path | Service and port |
|---|---|
/v1/agents/* | Worker service, 9005 |
/v1/components/<component>/workers and everything below it | Worker service, 9005 |
| Everything else | Registry service, 8083 |
Two of the worker service paths are WebSocket connections: /v1/agents/invoke-agent-session and /v1/components/<component>/workers/<agent>/connect.
Every other host goes to the worker service’s custom request port, 9006, which serves the HTTP API and MCP deployments of that host. Reserve the management host with GOLEM__DOMAIN_REGISTRATION__RESERVED_DOMAINS='["golem.example.com"]' on the registry service, so that no environment can register it.
To give the worker service the public scheme and host, set GOLEM__ROUTE_RESOLVER__TRUSTED_INGRESS_ADDRESSES to the address range of the proxy that connects to it (/32 for one address). The proxy must replace the client’s X-Forwarded-Proto and X-Forwarded-Host with one value each. A request from a trusted address without both headers fails. A single proxy needs a fixed address: give it a static IP address, or run it next to the worker service and trust 127.0.0.1/32. For an ingress that runs as several pods, trust its pod range and admit only the ingress with a network policy; see Configuring ingress. See the production checklist.
Moving from the all-in-one server
A fixed cluster can take over the database and the bucket of an all-in-one server on Postgres:
-
Stop the all-in-one server.
-
Give the cluster the same database, schemas, bucket,
namespace_prefixes, secrets and admin token. -
Give the registry service the IDs of the initial accounts and of the plan of the all-in-one server. The two configurations use different IDs, and the registry service does not start when an account with the same email address has another ID:
GOLEM__INITIAL_PLANS__DEFAULT__PLAN_ID=e808bd76-a6ab-4090-ade4-8447b8e8550f GOLEM__INITIAL_ACCOUNTS__ROOT__ID=51de7d7d-f286-49aa-b79a-96022f7e2df9 GOLEM__INITIAL_ACCOUNTS__ROOT__PLAN_ID=e808bd76-a6ab-4090-ade4-8447b8e8550f GOLEM__INITIAL_ACCOUNTS__BUILTIN_PLUGIN_OWNER__ID=b0a654af-d67f-4d73-a824-cf75e122bfc0 GOLEM__INITIAL_ACCOUNTS__BUILTIN_PLUGIN_OWNER__PLAN_ID=e808bd76-a6ab-4090-ade4-8447b8e8550f GOLEM__INITIAL_ACCOUNTS__BUILTIN_TOOL_OWNER__PLAN_ID=e808bd76-a6ab-4090-ade4-8447b8e8550f GOLEM__INITIAL_ACCOUNTS__MARKETING__PLAN_ID=e808bd76-a6ab-4090-ade4-8447b8e8550f -
Start the cluster with the shard manager in Postgres mode on the
golem_shard_managerschema, so that the shard epochs continue.
The default plan then takes the limits of the cluster’s configuration.