4.1 Generate talos configuration files with Talhelper¶
Talhelper is a declarative configuration tool for Talos Linux clusters. It generates Talos machine configs from a single talconfig.yaml file with native SOPS secret encryption support -- ideal for GitOps workflows.
Prerequisites & Tool Installation¶
The following tools are required before proceeding. Install each one for your platform.
sops¶
Mozilla SOPS encrypts secrets for safe storage in Git.
Download the latest binary from the SOPS releases page:
Or download the .exe from the SOPS releases page and add it to your PATH.
For other methods, see the SOPS repository.
age¶
A simple file encryption tool used by SOPS for key management.
On other distributions, download the binary from the age releases page.
Or download the .exe from the age releases page and add it to your PATH.
For other methods, see the age repository.
talhelper¶
The declarative configuration tool for Talos Linux clusters.
Download the latest binary from the release page:
# Download and extract the latest release
curl -fsSL https://github.com/budimanjojo/talhelper/releases/download/v3.1.4/talhelper_linux_amd64.tar.gz | tar xz -C /tmp talhelper
# Move the binary into your PATH
sudo mv /tmp/talhelper /usr/local/bin/talhelper
sudo chmod +x /usr/local/bin/talhelper
Or install via Go:
Or download the binary from the talhelper releases page and add it to your PATH.
For other methods, see the talhelper installation docs.
Step 1: Configure SOPS Encryption¶
Generate an Age key pair for encrypting cluster secrets:
Create a .sops.yaml in your project root:
Export the key so SOPS can decrypt:
Warning
Store age.agekey securely and never commit it to git. Add it to .gitignore.
Step 2: Create talconfig.yaml¶
This is the single source of truth for your cluster. Create talconfig.yaml in your working directory.
---
clusterName: <cluster-name>
talosVersion: v1.12.3
kubernetesVersion: v1.34.1
endpoint: https://<NLB-HOSTNAME>:6443
cniConfig:
name: none
patches:
- |-
machine:
features:
kubePrism:
enabled: true
port: 7445
kubelet:
extraArgs:
rotate-server-certificates: "true"
time:
disabled: false
servers:
- time.cloudflare.com
bootTimeout: 2m0s
cluster:
externalCloudProvider:
enabled: true
manifests: []
extraManifests:
- https://raw.githubusercontent.com/alex1989hu/kubelet-serving-cert-approver/main/deploy/standalone-install.yaml
- https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
proxy:
disabled: true
- |-
machine:
kubelet:
registerWithFQDN: true
- |-
machine:
network:
kubespan:
enabled: true
cluster:
discovery:
enabled: true
nodes:
- hostname: cp-01
ipAddress: <CP-PRIVATE-IP>
controlPlane: true
ignoreHostname: true
installDisk: /dev/xvda
networkInterfaces:
- interface: eth0
dhcp: true
- hostname: wn-01
ipAddress: <WORKER-PRIVATE-IP>
controlPlane: false
ignoreHostname: true
installDisk: /dev/xvda
networkInterfaces:
- interface: eth0
dhcp: true
controlPlane:
certSANs:
- <NLB-HOSTNAME>
nameservers:
- 10.2.0.2
- 169.254.169.253
worker:
nameservers:
- 10.2.0.2
- 169.254.169.253
Key differences from Bare Metal / Proxmox¶
| Feature | AWS | Bare Metal / Proxmox |
|---|---|---|
| CNI | name: none (Cilium installed separately) |
name: custom with inline manifest URL |
| Cloud provider | externalCloudProvider: enabled |
Not used |
| Networking | DHCP on eth0, no static IPs or routes |
Static addresses, routes, and optional VIP |
| DNS | VPC DNS (10.2.0.2) + AWS DNS (169.254.169.253) |
Public DNS (1.1.1.1, 8.8.8.8) |
| KubeSpan | Enabled with cluster discovery | Not configured |
| KubePrism | Enabled on port 7445 | Not configured |
| Install disk | /dev/xvda (AWS EBS) |
/dev/sda |
| Node identity | ignoreHostname: true, registerWithFQDN: true |
Hostname-based |
Update talconfig.yaml after provisioning¶
After running terraform apply, you need to update three values in talconfig.yaml with outputs from Terraform.
1. Get the outputs from Terraform:
Example output:
control_plane_private_ips = [
"10.2.11.65",
]
nlb_dns_name = "rciis-talos-api-nlb-3ed72d7abbe75960.elb.af-south-1.amazonaws.com"
worker_private_ips = [
"10.2.11.227",
]
2. Update talconfig.yaml with the real values:
| Placeholder | Replace with | Terraform output key |
|---|---|---|
<NLB-HOSTNAME> |
NLB DNS name (used in endpoint, controlPlane.certSANs) |
nlb_dns_name |
<CP-PRIVATE-IP> |
Control plane private IP (used in nodes[0].ipAddress) |
control_plane_private_ips |
<WORKER-PRIVATE-IP> |
Worker private IP (used in nodes[1].ipAddress) |
worker_private_ips |
Note
The nameservers values (10.2.0.2 and 169.254.169.253) are standard for AWS VPCs. The first is the VPC DNS resolver (VPC CIDR base + 2), and the second is the AWS instance metadata DNS. Adjust 10.2.0.2 if your VPC uses a different CIDR block.
3. Regenerate machine configs:
---
clusterName: rciis-kenya # Use the hosting country as the site name
talosVersion: v1.12.3
kubernetesVersion: v1.32.0
endpoint: https://192.168.200.10:6443
allowSchedulingOnMasters: true
cniConfig:
name: custom
urls:
- https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
nodes:
- hostname: cp1
ipAddress: 192.168.200.11
controlPlane: true
installDisk: /dev/sda
networkInterfaces:
- interface: eth0
addresses:
- 192.168.200.11/24
routes:
- network: 0.0.0.0/0
gateway: 192.168.200.1
nameservers:
- 1.1.1.1
- 8.8.8.8
- hostname: worker1
ipAddress: 192.168.200.21
controlPlane: false
installDisk: /dev/sda
networkInterfaces:
- interface: eth0
addresses:
- 192.168.200.21/24
routes:
- network: 0.0.0.0/0
gateway: 192.168.200.1
controlPlane:
schematic:
customization:
systemExtensions:
officialExtensions:
- siderolabs/intel-ucode
patches:
- |-
machine:
kubelet:
extraArgs:
rotate-server-certificates: "true"
Before setting installDisk, you need to identify the correct disk device on each node. The nodes must first be booted into Talos maintenance mode via Terraform (see 4.2 Boot & Install — Step 1).
Determine the Install Disk
Once a node is in maintenance mode, use talosctl get disk to list available disks:
Example output:
NODE NAMESPACE TYPE ID VERSION SIZE READ ONLY TRANSPORT ROTATIONAL WWID MODEL SERIAL
runtime Disk loop0 2 4.1 kB true
runtime Disk loop1 2 692 kB true
runtime Disk loop2 2 479 kB true
runtime Disk loop3 2 75 MB true
runtime Disk sda 2 54 GB false virtio true QEMU HARDDISK
runtime Disk sdb 2 54 GB false virtio true QEMU HARDDISK
runtime Disk sr0 2 4.2 MB false ata true QEMU DVD-ROM QEMU_DVD-ROM_QM00003
Look for real disks (not loop* or sr0 devices). In this example:
sda(54 GB) — OS disk (used asinstallDisk: /dev/sdaintalconfig.yaml)sdb(54 GB) — Data disk (available for persistent storage)
Tip
Run this command against each node to verify disk names are consistent. Proxmox VMs with virtio-scsi typically present disks as /dev/sda, /dev/sdb, etc.
Set the installDisk field in your talconfig.yaml to the appropriate device path (e.g., /dev/sda).
Example talconfig.yaml¶
---
clusterName: rciis-proxmox
talosVersion: v1.12.3
kubernetesVersion: v1.32.0
endpoint: https://192.168.100.10:6443
allowSchedulingOnMasters: true
cniConfig:
name: custom
urls:
- https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml
nodes:
- hostname: talos-cp1
ipAddress: 192.168.100.11
controlPlane: true
installDisk: /dev/sda # Verify with: talosctl get disk -n 192.168.100.11 --insecure
- hostname: talos-worker1
ipAddress: 192.168.100.21
controlPlane: false
installDisk: /dev/sda # Verify with: talosctl get disk -n 192.168.100.21 --insecure
controlPlane:
patches:
- |-
machine:
kubelet:
extraArgs:
rotate-server-certificates: "true"
certSANs:
- 192.168.100.10
For the full configuration reference, see the talconfig reference docs.
Key Configuration Fields¶
| Field | Description |
|---|---|
clusterName |
Name of the Kubernetes cluster |
talosVersion |
Talos Linux version to use |
kubernetesVersion |
Kubernetes version to deploy |
endpoint |
Control plane endpoint (VIP or load balancer) |
nodes[].hostname |
Node hostname |
nodes[].ipAddress |
Node IP address |
nodes[].controlPlane |
true for control plane, false for worker |
nodes[].installDisk |
Disk device for Talos installation |
nodes[].schematic |
Custom image extensions per node |
controlPlane.patches |
Patches applied to all control plane nodes |
worker.patches |
Patches applied to all worker nodes |
patches |
Global patches applied to all nodes |
cniConfig |
CNI plugin configuration |
Note
Please ensure the talosctl and talosVersion is on the same major version.
Step 3: Generate and Encrypt Secrets¶
Generate cluster secrets (certificates, keys, tokens):
Encrypt the secrets file with SOPS:
Step 4: Generate Machine Configs¶
This reads talconfig.yaml and talsecret.sops.yaml (auto-decrypted) and outputs per-node config files into ./clusterconfig/:
clusterconfig/
rciis-cluster-cp1.yaml
rciis-cluster-cp2.yaml
rciis-cluster-cp3.yaml
rciis-cluster-worker1.yaml
talosconfig
A .gitignore is created automatically to prevent committing the unencrypted generated files.
File Structure Summary¶
After setup, your GitOps repository should contain:
.
├── .sops.yaml # SOPS encryption rules
├── talconfig.yaml # Cluster definition (commit this)
├── talsecret.sops.yaml # Encrypted cluster secrets (commit this)
├── talenv.sops.yaml # Encrypted env variables (optional, commit this)
└── clusterconfig/ # Generated configs (gitignored)
├── rciis-cluster-cp1.yaml
├── rciis-cluster-worker1.yaml
└── talosconfig
Further Reading¶
- Talhelper Documentation
- Configuration Reference
- CLI Reference
- Secret Management Guide
- Talos Linux Documentation
- Example talconfig.yaml