Production checklist

The settings of myriarchd serve that matter for security, what the server refuses at startup, and how to check a configuration without starting it.

myriarchd serve (and the Helm chart deploy/helm/myriarch, which renders the same configuration) checks its configuration at startup and refuses unsafe values. This page lists what is refused, the settings you must decide for a real installation, and how to check a configuration before you roll it out. The reasoning behind the controls is in the security model; how people sign in is in Authentication. A complete, validated example is deploy/examples/production/myriarchd.yaml.

Refused at startup

In production mode the server does not start when one of these holds. Each message names the setting.

SettingRule
server.publicUrlRequired, and an https URL (session cookies are Secure).
server.tls.terminatedUpstreamMust be true when myriarchd serves plain HTTP (no server.tls.certFile). It confirms that TLS ends in front of the server and that the plain port is reachable only from there.
database.dsn or database.dsnFileRequired.
authAt least one login path: auth.oidc or auth.bootstrapAdmin with passwordHashFile or tokenFile. The development login is never accepted.
auth.devLogin.enabled, auth.cookieSecureThe development login must be off and cookieSecure must stay true.
opamp.requireEnrollmentTokenMay be false only together with opamp.requireClientCert: true (pure mTLS).
opamp.enableCompressionRefused unless opamp.allowUnboundedCompression: true acknowledges that decompressed messages are not bounded by opamp.maxMessageSize.
artifacts.verify.requireChecksum, requireSignatureMust stay true unless artifacts.allowUnverified: true (lab use).
install.grantTtl, install.bundleSourceBaseUrlWhile install.enabled is true (the default): the grant lifetime must be positive and at most artifacts.downloadTokenTtl, and the bundle source an https URL without credentials, query or fragment (an empty value is refused too: leave the setting out for the default). A configuration that sets artifacts.downloadTokenTtl below one hour and leaves install.grantTtl out gets that shorter lifetime as the default; a value that you set is checked as written. Available from v0.2.0.
URLsartifacts.sourceBaseUrl, releases.githubApiUrl, releases.rawBaseUrl and notification URLs must be https. gitops.repoUrl must be https or ssh; dir: and local paths are for development only, and the Git URL must not carry inline credentials (use gitops.auth.tokenFile or an SSH key).
notifications.channels[]A channel of type webhook needs secretFile (at least 32 bytes).

Settings to decide

SettingMeaning
server.tls.terminatedUpstream: trueThe ingress or load balancer terminates TLS 1.2 or newer, and only trusted networks reach the plain port. With the Helm chart these are the sources of its NetworkPolicy and, by default, every pod of the release namespace (networkPolicy.ingress.allowSameNamespace). Alternatively set server.tls.certFile and keyFile and let myriarchd terminate TLS.
server.trustedProxiesCIDRs of the ingress or gateway pods, whose X-Forwarded-* headers are honored. Without them every client looks like the proxy: per-address rate limits, audit source addresses and the brute-force protection then count the proxy address for everyone. The Helm chart requires it while an ingress is enabled. Production refuses ranges that cover every address (0.0.0.0/0, ::/0); a range shorter than /8 (IPv4) or /32 (IPv6) is a warning, and is refused while opamp.clientCertHeader is set.
server.metricsListenAddrDefault :9090 serves /metrics (and /healthz) on a separate port without authentication. Restrict it by network (the chart’s NetworkPolicy does). An empty value serves /metrics on the main listener, which an ingress usually exposes; production logs a warning, so block /metrics at the ingress then.
auth.oidc.roleMappingMaps groups or roles of the identity provider to admin, operator and viewer. Deny by default: users without a mapped group get no access, and auth.oidc.defaultRole stays empty.
auth.oidc.clientSecretFile or clientCertificateFile + clientKeyFileClient authentication at the token endpoint: a secret, or a certificate (private_key_jwt, RSA key in PKCS#1 or PKCS#8 form). Exactly one of the two.
auth.sessionTtl, auth.sessionMaxAgeIdle timeout (default 2 h, sliding) and absolute session lifetime (default 12 h).
auth.apiTokenMaxTtlMaximum lifetime of API tokens (default 90 days). Removing a user at the identity provider does not end the API tokens of that user; disable the user in myriarch for that (Authentication). 0 means unlimited and logs a warning.
auth.bootstrapAdminThe break-glass administrator. Keep it for emergencies with the credential in a vault, or turn it off after single sign-on works; with OIDC enabled it is off unless keepWithOidc: true.
auth.downloadTokenSecretFileA fixed HMAC key (32 or more random bytes) for artifact download tokens. By default one is generated and stored in the database. artifacts.downloadTokenTtl (default 24 h, must be positive in production) limits their age.
opamp.requireEnrollmentToken, opamp.requireClientCertBy default every agent needs an enrollment token. Client certificates (mTLS) need server.tls.clientCaFile, and either server.tls.certFile or opamp.clientCertHeader from a trusted proxy.
opamp.autoRebindOnTokenRotationDefault false: an agent whose bound token was revoked needs POST /api/v1/agents/{id}/actions/reenroll. true lets it move to a valid new token itself (audit event agent.rebind).
executors.allowedWebhookHostsPermitted webhook and AWX targets. An empty list refuses every target (fail closed, in every mode) and does not block startup. Secrets reach executors only through MYRIARCH_EXEC_* environment variables, executors.secretDir or executors.secrets, and each secret must be bound to its destination hosts (executors.secretEnvHosts, executors.secretFileHosts, or allowedHosts in executors.secrets).
outbound.allowedCidrsOpens internal destinations (Git server, AWX, mirror, identity provider) for outbound connections. Loopback, private ranges, link-local and cloud metadata addresses, and unique-local addresses are blocked otherwise.
outbound.caFileA PEM file with additional trusted CAs, for all outbound TLS connections: OIDC, Git, GitHub, the artifact mirror, TUF, executors and notifications. The server does not start if the file is missing or holds no certificate. Proxies come from the environment of the process: HTTPS_PROXY, NO_PROXY.
artifacts.sourceBaseUrl, artifacts.sourceAuthAn internal mirror of the release downloads (layout <base>/<tag>/<asset>) and its credentials: tokenFile (bearer) or username + passwordFile (basic). Credentials go only to the origin (scheme, host, port) of sourceBaseUrl, never to redirect targets on other hosts, and the files are re-read when they change. artifacts.allowedRedirectHosts lists the hosts a mirror may redirect to.
artifacts.verify.tufRepositoryUrl, artifacts.verify.trustedRootFileThe Sigstore trust base behind a proxy. The default is tuf-repo-cdn.sigstore.dev over the proxy. Use a 1:1 TUF mirror (https; do not cache timestamp.json for long) or, as a fallback, a fixed trusted_root.json.
artifacts.agentBaseUrlsBase URLs that updaters download from, chosen per updater by host and port. http:// entries need artifacts.allowInsecureAgentBaseUrls: true and belong only to cluster-internal services.
install.enabled, install.grantTtl, install.platformsAvailable from v0.2.0. The installation of hosts from the server (the plan for a host, the install grants for its downloads, the installer bundles and the commands that the web UI renders, Server behaviour) is on by default. Set install.enabled: false where hosts are installed on the manual path only: the routes then answer 404 and the server fetches no bundle. install.grantTtl (default 1 h, or artifacts.downloadTokenTtl when you left the setting out and that is shorter) is the lifetime of the grants of a plan, install.platforms lists the platforms. The routes need an enrollment token whatever opamp.requireEnrollmentToken says, and the plan names only releases that the release tracker knows (releases.enabled).
install.bundleSourceBaseUrlAvailable from v0.2.0. Where every replica of a server that runs a release fetches the installer bundle of its release (the updater archive with the installer scripts), shortly after the start: the GitHub releases of the project by default, an internal mirror with the layout <base>/v<version>/<asset> (with checksums.txt, checksums.txt.sigstore.json and the myriarch-updater_<version>_<os>_<arch> archives of the release) in an air gap. It is read without credentials, through the outbound policy, artifacts.proxyUrl and artifacts.allowedRedirectHosts, and what it delivers is used only after the signature of checksums.txt was verified against the release workflow of the project (the trust of artifacts.verify). The bundles take about 25 MB of artifacts.dir for each release and replica. A server that cannot reach the source logs a warning, shows the bundles as failed and cannot give the commands until it can. A build from a Git checkout has no bundle.
notifications.channels[].secretFileHMAC key (at least 32 bytes) for webhook channels. Receivers check the timestamp and drop repeated X-Myriarch-Delivery values.
gitops.auth, gitops.webhookSecretFileRead-only credentials for the repository (token, or SSH key plus known hosts) and the shared secret for POST /api/v1/gitops/webhook.
database.dsnFileThe connection string with sslmode=verify-full (or verify-ca): require encrypts but does not verify the server, and production warns about a remote database without verification. No connection pooler in transaction mode.
artifacts.dir, gitops.cloneDirDirectories that only the server account can write. Empty values use fixed names in the temporary directory, which production reports as a warning.
CertificatesThe TLS certificate, key and client CA of myriarchd are read at start: rotate them with a (rolling) restart.

The defaults are restrictive on purpose: WebSocket compression off, opamp.maxMessageSize 4 MiB, download tokens valid for 24 hours, checksum and Sigstore verification on. Secret files should be readable by the server account only (mode 0400 or 0440); the server logs a warning at start for every secret file that others can read (see Authentication).

At start myriarchd logs a warning for every setting that weakens a control: a non-empty auth.oidc.defaultRole, provider-specific OIDC hints (see Authentication), the bootstrap admin as the only login path or next to OIDC, expired declarative enrollment tokens, enabled compression, empty server.trustedProxies, disabled artifact verification, plain http:// entries in artifacts.agentBaseUrls, and mirror credentials combined with the public GitHub URL, the installation from the server with the release tracker off, a metrics endpoint on the main listener, a database connection to a remote host without certificate verification, the default cache and clone directories, secrets set as literal values in the configuration file, credentials in artifacts.proxyUrl and an unlimited auth.apiTokenMaxTtl. myriarchd config validate prints the same warnings.

Check a configuration without starting the server

myriarchd config validate --config myriarchd.yaml                 # also reads the files it refers to (secrets, CA, certificates)
myriarchd config validate --config myriarchd.yaml --skip-files    # without files: for CI and `helm template`, where secrets are absent
myriarchd config print    --config myriarchd.yaml --skip-files    # the effective configuration, secrets redacted

The command loads the configuration exactly like myriarchd serve: defaults, the YAML file (unknown keys are errors), ${VAR} expansion and MYRIARCH_* environment overrides. It runs every check that serve makes before it touches the database or the network. Nothing is started and no connection is made. On success it prints OK: <file> is valid and one WARNING: <text> line per warning on standard output; each problem is a line ERROR: <path>: <message> on standard error. config print shows the effective configuration as YAML, with the DSN, client secrets, webhook secrets, notification URLs and development passwords replaced by <redacted>; it validates first. The server configuration reference lists every key with its type, default and effect.

Exit codes: 0 valid (warnings are allowed), 1 invalid (YAML syntax, an unknown key, a failed check), 2 wrong usage (no --config, a file that cannot be read, an unknown option). An example:

$ myriarchd config validate --config deploy/examples/production/myriarchd.yaml --skip-files
OK: deploy/examples/production/myriarchd.yaml is valid
$ myriarchd config validate --config broken.yaml --skip-files
ERROR: auth: no login path is configured for serve mode: enable auth.oidc (single sign-on) or auth.bootstrapAdmin with passwordHashFile or tokenFile (break-glass administrator); the dev login exists only in "myriarchd dev"

Without --skip-files, file and certificate problems appear as errors; they disappear when the secrets are mounted. Use --skip-files in CI. The options are in the myriarchd config validate reference; the other commands are in the myriarchd reference and the server start in the myriarchd serve reference.

For Kubernetes, the settings above are values of the Helm chart; see deploy/helm/myriarch/README.md and the troubleshooting page for symptoms: Troubleshooting.