Documentation

Configuration

Everything the installer does comes from one JSON file, conventionally cc.config.json. This is the reference for every section of it.

The configuration file

A cluster is described by one JSON file — nodes, networking, storage, packages, registries. Every installer command takes its path:

usage
$ community-cloud preflight cc.config.json
$ community-cloud run-bundle k3s phoenix cc.config.json
$ community-cloud doctor cc.config.json

You don't have to write it by hand. community-cloud with no arguments walks through building one, and can edit an existing file:

configure
$ community-cloud # build or edit a configuration
$ community-cloud configure --section nodes
$ community-cloud configure --all # every section, rather than the menu

The file holds secrets — the cluster token, registry passwords, a Nebula API key — so keep it out of version control. The upstream repository ignores *.config.json for this reason. What the installer records in the cluster has those stripped out.

Treat the machine holding it as the most privileged machine you have. cc.config.json is the cluster token, the registry credentials, and the Nebula API key in one place, usually next to an SSH key with root on every node. A dedicated administrative host beats a laptop.

A small complete example

{
  "config": { "adminEmail": "you@example.com" },

  "k3s": { "token": "a-long-shared-secret" },

  "values": {
    "domain": { "main": "example.com" }
  },

  "network": {
    "congestionControl": "bbr",
    "gateway": {
      "mode": "port-forward",
      "addresses": ["192.168.1.240-192.168.1.250"]
    }
  },

  "packages": { "cert-manager": true, "cloudnative-pg": true },

  "nodes": [
    {
      "name": "server-1",
      "address": "server-1.local",
      "username": "kyle",
      "type": "server",
      "gateway": true,
      "roles": ["worker", "storage-local"],
      "region": "eur",
      "zone": "eur-de-1"
    }
  ]
}

nodes

The only required section. Each entry is one machine.

Key Meaning
name What to call it. Also how it registers in the cluster, when it can be a DNS label.
address How to reach it over SSH. Often an alias from ~/.ssh/config rather than a name DNS knows.
type server or agent. Exactly one node should be a server.
username The SSH user. Needs passwordless sudo.
keyFile A private key path. Otherwise the usual ~/.ssh keys and your agent are tried.
port SSH port, when it isn't 22.
apiAddress Where other nodes reach this one's Kubernetes API, when that differs from address. A bastion or port forward gets you to a node without being a name the cluster can use.
nodeName What to register as in the cluster, when name and address can't be DNS labels. "Mamoru BKK" and 10.0.0.5 both can't.
gateway true if the outside world can reach this node. See network.gateway.
roles What it's for: worker, worker-gpu, storage-local, storage-distributed, storage, gateway, lighthouse. Applied as node-role.kubernetes.io/<role> labels.
labels Anything else worth labelling it with, as key/value pairs.
region, zone Where it physically is. Kubernetes spreads workloads across zones. Region codes are fixed — see installer/src/topology/regions.ts.
storage Per-node storage settings, overriding the cluster-wide storage section.
nebula Per-node mesh settings: role, listenPort, staticAddresses, isLighthouse, isRelay.

Some roles are worked out rather than written down: a node with a GPU gets worker-gpu, and one with "gateway": true gets gateway.

k3s

Key Default Meaning
token — The cluster token. The server and every agent need the same one. Required.
version / channel current stable Pin the K3s release.
clusterCidr 10.42.0.0/16 The pod network. Should agree with the Cilium values.
disable — Packaged components to leave out, e.g. metrics-server.
extraServerArgs, extraAgentArgs — Anything else to pass through.
kubeconfig /etc/rancher/k3s/k3s.yaml Where kubectl should look, for a cluster this didn't build.

traefik, servicelb and local-storage are always disabled, whatever disable says. Cilium's Envoy serves the Gateway API, Cilium's own IP management hands out load balancer addresses, and TopoLVM provisions local volumes. K3s's local-path provisioner also claims to be the default storage class, and Kubernetes allows only one.

network

Kernel networking settings sit at the top of this section; everything else is a subsection.

Key Default Meaning
congestionControl bbr TCP congestion control. BBR holds throughput up over links that lose the odd packet, which CUBIC reads as congestion.
qdisc fq The default queueing discipline. BBR wants a fair-queueing one underneath it.
congestionModule tcp_<algorithm> The kernel module, when it isn't named after the algorithm.

network.cilium

Key Default Meaning
version 1.20.1 The Cilium release.
cliVersion v0.20.0 The Cilium CLI. "stable" takes whatever is current instead.
gatewayApiVersion v1.6.1 The Gateway API release. Pinned deliberately: a mismatched pair fails quietly.
kubeProxyReplacement true Whether Cilium takes over from kube-proxy. K3s reads this too.
valuesFile the embedded one Override the Cilium values.

network.gateway

How the outside world reaches the cluster. The gateway bundle turns this into a Cilium address pool, and on a home network an ARP announcement policy as well.

Key Meaning
mode floating — the address is already on an interface of a gateway node, routed there by a provider. port-forward — a router maps ports to a LAN address nothing holds, and Cilium answers ARP for it.
addresses What a LoadBalancer Service may be given: "203.0.113.10", "10.0.0.0/29", or "192.168.1.240-192.168.1.250".
interfaces port-forward only. Which interfaces to answer ARP on, as regular expressions. Defaults to every local interface.
name What to call the pool and policy. Defaults to community-cloud-gateway.

network.externalIPs

Addresses a particular node holds, as node name to address. This is the floating case written per node, and the node must be a gateway.

"externalIPs": { "Brahma": "203.0.113.10" }

network.nebula

A Defined Networking mesh, for nodes that can't reach each other directly.

Key Meaning
apiKey A Defined Networking API key. Falls back to the DEFINED_API_KEY environment variable.
network / networkID Which network, by name or by ID.
defaultRole The role nodes enrol under.
roles Roles to create: name, description, and firewallRules of protocol (ANY/TCP/UDP/ICMP), allowedRole or allowedTags, and portRange.

storage

Which disks TopoLVM may carve volumes out of. Can also be set per node, which is usually where it belongs — nodes differ in their disks more than in anything else.

Key Meaning
disks Consider only these, by path. This narrows what's considered; it never overrides a safety check.
diskClasses Put a disk in a class other than the one its hardware suggests: {"/dev/sdb": "hdd"}.
minimumSizeGb Ignore disks smaller than this. Defaults to 1.

Disks are sorted by what they are — NVMe to ssd, other solid state to ssd-sata, spinning to hdd. See Storage for how volume groups are built and which safety checks apply.

packages

Which Helm charts to install. true is shorthand for enabled.

"packages": {
  "cert-manager": true,
  "cloudnative-pg": true,
  "topolvm": { "enabled": true, "version": "15.5.2" }
}

In the catalogue: cert-manager, cloudnative-pg, topolvm, headlamp, tailscale-operator. Dependencies are worked out and installed first — TopoLVM needs cert-manager, so enabling it enables that too.

A package not in the catalogue needs repo and chart. Any package takes version, namespace, releaseName, valuesFile (relative to the working directory) and requires.

Some packages bring credentials with them, such as the database passwords applications connect with. Those aren't configured here; they're generated during the install. See Generated credentials.

Applications live in plugins rather than in the catalogue — Authentik is the worked example. See plugins below.

plugins

Which plugins to load from ~/.community-cloud/plugins/. Being in that directory isn't enough: a plugin loads only when the configuration names it, and only when its fingerprint matches.

"plugins": {
  "authentik": {
    "enabled": true,
    "sha256": "481ec17bce6555d9ea01cb23ec1bdf6290bb0b7644be562a0fdc298b8bfbd4c2"
  }
}

Enable one without a sha256 and the installer prints everything it would add — the charts, the manifests, the credentials, and the actual shell of every command — then refuses to run and gives you the line to paste. See Plugins.

registries

Credentials for private registries, keyed by hostname. These end up in /etc/rancher/k3s/registries.yaml, owned by root and readable by nobody else.

"registries": {
  "registry.gitlab.com": {
    "auth": { "username": "deploy-token", "password": "..." }
  }
}

auth takes either username and password, or one of the encoded forms: auth (base64 of username:password) or identityToken. tls takes caFile, certFile, keyFile, insecureSkipVerify. endpoint and rewrite are for mirroring.

values and config

What the manifests render against. Anything in values is available as .Values.<path>. The top level of the configuration is too, so config.adminEmail reaches a manifest as .Values.adminEmail.

The manifests currently reference adminEmail, domain.main, domain.auth, domain.agent, domain.crm, domain.mattermost and authentik.secretKey.

A few values are derived rather than written:

Value Derived from
k8sApiServer, k8sApiPort The control-plane node
l2Announcements The gateway mode
networkDevices The shared interface list

pipelines

Named sequences of bundles, for run-pipeline:

"pipelines": {
  "new-node": ["base", "network", "gpu", "k3s", "nodeLabels"]
}

helm

version pins the Helm release. That's all it takes.

Drift

Once a cluster exists, the cluster bundle records its configuration in the cluster itself as a ConfigMap in kube-system called community-cloud, with the secrets stripped out. Finding it is how anything can tell this is a Community Cloud cluster, and doctor compares it against your file to report anywhere they've drifted apart.