Skip to main content

Server Configuration Reference

Complete reference for Miren server configuration. Settings can be specified via config file, environment variables, or CLI flags.

Configuration Precedence​

Settings are resolved in this order (highest priority first):

  1. CLI flags — e.g. --address :9443
  2. Environment variables — e.g. MIREN_SERVER_ADDRESS=:9443
  3. Config file — server.toml
  4. Defaults

Config File​

The server reads its config from the first file found:

  1. Path specified via --config
  2. /etc/miren/server.toml
  3. {data_path}/config/server.toml (default: /var/lib/miren/config/server.toml)

Example​

mode = "standalone"

[server]
address = ":8443"
data_path = "/var/lib/miren"
http_request_timeout = 60

[ingress]
mode = "tls-autoprovision"

[tls]
acme_email = "admin@example.com"

[etcd]
start_embedded = true

[buildkit]
gc_keep_storage = "20GB"
gc_keep_duration = "14d"

Server Modes​

Miren has two operating modes:

ModeDescription
standaloneAll components (etcd, containerd, buildkit, logs, metrics) run embedded within a single process. This is the default.
distributedComponents run as separate services. Experimental.

In standalone mode, embedded services start automatically unless explicitly disabled.

Coordinator metadata​

On startup, the coordinator imports existing small metadata into etcd. This includes the cluster CA and API credentials, cloud registration, workload identity keys and anchor history, OIDC session signing key, ACME accounts and certificates, secret keyring, and runner-fleet upgrade checkpoints. Ordinary standalone servers use this storage; redundancy is not required.

Etcd is authoritative after migration. Local credential files are recoverable caches, except for the cluster sealing key described below. Registration commands and manual workload-key changes still take effect on the next coordinator start; the coordinator fingerprints its caches to distinguish deliberate changes from unchanged files or a restore directory.

Registration and workload identity are restored before their boot consumers run, including telemetry and cloud storage clients. Credential caches are published atomically; the CA's private ca.bundle keeps its certificate and key in one bootstrap generation, with separate PEM files retained for compatibility.

An etcd snapshot is not a full data-path backup. Server configuration, node-local executor state, SQLite databases, OCI images, application disks, staged disk transfers, build data, logs, and metrics remain outside etcd and need their own backup strategy. Keep the same server configuration when restoring, including the etcd prefix and workload identity hostname settings.

Flannel cleans its etcd registry keys with path.Join. For example, runners may have /miren//sub/flannel saved in their configuration, but their actual leases and network config remain under /miren/sub/flannel/. RBAC covers that cleaned key range; existing runners do not need to rejoin. Keep the configured etcd prefix unchanged during the upgrade because other stores also depend on it.

Back up the cluster sealing key separately

On first migration, Miren generates a random 32-byte key at <data-path>/server/cluster-sealing.key, with permissions 0600. This host-only key seals the CA, the secret keyring, and all other coordinator metadata in etcd. It is never stored in etcd or sent to Miren Cloud. An etcd snapshot alone cannot decrypt this metadata or mint cluster identities.

Back up this key to a separate, restricted location, not alongside snapshots uploaded to Cloud. Losing both the host key and its backup makes encrypted metadata and secrets unrecoverable. Miren does not regenerate a missing key when metadata already exists; there is no automatic sealing-key rotation or KMS recovery yet.

To restore on another host, restore the etcd snapshot and place the original key at <data-path>/server/cluster-sealing.key with permissions 0600 before starting the coordinator. No other original metadata files are required. Keep the same server configuration and etcd prefix. A wrong or missing key fails startup instead of overwriting the restored metadata.

Protect snapshots with restricted access as well: application/entity data is not all encrypted. Migration completes only after a successful coordinator startup; retain existing filesystem backups until then. The sealing key remains required for every later backup and restore.

Top-Level Fields​

FieldTypeDefaultEnv VarCLI Flag
modestringstandaloneMIREN_MODE--mode, -m
labsstring[][]MIREN_LABS--labs

[server] — Core Settings​

FieldTypeDefaultDescriptionEnv VarCLI Flag
addressstring:8443Address to listen on (host:port)MIREN_SERVER_ADDRESS--address, -a
runner_addressstringlocalhost:8444Runner address (host:port)MIREN_SERVER_RUNNER_ADDRESS--runner-address
data_pathstring/var/lib/mirenRoot data directoryMIREN_SERVER_DATA_PATH--data-path, -d
runner_idstringmirenRunner identifierMIREN_SERVER_RUNNER_ID--runner-id, -r
release_pathstring—Path to release directory containing binariesMIREN_SERVER_RELEASE_PATH--release-path
config_cluster_namestringlocalName for this cluster in client config and as the telemetry label fallback when it is not registered with Miren CloudMIREN_SERVER_CONFIG_CLUSTER_NAME--config-cluster-name, -C
skip_client_configboolfalseSkip writing client config to clientconfig.dMIREN_SERVER_SKIP_CLIENT_CONFIG--skip-client-config
http_request_timeoutint60HTTP request timeout in seconds (minimum: 1). Cluster-wide default; override per route with miren route timeout — see Request TimeoutsMIREN_SERVER_HTTP_REQUEST_TIMEOUT--http-request-timeout
stop_sandboxes_on_shutdownboolfalseStop all sandboxes when server shuts down (useful in development)MIREN_SERVER_STOP_SANDBOXES_ON_SHUTDOWN--stop-sandboxes-on-shutdown

[ingress] — Ingress Settings​

Selects the deployment shape for Miren's HTTP/HTTPS ingress. The mode determines where Miren listens and whether it terminates TLS. See TLS for cert sourcing under each mode.

FieldTypeDefaultDescriptionEnv VarCLI Flag
modestringtls-autoprovisionIngress mode: tls-autoprovision, behind-proxy-http, or behind-proxy-httpsMIREN_INGRESS_MODE--ingress-mode
addressstring—Optional bind override (full host:port). Replaces the mode's default bind entirely. Ignored under tls-autoprovision.MIREN_INGRESS_ADDRESS--ingress-address
trusted_proxy_hopsint1Number of trusted proxies immediately in front of Miren. Used to select the visitor address from X-Forwarded-For under behind-proxy-http.MIREN_INGRESS_TRUSTED_PROXY_HOPS--ingress-trusted-proxy-hops
error_pagestring—Absolute path to a cluster-wide HTML error template. Loaded at server startup.MIREN_INGRESS_ERROR_PAGE—

Custom error pages​

Set error_page = "/etc/miren/error.html" under [ingress] to replace the built-in HTML page for the cluster. The file must exist and be a valid Go html/template (up to 128 KiB), or ingress will fail to start. Restart the server after changing it. For an app-specific override, see app.toml static error pages.

Templates receive .Status (HTTP status number), .Title, .Description, .Site (visitor hostname on maintenance pages), .Reason, .BackAt, and .Maintenance (boolean). {{brandLogo}} renders the built-in Miren logo. Use self-contained markup and inline CSS if the page must work when the app is unavailable. Only HTML responses use these templates: Accept negotiation still selects JSON or plain text for API clients. If the app's template is missing or cannot render, ingress falls back to the cluster template, then to the built-in page. Errors without a resolved app use the cluster template.

For ordinary errors, .Status, .Title, and .Description are set; .Site, .Reason, and .BackAt are empty. During maintenance, .Status is 503, .Maintenance is true, .Site is the visitor's hostname, .Reason is the operator's message, and .BackAt is a formatted UTC time when provided. .Title and .Description are empty on maintenance pages. These are the only template data fields; app IDs, raw failure messages, and request details are not exposed. HTML escaping is automatic. Templates must be at most 128 KiB; rendered output is capped at 256 KiB and falls back if it exceeds that limit. App templates are also checked at deployment: they may use if and with, but not define, block, template, or range actions. Only brandLogo, eq, ne, lt, le, gt, ge, and, or, not, and len are available as functions. This prevents expressions from growing without writing output in the shared ingress process. Cluster templates may use the full Go template syntax.

Copy this into /etc/miren/error.html for a cluster template, or into the app's static.dir output for an app template. Replace “Example” with your brand. It requires no external assets, so it still works during an outage:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{if .Maintenance}}Maintenance{{else}}{{.Status}} · {{.Title}}{{end}} — Example</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; min-height: 100vh; background: #fdfaf2; color: #1b1f27;
font-family: system-ui, sans-serif; }
.page { min-height: 100vh; display: flex; flex-direction: column;
padding: 0 clamp(24px, 7vw, 112px); }
header, footer { padding: 28px 0; border-bottom: 1px solid #eadfd6; }
header { color: #0059ff; font-size: 24px; font-weight: 700; }
footer { border-top: 1px solid #eadfd6; border-bottom: 0; color: #656b76; }
main { flex: 1; display: flex; align-items: center; padding: 64px 0; }
.content { max-width: 760px; }
.label { color: #545868; font-size: 13px; font-weight: 700;
letter-spacing: .14em; text-transform: uppercase; }
h1 { font-size: clamp(40px, 6vw, 72px); line-height: 1.08;
letter-spacing: -.04em; overflow-wrap: anywhere; }
.detail { color: #545868; font-size: 20px; line-height: 1.6; }
@media (prefers-color-scheme: dark) {
body { background: #151a23; color: #f4f5f5; }
header, footer { border-color: #393e48; }
.label, .detail, footer { color: #b6bac1; }
}
</style>
</head>
<body>
<div class="page">
<header>Example</header>
<main><div class="content">
<div class="label">{{if .Maintenance}}Maintenance{{else}}Error {{.Status}}{{end}}</div>
{{if .Maintenance}}
<h1>{{if .Site}}{{.Site}} is down for maintenance{{else}}Down for maintenance{{end}}</h1>
{{if .Reason}}<p class="detail">{{.Reason}}</p>{{else}}<p class="detail">Please check back soon.</p>{{end}}
{{if .BackAt}}<p class="detail">Expected back at {{.BackAt}}.</p>{{end}}
{{else}}
<h1>{{.Title}}</h1>
<p class="detail">{{.Description}}</p>
{{end}}
</div></main>
<footer>Example</footer>
</div>
</body>
</html>

Modes​

ModeDefault bindTLS terminatedCert source
tls-autoprovision (default)0.0.0.0:443 plus :80 for redirect / HTTP-01 ACMEyes[tls] (ACME or self-signed)
behind-proxy-http127.0.0.1:80non/a
behind-proxy-https127.0.0.1:443yes[tls] (self-signed or DNS-01 ACME)

Only behind-proxy-http trusts X-Forwarded-Proto / Forwarded from the peer; the proxy must set it. It also uses X-Forwarded-For for access logs, selecting the address immediately before the configured number of trusted proxy hops from the right. The other modes derive the scheme and visitor address from the connection itself.

The behind-proxy-* modes default to localhost to keep accidental misconfigurations from quietly exposing an internal endpoint to the network. Set ingress.address = "0.0.0.0:80" (or similar) explicitly when the proxy is on a different host.

Widening behind-proxy-http off loopback

Under behind-proxy-http, Miren trusts X-Forwarded-Proto from every connection to the listener; it does not check the peer address. If you bind to 0.0.0.0, [::], or any non-loopback address, a firewall or security group must restrict that port to the proxy's addresses. Any other client that can reach it directly can send X-Forwarded-Proto: http and be issued auth cookies without the Secure flag.

Unix socket addresses

unix:/path is reserved for a future release and rejected today with a clear error.

[tls] — TLS Settings​

Settings under [tls] cover two kinds of certs. acme_email, acme_dns_provider, and self_signed configure the ingress cert and only apply when Miren terminates TLS (tls-autoprovision or behind-proxy-https); they're rejected at startup under behind-proxy-http. additional_names and additional_ips are different: they extend the SANs on the API server and etcd certs, which exist regardless of ingress mode, so they're valid under any mode. Under tls-autoprovision, additional_names entries can also get ingress certs. See TLS for how, and for setup guides.

additional_ips does more than its name suggests. Alongside adding SANs, every address listed there is passed straight through to the addresses the server advertises to Miren Cloud, skipping the filtering that discovered addresses go through. That makes it the way to pin an address discovery gets wrong — a host behind a static NAT, or one where you want a specific interface used. See Running Miren on a Tailnet for a worked example, and run miren debug advertise on the host to see what discovery decided and why.

FieldTypeDefaultDescriptionEnv VarCLI Flag
additional_namesstring[][]Extra DNS names for the server certificate, and for the ingress certificate under autoprovisioningMIREN_TLS_ADDITIONAL_NAMES--dns-names
additional_ipsstring[][]Extra IPs for the server certificate, and forced into the advertised address listMIREN_TLS_ADDITIONAL_IPS--ips
acme_dns_providerstring—DNS provider for ACME DNS-01 challenges (e.g. cloudflare, route53). Required under behind-proxy-https if not using self_signed.MIREN_TLS_ACME_DNS_PROVIDER--acme-dns-provider
acme_emailstring—Email for ACME account registrationMIREN_TLS_ACME_EMAIL--acme-email
self_signedboolfalseUse self-signed certificates (development only, or behind a TLS-terminating proxy that doesn't verify)MIREN_TLS_SELF_SIGNED--self-signed-tls

[etcd] — Etcd Settings​

Miren uses etcd as its entity store. In standalone mode, an embedded etcd server starts automatically.

FieldTypeDefaultDescriptionEnv VarCLI Flag
endpointsstring[][]Etcd endpoints (auto-configured when embedded)MIREN_ETCD_ENDPOINTS--etcd, -e
prefixstring/mirenKey prefix in etcdMIREN_ETCD_PREFIX--etcd-prefix, -p
start_embeddedbooltrue*Start embedded etcd serverMIREN_ETCD_START_EMBEDDED--start-etcd
client_portint12379Embedded etcd client portMIREN_ETCD_CLIENT_PORT--etcd-client-port
peer_portint12380Embedded etcd peer portMIREN_ETCD_PEER_PORT--etcd-peer-port
http_client_portint12381Embedded etcd HTTP client port (bound to loopback)MIREN_ETCD_HTTP_CLIENT_PORT--etcd-http-client-port

* Defaults to true in standalone mode only.

Embedded etcd always comes up with mTLS

client_port binds to all interfaces since embedded etcd always starts with mTLS. peer_port and http_client_port bind to 127.0.0.1. etcd's JSON gateway is disabled outright; its health and metrics endpoints are unaffected.

Before importing coordinator credentials, Miren enables etcd certificate-identity authorization. The coordinator's etcd-client-coordinator certificate has administrative access; enrolled runners are restricted to <prefix>/sub/flannel/ and cannot read or modify coordinator credentials. Existing runner identities are provisioned on startup. Direct administrative etcd clients must use the coordinator certificate and key under <data-path>/etcd-certs.

Before upgrading an existing coordinator, check that every runner certificate's CommonName is runner-<runner_id>. Re-provision runners with a different name using miren runner remove and miren runner join with the same runner ID before the coordinator upgrade; otherwise their etcd networking may stop when auth is enabled. Certificate refresh also provisions a verified legacy CommonName when it can still be mapped to a registered node, but this does not repair workload token authorization or guarantee uninterrupted networking during the upgrade.

For a downgrade to a binary predating etcd authorization, restore its pre-upgrade etcd snapshot using the managed rollback path. An in-place binary downgrade leaves authorization enabled: older maintenance clients and newly joined runners do not have the required etcd identities.

This automatic authorization setup applies to embedded mTLS etcd. If you use external etcd, you are responsible for isolating and protecting that server; its contents include the cluster's private credentials.

[containerd] — Containerd Settings​

FieldTypeDefaultDescriptionEnv VarCLI Flag
start_embeddedbooltrue*Start embedded containerd daemonMIREN_CONTAINERD_START_EMBEDDED--start-containerd
binary_pathstringcontainerdPath to containerd binaryMIREN_CONTAINERD_BINARY_PATH--containerd-binary
socket_pathstring—Path to containerd socketMIREN_CONTAINERD_SOCKET_PATH--containerd-socket

* Defaults to true in standalone mode only.

[buildkit] — BuildKit Settings​

Controls the BuildKit daemon used for building container images.

FieldTypeDefaultDescriptionEnv VarCLI Flag
start_embeddedbooltrue*Start embedded BuildKit daemonMIREN_BUILDKIT_START_EMBEDDED--start-buildkit
socket_pathstring—Path to external BuildKit socket (distributed mode)MIREN_BUILDKIT_SOCKET_PATH--buildkit-socket
socket_dirstring—Directory for embedded BuildKit socketMIREN_BUILDKIT_SOCKET_DIR--buildkit-socket-dir
gc_keep_storagestring10GBMaximum BuildKit layer cache sizeMIREN_BUILDKIT_GC_KEEP_STORAGE--buildkit-gc-storage
gc_keep_durationstring7dHow long to keep cache entriesMIREN_BUILDKIT_GC_KEEP_DURATION--buildkit-gc-duration

* Defaults to true in standalone mode only.

[victorialogs] — Log Storage Settings​

Controls the embedded VictoriaLogs instance used for application log storage.

FieldTypeDefaultDescriptionEnv VarCLI Flag
start_embeddedbooltrue*Start embedded VictoriaLogs serverMIREN_VICTORIALOGS_START_EMBEDDED--start-victorialogs
http_portint9428HTTP port in embedded modeMIREN_VICTORIALOGS_HTTP_PORT--victorialogs-http-port
retention_periodstring30dRetention period (e.g. 30d, 2w, 1y)MIREN_VICTORIALOGS_RETENTION_PERIOD--victorialogs-retention
addressstringvictorialogs:9428Address when not using embeddedMIREN_VICTORIALOGS_ADDRESS--victorialogs-addr

* Defaults to true in standalone mode only.

Miren snapshots embedded VictoriaLogs data before image upgrades. See VictoriaLogs upgrade and rollback for backup retention and rollback details. External VictoriaLogs is not managed by Miren.

[victoriametrics] — Metrics Storage Settings​

Controls the embedded VictoriaMetrics instance used for Miren's own runtime metrics. Managed application metrics are sent directly to their remote-write destination and are not copied into this instance.

FieldTypeDefaultDescriptionEnv VarCLI Flag
start_embeddedbooltrue*Start embedded VictoriaMetrics serverMIREN_VICTORIAMETRICS_START_EMBEDDED--start-victoriametrics
http_portint8428HTTP port in embedded modeMIREN_VICTORIAMETRICS_HTTP_PORT--victoriametrics-http-port
retention_periodstring1Retention period in monthsMIREN_VICTORIAMETRICS_RETENTION_PERIOD--victoriametrics-retention
addressstringvictoriametrics:8428Address when not using embeddedMIREN_VICTORIAMETRICS_ADDRESS--victoriametrics-addr

* Defaults to true in standalone mode only.

[telemetry.metrics] — Managed Application Metrics​

Configures the Prometheus Remote Write destination for services that enable managed metrics in app.toml. When this section is absent, Miren does not start vmagent and does not scrape application endpoints.

[telemetry.metrics]
remote_write_url = "https://metrics.example.com/api/v1/write"
workload_identity_audience = "metrics.example.com"
FieldTypeDefaultDescriptionEnv VarCLI Flag
remote_write_urlstring—Absolute HTTP or HTTPS remote-write endpointMIREN_TELEMETRY_METRICS_REMOTE_WRITE_URL--telemetry-metrics-remote-write-url
workload_identity_audiencestring—Audience for the short-lived system:telemetrywriter bearer tokenMIREN_TELEMETRY_METRICS_AUDIENCE--telemetry-metrics-audience
Remote-write requirements

Both fields must be set together. URLs containing credentials are rejected; authentication always uses the cluster's workload identity.

The coordinator runs one supervised vmagent and keeps its on-disk retry queue under <data_path>/app-metrics.

Cluster label selection

Registered clusters use their Miren Cloud cluster ID for the miren_cluster label. Other clusters use server.config_cluster_name, so set it to a stable name when several clusters write to the same destination.

Moved from metrics.remote_write

Before this section existed, the same settings lived under [metrics.remote_write] as url and workload_identity_audience (MIREN_METRICS_REMOTE_WRITE_URL, MIREN_METRICS_REMOTE_WRITE_AUDIENCE). Those still work and log a deprecation warning at startup. If both the old and new key are set, they must agree.

[telemetry.traces] — Trace Export​

Configures where Miren sends its own OpenTelemetry traces and how it authenticates there.

[telemetry.traces]
endpoint = "https://traces.example.com"
workload_identity_audience = "traces.example.com"
FieldTypeDefaultDescriptionEnv VarCLI Flag
endpointstringOTEL_EXPORTER_OTLP_ENDPOINTOTLP/HTTP base URL; Miren appends /v1/tracesMIREN_TELEMETRY_TRACES_ENDPOINT--telemetry-traces-endpoint
workload_identity_audiencestring—Authenticate with a short-lived system:telemetrywriter bearer token for this audienceMIREN_TELEMETRY_TRACES_AUDIENCE--telemetry-traces-audience

When endpoint is unset, Miren falls back to the standard OTEL_EXPORTER_OTLP_ENDPOINT environment variable. With neither, Miren exports no traces.

Unlike metrics, identity is optional here. Without an audience, Miren sends whatever static headers OTEL_EXPORTER_OTLP_HEADERS holds, which suits hosted backends that take an API key. With an audience, the token replaces any Authorization header from the environment (Miren warns once at startup), and other headers still go through. The collector verifies the token with OIDC discovery against the cluster's issuer, so it needs no shared secret.

Trace export requirements

An audience needs an endpoint from one of the two sources. URLs containing credentials are rejected. With an audience set, Miren's exporter no longer reads OTEL_EXPORTER_OTLP_CERTIFICATE, so a collector behind a private CA is not supported in that mode.

[app_version] — Version Retention​

Every deploy creates a new version of an app, and Miren keeps a bounded history of them rather than retaining every version forever. Pruning old versions frees the disk space their container images take up and keeps the server's per-app state from growing with every deploy.

A version is retained if it is among the most recent retention_count or newer than retention_period — whichever rule keeps it. The two settings are a floor, not a budget: raising either one keeps more versions. The currently active version is always retained regardless of these limits, and ephemeral (preview) versions are managed separately by their own TTL.

On a frequently-deployed cluster this window can pin more image data than the disk can hold, so Miren tightens retention automatically under pressure: once storage reaches 80%, a sweep drops the retention_period floor and keeps the active version plus retention_count (plus anything still in use). That makes retention_count a hard floor, always honored, while retention_period is best-effort and yields when the disk is tight. See Managing Disk Space for how the reclaim works end to end and what to tune.

FieldTypeDefaultDescriptionEnv VarCLI Flag
retention_countint10Most-recent versions to keep per app, regardless of ageMIREN_APP_VERSION_RETENTION_COUNT--app-version-retention-count
retention_periodstring30dKeep versions newer than this, regardless of count (e.g. 30d, 2w)MIREN_APP_VERSION_RETENTION_PERIOD--app-version-retention-period

[deployment] — Deployment History Retention​

Every deploy, rollback, and config change writes a deployment record, and miren app history reads them back. Miren keeps a bounded window of these per app rather than every record forever. The records are small, but a cluster that runs for years accumulates thousands, and every history lookup and reconciliation pass pays to scan them.

A record is retained if it is among the most recent retention_count for its app or newer than retention_period — whichever rule keeps it. A few records are always kept regardless of these limits: the deployment that made the app's current version active, any deployment still in progress or holding the app's deploy lock, and any older record whose status still reads active. Pruning a record does not touch the app version it produced; versions have their own retention.

Clusters registered with Miren Cloud keep their full history there. Cloud stores deployments as an archive, so a record pruned here stays visible in cloud, and the runtime only prunes a record once cloud has confirmed it holds it. If the cluster cannot reach cloud, eligible records simply wait; nothing is lost while the link is down. On a cluster that runs without cloud, this window is the history, so size it to how far back you want miren app history to reach.

FieldTypeDefaultDescriptionEnv VarCLI Flag
retention_countint25Most-recent deployment records to keep per app, regardless of ageMIREN_DEPLOYMENT_RETENTION_COUNT--deployment-retention-count
retention_periodstring30dKeep records newer than this, regardless of count (e.g. 30d, 2w). 0 keeps every record indefinitelyMIREN_DEPLOYMENT_RETENTION_PERIOD--deployment-retention-period

[saga] — Saga Execution Retention​

Miren records a saga execution for multi-step operations like creating a sandbox or running a build, so that a server crash mid-operation can resume or roll back cleanly instead of leaving things half-done. Each record holds the outputs of every step it ran.

Once an execution finishes, that record is only useful for looking back at what happened, so Miren deletes it after retention_period. Successes and failures are treated the same way. An execution that is still in progress is never deleted at any age, including one stuck retrying a rollback, since those are exactly what the server needs to recover.

That leaves one gap, which Miren closes on its own. An execution can end up with nothing driving it and no way for anything to find it again, and because it never reaches a finished state, retention never considers it either. It would sit in the store forever, and every recovery pass would pay to read it. So an execution that has gone a week without changing state is marked failed, which is both the honest description of it and what lets the ordinary rules take over: whoever owns the operation can retry it, and retention collects the record a retention_period later. There's no separate setting for this — a week is far longer than the gap between two steps of a saga that's actually running, and anything the server is working on, or retrying on a loop, stays well clear of it.

Indefinite retention grows without bound

Setting retention_period to 0 freezes saga records: nothing is deleted, and nothing in progress is marked failed either. That's useful while investigating an incident, but a cluster where one app repeatedly fails to start can write thousands of executions a day, so it's worth putting back afterward.

FieldTypeDefaultDescriptionEnv VarCLI Flag
retention_periodstring7dDelete finished saga executions older than this (e.g. 7d, 24h). 0 freezes saga records entirelyMIREN_SAGA_RETENTION_PERIOD--saga-retention-period

Workload Identity Anchor​

The anchor is the iss claim in the tokens this cluster mints for its apps, and the address an outside verifier fetches its public keys from. The signing key is generated on the cluster and never leaves it either way — the anchor decides who serves the keys, not who holds them.

There is no configuration field for it, because it is a property of the registration rather than of the server. A cluster registered with Miren Cloud is anchored there, and Miren Cloud serves its discovery; a cluster installed with --without-cloud anchors at its own hostname and serves its own.

To change it on a registered cluster, use miren server identity-anchor, which handles the restart and the verification overlap that keeps in-flight tokens working. See Moving the anchor.