Skip to content

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.

brew install sops

Download the latest binary from the SOPS releases page:

# Download the binary
curl -LO https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64

# Move the binary in to your PATH
mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops

# Make the binary executable
chmod +x /usr/local/bin/sops
scoop install sops

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.

brew install age
sudo apt install age

On other distributions, download the binary from the age releases page.

scoop install age

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.

brew install budimanjojo/tap/talhelper

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:

go install github.com/budimanjojo/talhelper@latest
scoop install talhelper

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:

age-keygen -o age.agekey
# Note the public key from the output

Create a .sops.yaml in your project root:

---
creation_rules:
  - age: >-
      <age-public-key>

Export the key so SOPS can decrypt:

export SOPS_AGE_KEY_FILE=$(pwd)/age.agekey

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:

cd terraform/cluster/aws
terraform output

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:

cd apps/talos/aws/
talhelper genconfig
---
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:

talosctl get disk -n <node-ip> --insecure

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 as installDisk: /dev/sda in talconfig.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):

cd apps/talos/<aws/proxmox>/
talhelper gensecret > talsecret.sops.yaml

Encrypt the secrets file with SOPS:

sops -e -i talsecret.sops.yaml

Step 4: Generate Machine Configs

talhelper genconfig

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.

Tip

Use --dry-run to preview changes without writing files:

talhelper genconfig --dry-run

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