The configuration file
KubeSolo reads its settings from one YAML file, /etc/kubesolo/config.yaml. The installer creates it, and the service starts KubeSolo with --config=/etc/kubesolo/config.yaml. A file only needs the keys it changes; anything omitted keeps its default.
The file lives outside the data directory on purpose: it holds settings, not cluster state, so it survives kubesoloctl reset. It is written 0600 and owned by root because it can hold portainer.edgeKey, a credential. A file that declares an apiVersion KubeSolo does not recognise is rejected.
When changes take effect
On restart. KubeSolo reads its configuration once at startup and derives every path, certificate and component argument from it. There is no live reload.
One setting can never change: path. Every certificate, the cluster database and all container state live below it, and nothing moves them. Choose it at install time.
Precedence
Four layers, lowest to highest. Each layer overrides only the settings it actually sets.
| Layer | Notes |
|---|---|
| 1. Built-in defaults | What KubeSolo runs with when nothing is set. |
| 2. The configuration file | /etc/kubesolo/config.yaml, or the file named by --config / KUBESOLO_CONFIG. |
| 3. Environment variables | One KUBESOLO_* variable per setting. An empty value counts as set. |
| 4. Command-line flags | Deprecated, but they still win. |
If a change in the file seems to have no effect, a flag or environment variable in the service definition is overriding it:
To see what KubeSolo actually resolves, without starting anything:
Editing the configuration
With kubesoloctl
Nothing is written until validation passes, so a rejected value leaves the file as it was. List and map settings take the same form as their flag: kubernetes.apiServer.extraSANs takes 10.0.0.4,kubesolo.local, and kubernetes.kubelet.systemReserved takes cpu=1,memory=500Mi.
By hand
The file is plain YAML. When KubeSolo or kubesoloctl writes it, comments are dropped and keys are sorted alphabetically; the previous version is kept as /etc/kubesolo/config.yaml.bak. Validate hand edits before restarting with sudo kubesoloctl config validate.
The repository ships an annotated example file with every setting at its default and a command to verify each one.
The whole document
Every setting at its default:
Every setting
Every setting, with its deprecated flag and its environment variable. All of them except path apply on the next restart.
| Setting | Flag | Env var | Default | Applies | Description |
|---|---|---|---|---|---|
| path | --path | KUBESOLO_PATH | /var/lib/kubesolo | immutable | Directory for all state: certificates, the Kine database and containerd state. |
| logging.debug | --debug | KUBESOLO_DEBUG | false | restart | Debug logging. |
| logging.pprof | --pprof-server | KUBESOLO_PPROF_SERVER | false | restart | Go pprof server on port 6060, all interfaces. |
| network.nodeIP | --node-ip | KUBESOLO_NODE_IP | "" (auto-detect) | restart | IPv4 address for the API server, kubeconfig, kubelet and LoadBalancer EXTERNAL-IP. See node IP. |
| network.mtu | --mtu | KUBESOLO_MTU | 0 (auto-detect) | restart | MTU for the cni0 bridge and pod interfaces. |
| network.disableIPv6 | --disable-ipv6 | KUBESOLO_DISABLE_IPV6 | false | restart | Turn IPv6 off for CoreDNS, the kubelet and the host. See IPv6. |
| network.loadBalancer.enabled | --load-balancer | KUBESOLO_LOAD_BALANCER | true | restart | Set EXTERNAL-IP on LoadBalancer Services. Required by d2k. |
| network.loadBalancer.ip | --load-balancer-ip | KUBESOLO_LOAD_BALANCER_IP | "" (node IP) | restart | Address published as EXTERNAL-IP. |
| runtime.endpoint | --container-runtime-endpoint | KUBESOLO_CONTAINER_RUNTIME_ENDPOINT | "" (embedded) | restart | CRI endpoint of a host-managed runtime instead of the embedded containerd. |
| runtime.containerMode | --container-mode | KUBESOLO_CONTAINER_MODE | unset (auto-detect) | restart | Force container mode on or off. |
| kubernetes.nodeName | — | KUBESOLO_NODE_NAME | "" (hostname) | restart | Node name. Trimmed and lowercased. |
| kubernetes.apiServer.extraSANs | --apiserver-extra-sans | KUBESOLO_APISERVER_EXTRA_SANS | [] | restart | Extra IPs or DNS names for the API server certificate. |
| kubernetes.apiServer.startupTimeoutSeconds | --startup-timeout | KUBESOLO_STARTUP_TIMEOUT | 600 | restart | Seconds each component may take to pass its startup health check. Raise it on SD cards. |
| kubernetes.kubelet.cpuManager.policy | --cpu-manager-policy | KUBESOLO_CPU_MANAGER_POLICY | none | restart | none or static. Static is not allowed in container mode. |
| kubernetes.kubelet.cpuManager.policyOptions | --cpu-manager-policy-options | KUBESOLO_CPU_MANAGER_POLICY_OPTIONS | {} | restart | Static policy options, such as full-pcpus-only. |
| kubernetes.kubelet.cpuManager.reservedCPUs | --reserved-cpus | KUBESOLO_RESERVED_CPUS | "" ("0" under static) | restart | Cpuset held back for the host. |
| kubernetes.kubelet.systemReserved | --system-reserved | KUBESOLO_SYSTEM_RESERVED | {} | restart | Resources withheld from allocatable: cpu, memory, ephemeral-storage, pid. |
| storage.localPath.enabled | --local-storage | KUBESOLO_LOCAL_STORAGE | true | restart | local-path storage provisioner and default StorageClass. |
| storage.localPath.sharedPath | --local-storage-shared-path | KUBESOLO_LOCAL_STORAGE_SHARED_PATH | "" | restart | Shared filesystem for volumes instead of the data directory. |
| storage.dbWALRepair | --db-wal-repair | KUBESOLO_DB_WAL_REPAIR | false | restart | Check the SQLite database at startup and clear WAL files if it is corrupt. |
| portainer.edgeID | --portainer-edge-id | KUBESOLO_PORTAINER_EDGE_ID | "" | restart | Portainer Edge ID. |
| portainer.edgeKey | --portainer-edge-key | KUBESOLO_PORTAINER_EDGE_KEY | "" | restart | Portainer Edge key. Secret: redacted by the config API. |
| portainer.async | --portainer-edge-async | KUBESOLO_PORTAINER_EDGE_ASYNC | false | restart | Edge async mode. |
| portainer.image | --portainer-edge-image | KUBESOLO_PORTAINER_EDGE_IMAGE | docker.io/portainer/agent:lts | restart | Edge Agent image, including the tag. |
| d2k.enabled | --d2k | KUBESOLO_D2K | false | restart | Docker-compatible API on port 2376 (amd64 and arm64 only). |
| d2k.namespace | --d2k-namespace | KUBESOLO_D2K_NAMESPACE | d2k | restart | Namespace d2k deploys into and translates against. |
| metrics.enabled | --metrics-server | KUBESOLO_METRICS_SERVER | false | restart | Prometheus metrics endpoint. See observability. |
| metrics.bindAddress | --metrics-bind-address | KUBESOLO_METRICS_BIND_ADDRESS | 127.0.0.1:9105 | restart | Address the metrics endpoint listens on. |
| api.enabled | — | KUBESOLO_API_ENABLED | false | restart | Serve this configuration over a unix socket. |
| api.socketPath | — | KUBESOLO_API_SOCKET_PATH | "" (<path>/config.sock) | restart | Socket location. Must stay under the unix socket path limit. |
A few combinations are rejected at startup: d2k.enabled without network.loadBalancer.enabled, and the static CPU manager policy in container mode. On arm and riscv64, d2k disables itself with a warning. An MTU below 1280 warns that IPv6 pod traffic may not fragment correctly.
Flags: deprecated, still honoured
Every KubeSolo flag in the table above still works and still overrides the file, so existing installs keep running. They are deprecated: no new flags will be added, and new settings are available only through the file. Boolean flags also have a negated form, such as --no-load-balancer.
| Flag | Status |
|---|---|
| --config | Names the configuration file. Defaults to /etc/kubesolo/config.yaml; also KUBESOLO_CONFIG. |
| --print-config | Prints the resolved configuration as YAML and exits without starting anything. |
| --version, -v | Prints the version and exits. |
| --full | Deprecated and has no effect. KubeSolo always uses upstream Kubernetes defaults. Still accepted with a warning, for compatibility; it will be removed. |
Migrating a flag-based install
Installs made before v1.2.1 pass settings as flags in the service definition. kubesoloctl upgrade converts them automatically and keeps the old service file as .bak. To do it by hand, run the binary with the flags the service currently passes plus --print-config, then save the result:
Then reduce the service command line to one flag and restart:
Configuration API
KubeSolo can serve its configuration over a unix socket, so it can be read and changed programmatically. It is off by default:
The socket is mode 0600 and owned by root. File permissions are the whole access model: there is no token and no TLS, because there is no network listener. Anything that can open the socket can change the configuration. Changes made through the API are desired state; they take effect on the next restart, and every write response lists the settings that need one.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/config | Read the stored configuration. portainer.edgeKey is returned as *** unless you add ?showSecrets=true. |
| GET | /api/v1/config/schema | Every setting, its type, default and mutability. |
| PATCH | /api/v1/config | Change part of it with an RFC 7386 JSON merge patch. null resets a setting to its default. |
| PUT | /api/v1/config | Replace the whole document. Omitted settings return to their defaults. |
| POST | /api/v1/config:validate | Report what a candidate would change, without saving. |
| DELETE | /api/v1/config | Reset every setting to its default. path is kept. |
| GET | /healthz | Liveness. |
Reads return an ETag; send it back as If-Match to get a 412 if the configuration changed underneath you. Changing path returns 409, and an invalid configuration returns 422. A rejected request writes nothing. Each write is logged with the setting paths it changed, never their values: journalctl -u kubesolo | grep configapi.
The full reference is in the repository: docs/configuration/config-api.md.