Hybrid Talos cluster with KubeSpan. Phase 3 - Applying config files
Hybrid Talos cluster with KubeSpan
In this blog series I explore how I deploy hybrid Talos cluster with KubeSpan.
Previous articles:
Introduction post on hybrid cluster deployment
Phase 0 - provisioning control plane nodes with Terraform
Phase 1 - preparing environment
Phase 2 - generating config files
Phase 3 - applying configuration
In this walkthrough we’ll apply generating configuration for our cluster. Configuration is applied to each node individually.
generated folder will contain configuration for each node separately. Configuration looks the same for all nodes except for IP, hostname and, maybe, disk configuration.
To apply the configuration for control plane nodes we’ll run talos apply-confg command:
INSECURE_FLAG=${INSECURE:-"yes"}
INSECURE_OPTION=""
if [ "$INSECURE_FLAG" = "yes" ]; then
echo "Using --insecure flag for initial configuration (maintenance service)"
INSECURE_OPTION="--insecure"
else
echo "Not using --insecure flag (connecting to configured Talos API)"
fi
for node in $CONTROLPLANE_NODES; do
ip_var="CONTROLPLANE_${node}_IP"
name_var="CONTROLPLANE_${node}_NAME"
ip="${!ip_var}"
name="${!name_var}"
if [ -n "$ip" ]; then
echo "Applying controlplane-${node} configuration to $ip ($name)..."
if talosctl apply-config $INSECURE_OPTION --nodes $ip --file generated/controlplane-${node}.yaml; then
echo "Successfully applied controlplane-${node} configuration to $ip"
else
echo "ERROR: Failed to apply controlplane-${node} configuration to $ip"
fi
else
echo "WARNING: IP address for controlplane-${node} not found"
fi
done
For initial configuration, we need —insecure flag set. Current state of nodes does not have CA set, so this option is required. Any config change past initial apply will not need this option as trust will be configured.
Similar loop is used to apply worker configuration.
# Apply worker configurations
echo "Applying worker configurations..."
for node in $WORKER_NODES; do
ip_var="WORKER_${node}_IP"
ip="${!ip_var}"
if [ -n "$ip" ]; then
echo "Applying worker-${node} configuration to $ip..."
if talosctl apply-config $INSECURE_OPTION --nodes $ip --file generated/worker-${node}.yaml; then
echo "Successfully applied worker-${node} configuration to $ip"
else
echo "ERROR: Failed to apply worker-${node} configuration to $ip"
fi
else
echo "WARNING: IP address for worker-${node} not found"
fi
done
Bootstrapping
Bootstrapping is where etcd initialization takes place. As of 2026, Talos does not allow BYO etcd cluster, so this step is required.
Only one control plane node is needed to bootsrap etcd. Let’s take the first node to do that:
# Select first node as the bootstrap node
BOOTSTRAP_NODE=$(echo $NODE_IPS | awk '{print $1}')
echo "Using control plane node $BOOTSTRAP_NODE as bootstrap target"
# Bootstrap the cluster
echo "Bootstrapping the cluster on node $BOOTSTRAP_NODE"
if talosctl bootstrap --nodes $BOOTSTRAP_NODE -e $BOOTSTRAP_NODE; then
echo "Successfully bootstrapped the cluster"
else
echo "ERROR: Failed to bootstrap the cluster"
exit 1
fi
This will take some time, but after this process is done you should get a healthy, boostrapped Kubernetes cluster. We’ll provision networking next.
Cilium
I chose Cilium for it’s comprehensive stack. It never failed me. I use Cilium as a replacement of networking fabric, MetalLB L2 ingress configuration and kube-proxy. It also includes GatewayAPI support out of the box.
In the following subsections I’ll describe some values from values.yaml file.
L2 Announcement
For L2 announcement to work, you need to supply list of interface regex’s to supply announcement to. This is to ensure only your data plane interfaces get ingress traffic.
I have a mix of nodes with interface names like enp5s01, eno1, etc. My devices field looks like this:
devices: "eth+ ens+ enp+ eno+"
GatewayAPI
GatewayAPI configuration is enabled via the following section:
gatewayAPI:
enabled: true
hostNetwork:
enabled: false
enableAlpn: true
enableProxyProtocol: false
set enableProxyProtocol to true in the case you are running DMZ ingress in a different VLAN. I chose Caddy with L4 plugin to setup ingress in layer 4 and route traffic to various gateways.
I run separate ingress instances – private and public. Each in their own /28 VLAN. Via firewall they are allowed to connect to their respective GatewayAPI bound IP address. This way, isolation between public and private zones is enforced on multiple layers.
A really clean defence in depth approach to expose your workloads to the public internet, and keep your private endpoints isolated.
Final words
I’ll leave specific bootstrapping of Cilium and ArgoCD steps out of this series. You can take inspiration from the Github repository accompanying this blog. The process closely resembles one described in Talos blog on Deploying Cilium CNI.
I use the same approach to deploy ArgoCD. My bootstrapped cluster comes with networking and gitops out of the box so they’re ready to sync all my applications from Git.
Code
The code used to deploy the cluster is available via Github - sashkachan/talos-kubespan-bootstrap. I will use this code for the walkthrough of all phases and configuration required to make it succeed.