Cluster Manifest Reference
The cluster manifest is the blueprint for your FrameWorks deployment. It’s a YAML file that tells the CLI how to provision infrastructure, services, and interfaces.
File Structure
Section titled “File Structure”version: v1type: clusterprofile: productionchannel: stable # optional: stable (default) or rcroot_domain: example.com # optional: used for Caddy routing and cookie domainsenv_files: - config/production.env - secrets/production.envhosts_file: clusters/production/hosts.enc.yaml # optional: SOPS-encrypted host inventorybootstrap_overlay: gitops/clusters/production/bootstrap.yaml # optional desired-state overlayhosts: {}clusters: {}wireguard: {}infrastructure: {}services: {}interfaces: {}observability: {}geoip: {}tls_bundles: {}ingress_sites: {}Top-Level Fields
Section titled “Top-Level Fields”version
Section titled “version”Manifest schema version (required).
Example:
version: v1Cluster manifest type (required).
Values:
cluster- Full multi-service deployment.edge- Single edge node deployment (use the dedicated edge manifest format for multi-node edge fleets; see below).
Example:
type: clusterprofile
Section titled “profile”Environment/profile name. This is used to form the default cluster ID during provisioning ({type}-{profile}), so choose a stable slug (e.g., production, eu-1).
Common values: development, staging, production.
Example:
profile: productionchannel
Section titled “channel”Release channel to track (optional). Defaults to stable.
Values: stable (production-ready), rc (release candidate for staging/testing).
Example:
channel: rcenv_files
Section titled “env_files”Paths to shared environment files for all services (optional). Relative to the manifest directory. Files are loaded in order. Shared env files override auto-generated environment variables, but are overridden by per-service env_file and inline config entries.
Example:
env_files: - config/production.env - secrets/production.envroot_domain
Section titled “root_domain”Base domain used by Caddy routing, public interface metadata, and production cookie-domain derivation.
Example:
root_domain: example.comhosts_file
Section titled “hosts_file”Optional path to a SOPS-encrypted host inventory. When set, frameworks cluster provision merges sensitive host fields such as external_ip, user, and WireGuard private-key material from this file before validation.
Example:
hosts_file: clusters/production/hosts.enc.yamlbootstrap_overlay
Section titled “bootstrap_overlay”Optional path to a bootstrap desired-state overlay. The CLI renders manifest-derived bootstrap state and then merges this overlay for operator-authored tenants, billing, cluster pricing, and service registry overrides that do not belong directly in the manifest.
Example:
bootstrap_overlay: gitops/clusters/production/bootstrap.yamlMap of host definitions for SSH access and mesh identity. The CLI connects to
each host via SSH using external_ip; internal service traffic uses
wireguard_ip or .internal names after Privateer starts.
Format:
hosts: <hostname>: external_ip: <public-ip> user: <ssh-user> cluster: <cluster-id> wireguard_ip: <mesh-ip> wireguard_public_key: <base64-public-key> wireguard_port: 51820 roles: [infrastructure, services] # optional labels: # optional environment: prodFields:
external_ip(required unless provided byhosts_file) - Public IP address used for SSH/bootstrap and WireGuard endpoint dialinguser(required unless provided byhosts_file) - SSH usernamecluster(optional) - Explicit cluster membership for this host. When one cluster is declared, hosts implicitly belong to itwireguard_ip(required for Privateer-covered hosts) - GitOps-owned internal mesh addresswireguard_public_key(required for Privateer-covered hosts) - Public half of the host mesh identitywireguard_port(required for Privateer-covered hosts) - Host WireGuard UDP listen portwireguard_private_key_file(optional) - On-disk private-key path for adopted-local nodeswireguard_private_key_managed(optional) - Set tofalsefor adopted-local nodes whose key is preserved on diskroles(optional) - Arbitrary tags used for human groupinglabels(optional) - Free-form metadata
clusters
Section titled “clusters”Map of cluster definitions for multi-cluster deployments. Each key is the cluster ID that will be registered in Quartermaster.
Format:
clusters: prod-platform: name: Production Platform type: central owner_tenant: frameworks roles: [control, data, analytics, support, infra, mesh, interface, observability] prod-media: name: Production Media Plane type: edge default: true platform_official: true allow_private_pull_sources: false owner_tenant: frameworks region: us-east roles: [media] pricing: model: tier_inherit required_tier_level: 0 allow_free_tier: trueFields:
name(required) - Display name for the clustertype(required) - Cluster type:central,edge, etc.region(optional) - Region identifierroles(optional) - Service roles that belong to this cluster. Used for automatic cluster resolution when services don’t have an explicitclusterfield.default(optional) - Mark as the default cluster. New tenants auto-subscribe to this cluster on signup. At most one cluster may havedefault: true.platform_official(optional) - Mark as a platform-operated cluster. Used by billing tier access and unauthenticated service discovery.allow_private_pull_sources(optional) - Capability flag allowing pull-input streams on this media cluster to use private literal source addresses such as RFC1918, ULA, or non-link-local multicast. Keep thisfalsefor platform-managed clusters and enable it only on self-hosted edges that can reach those sources. This does not place a pull stream by itself; private sources must also setallowed_cluster_ids/allowedClusters.clusterIdson the pull source.owner_tenant(optional) - Owning tenant. Useframeworksfor the system tenant, or a tenant UUID.pricing(optional) - Purser cluster pricing config. When present, provision reconciles it authoritatively. When absent, existing pricing is left untouched.model(required) -free_unmetered,metered,monthly,tier_inherit, orcustomrequired_tier_level(optional) - Minimum billing tier level (0-5)allow_free_tier(optional) - Allow free-tier tenantsdefault_quotas(optional) - Cluster pricing metadata for marketplace/self-host offers. Platform Free-tier stream/viewer caps are billing-tier entitlements, not static cluster capacity.
Role-based resolution: When a service doesn’t specify cluster, the CLI matches the service’s role (from the built-in service registry) against each cluster’s roles list. For example, foghorn has role media, so it resolves to whichever cluster lists media in its roles. Available roles: control, data, analytics, media, support, infra, mesh, interface, observability.
Resolution priority:
- Explicit
clusterfield on the service - Role match against cluster
roles - If only one cluster is defined, use it
- Auto-generated
{type}-{profile}when noclusterssection is declared
wireguard
Section titled “wireguard”WireGuard mesh configuration for backend nodes managed by Privateer
(api_mesh). Production/staging managed clusters keep this block in GitOps
and generate per-host identity with frameworks mesh wg generate.
Format:
wireguard: enabled: true mesh_cidr: 10.88.0.0/16 listen_port: 51820Fields:
enabled(required)mesh_cidr(required when enabled) - CIDR used by generated hostwireguard_ipvalueslisten_port(required when enabled) - Default UDP listen port used bymesh wg generate
Edge nodes do not join the mesh.
Infrastructure Section
Section titled “Infrastructure Section”Database and messaging infrastructure.
Version Reference: See
configin the monorepo for tested/recommended versions.
postgres
Section titled “postgres”PostgreSQL or YugabyteDB configuration.
Format:
infrastructure: postgres: enabled: true engine: postgres mode: native version: "15.14" host: central-01 port: 5432 databases: - name: quartermaster owner: quartermaster_userFields:
enabled(required)engine(optional) -postgresby default, oryugabytemode(required) -nativeversion(required)host(required for single-node Postgres) - Host name fromhostsnodes(required for multi-node YugabyteDB) - List of{host, id, rpc_port}entriesport(required)replication_factor(optional) - Defaults to the number ofnodesdatabases(optional)tuning(optional) - key/value overrides for server tuningsql_access(optional) -directby default, orsshpassword(optional) - Database password override
clickhouse
Section titled “clickhouse”ClickHouse analytics database.
Format:
infrastructure: clickhouse: enabled: true mode: native version: "25.9.2.1" host: central-01 port: 9000 databases: [periscope]Fields:
enabled(required)mode(required) -nativeversion(required)host(required)port(required)databases(optional)sql_access(optional) -directby default, orssh
Redis or Valkey cache instances. Use named instances when services need separate logical caches.
Format:
infrastructure: redis: enabled: true engine: valkey mode: native version: "8.1" instances: - name: foghorn host: central-01 port: 6379 - name: chatwoot engine: redis host: central-01 port: 6380Fields:
enabled(required)engine(optional) - Default engine for instances,valkeyorredismode(required) -dockerornativeversion(optional)instances(required)name(required)engine(optional) - Instance-level engine overridehost(required) - Host name fromhostsport(required)password(optional)config(optional) - key/value overrides such asmaxmemoryorappendonly
Apache Kafka message broker. Kafka native mode is KRaft-only.
Two modes are supported:
- Dedicated controllers (recommended for production): Add a
controllerslist. Controllers run the metadata quorum as separate processes; brokers handle client traffic only. Controllers are provisioned first, then brokers join via dynamic discovery. - Combined mode (suitable for dev): Omit
controllers. Each broker runs both broker and controller roles in a single process.
Production format (dedicated controllers):
infrastructure: kafka: enabled: true mode: native version: "4.2.0" cluster_id: "iwNix2BLQny7Z9tlyna7Qw" min_insync_replicas: 2 controllers: - host: central-01 id: 100 port: 9093 dir_id: "2uBL_MXZSAe-9agF6p5aMQ" - host: media-01 id: 101 port: 9093 dir_id: "2AyDvMsASN26Tk7joXgJ6Q" - host: media-02 id: 102 port: 9093 dir_id: "ZTJH65OoRGqcxUYwht1RSw" brokers: - host: central-01 id: 1 port: 9092 - host: media-01 id: 2 port: 9092 - host: media-02 id: 3 port: 9092 topics: - name: analytics_events partitions: 10 replication_factor: 3Fields:
enabled(required)mode(required) -nativeversion(required) - Apache Kafka version (4.x+)cluster_id(required) - KRaft cluster UUID. Generate withkafka-storage.sh random-uuidcontrollers(optional) - dedicated controller nodes. If present, requires at least 3. Controller IDs must not overlap with broker IDshost(required) - host name from hosts mapid(required) - controller node IDport(optional) - controller listener port, default9093dir_id(required) - directory UUID for dynamic quorum bootstrap. Generate withkafka-storage.sh random-uuid
controller_port(optional) - combined mode only, default9093brokers(required) - at least 1; use 3+ for productiontopics(optional)min_insync_replicas(optional)delete_topic_enable(optional)offsets_topic_replication_factor(optional)transaction_state_log_replication_factor(optional)transaction_state_log_min_isr(optional)
Services / Interfaces / Observability
Section titled “Services / Interfaces / Observability”All three sections share the same shape. Use them to define application services, public interfaces (reverse proxy + web surfaces), and observability stack (VictoriaMetrics, vmagent, Grafana, etc.).
Service Definition
Section titled “Service Definition”Format:
services: commodore: enabled: true mode: docker version: v0.0.1 image: livepeerframeworks/frameworks-commodore:latest host: central-01 port: 18001 grpc_port: 19001 env_file: /etc/frameworks/commodore.env depends_on: [postgres, kafka] config: EXAMPLE_KEY: valueFields:
enabled(required)mode(required) -dockerornativeversion(optional) - Version or channel to trackimage(optional) - Docker image (docker mode)binary_url(optional) - Binary download URL (native mode)deploy(optional) - Override deploy slug (container/binary name)cluster(optional) - Explicit cluster assignment. Must reference a key inclusters. When omitted, the service’s role determines its cluster automaticallyhost(optional) - Single hosthosts(optional) - Multiple hosts for replicas. The CLI creates one task per host (e.g.,bridge@regional-eu-1)replicas(optional) - Replica countport(optional)grpc_port(optional)env_file(optional) - Per-service env file (overrides the top-levelenv_files)depends_on(optional)config(optional) - Inline key/value overrides (highest priority)
Environment variable merge order (later wins):
- Auto-generated from manifest infrastructure (DATABASE_URL, KAFKA_BROKERS, QUARTERMASTER_GRPC_ADDR, etc.)
- Top-level
env_files - Per-service
env_file - Inline
configmap
The merged result is written to /etc/frameworks/{service}.env on each host.
For profile: production, provisioning also forces secure internal gRPC defaults:
BUILD_ENV=productionGRPC_ALLOW_INSECURE=falseGIN_MODE=releasewhen not explicitly set or when set todebug
If navigator is enabled in a production manifest, the shared env_files must also provide:
- one complete managed CA source for Navigator:
NAVIGATOR_INTERNAL_CA_ROOT_CERT_FILENAVIGATOR_INTERNAL_CA_INTERMEDIATE_CERT_FILENAVIGATOR_INTERNAL_CA_INTERMEDIATE_KEY_FILE- or:
NAVIGATOR_INTERNAL_CA_ROOT_CERT_PEM_B64NAVIGATOR_INTERNAL_CA_INTERMEDIATE_CERT_PEM_B64NAVIGATOR_INTERNAL_CA_INTERMEDIATE_KEY_PEM_B64
Edge Manifest
Section titled “Edge Manifest”For multi-node edge fleets, use a dedicated edge manifest.
Format:
version: v1channel: stableroot_domain: example.compool_domain: edge.media-eu.example.comcluster_id: regional-productioncluster_manifest: cluster.yamlenrollment_token: fw_edge_...hosts_file: clusters/edge/hosts.enc.yamlmode: dockercapabilities: [ingest, edge, storage, processing]bandwidth_mbps: 2000max_transcodes: 4storage_capacity_bytes: 500000000000nodes: - name: edge-us-east-1 subdomain: edge-us-east-1 region: us-east apply_tune: true register_qm: trueSSH auth for edge nodes resolves from the operator’s machine. Pass --ssh-key
on the frameworks edge provision command when an explicit key is needed.
Fields:
channel(optional) -stable,rc, or an explicit version such asv0.2.0-rc3root_domainorpool_domain(at least one required)email(ACME)cluster_id(required whenregister_qm: true)cluster_manifest(optional) - Cluster manifest used to load platformSERVICE_TOKENforregister_qm; relative paths resolve from the edge manifest directory. Can also be passed as--cluster-manifest.enrollment_token(optional) - Token used for edge bootstraphosts_file(optional) - SOPS-encrypted edge host inventoryfetch_cert(deprecated) - Ignored for edge manifests; enrolled edge TLS is delivered by FoghornConfigSeedmode(optional) - Default node mode,dockerby default ornativecapabilities(optional) - Default enabled Helmsman capabilities:ingest,edge,storage,processing. Empty means all capabilities enabled.bandwidth_mbps(optional) - Default node bandwidth limit advertised by Helmsman and reconciled into MistServerbwlimit.max_transcodes(optional) - Default local transcoding concurrency limit reported by Helmsman.storage_capacity_bytes(optional) - Default local storage capacity reported by Helmsman.nodes(required)
Edge telemetry ingress is control-plane managed. Enrolled edge nodes receive remote-write URL and auth material from Foghorn during preregistration and subsequent ConfigSeed updates.
Node fields:
name(required)ssh(required unless provided byhosts_file) -user@hostsubdomain(optional)region(optional)apply_tune(optional)register_qm(optional)mode(optional) - Per-node override,dockerornativecapabilities(optional) - Per-node override for enabled capabilitiesbandwidth_mbps(optional) - Per-node bandwidth overridemax_transcodes(optional) - Per-node transcode limit overridestorage_capacity_bytes(optional) - Per-node storage capacity overridelabels(optional)
Validation Notes
Section titled “Validation Notes”The CLI validates:
- Required fields (
version,type, hosts) - Host references used by Postgres, ClickHouse, Kafka, services, interfaces, and observability entries
- If
clustersis present, each entry must havenameandtype - If a service sets
cluster, it must reference a defined cluster key - Cluster IDs (map keys) must be non-empty strings
Unknown keys are rejected by the parser. Keep manifests minimal and aligned with the schema above so typos and removed fields fail loudly.
Remote Manifests (GitHub App)
Section titled “Remote Manifests (GitHub App)”Instead of managing manifests locally, you can store them in a private GitHub repository and fetch them at provision time using GitHub App credentials.
Credentials are written to the canonical CLI config ($XDG_CONFIG_HOME/frameworks/config.yaml by default; run frameworks config path --kind config to confirm):
frameworks config set github.app-id 12345frameworks config set github.installation-id 67890frameworks config set github.private-key /path/to/app-private-key.pemframeworks config set github.repo your-org/infra-repoframeworks config set github.ref main # optional, defaults to mainFor CI or one-shot automation, the same values can be passed via environment variables: FRAMEWORKS_GITHUB_APP_ID, FRAMEWORKS_GITHUB_INSTALLATION_ID, FRAMEWORKS_GITHUB_PRIVATE_KEY, FRAMEWORKS_GITHUB_REPO, FRAMEWORKS_GITHUB_REF. Env values beat the stored config; per-invocation flags beat both.
# Provision using the remote manifest (cluster name required)frameworks cluster provision --github-repo your-org/infra-repo --cluster production
# Override credentials via flagsframeworks cluster provision \ --github-repo your-org/infra-repo \ --cluster production \ --github-app-id 12345 \ --github-installation-id 67890 \ --github-private-key /path/to/key.pemThe CLI fetches clusters/<cluster>/cluster.yaml from the repo, along with all referenced env_files, per-service env_file paths, and hosts_file. The standard provision pipeline then runs with the fetched data inside a tempdir that’s cleaned up when the command exits.
Configuration Keys
Section titled “Configuration Keys”| Key | Description |
|---|---|
github.app-id | GitHub App ID |
github.installation-id | GitHub App Installation ID |
github.private-key | Path to PEM-encoded private key |
github.repo | Repository in owner/repo format |
github.ref | Git ref (branch/tag), defaults to main |