Air-Gapped Installation of Talos Linux Cluster

Bootstrap a Talos Linux cluster for Cozystack with an air-gapped environment

Introduction

This guide outlines the steps to bootstrap a Cozystack cluster in an air-gapped environment.

Air-gapped installation means that the cluster has no direct access to the Internet. All necessary resources, such as images and metadata, must be available on the private network.

Configuring Talos Nodes

1. Configure NTP Servers

Accurate time synchronization is critical for the cluster. In your Talos machine configuration, set local NTP servers that are accessible inside your private network:

machine:
  time:
    servers:
      # example values
      - 192.168.0.4
      - 10.10.0.5

Ensure that these NTP servers are reachable from the first Talos node.

2. Configure Container Registry Mirrors

Since the cluster cannot access public container registries, it needs to use their local mirrors. Creating such mirrors is out of the scope of this guide.

Update your machine configuration in the following way, providing the IP addresses and ports of your local mirrors for each registry:

machine:
  registries:
    mirrors:
      docker.io:
        endpoints:
          - http://10.0.0.1:8082
      ghcr.io:
        endpoints:
          - http://10.0.0.1:8083
      gcr.io:
        endpoints:
          - http://10.0.0.1:8084
      registry.k8s.io:
        endpoints:
          - http://10.0.0.1:8085
      quay.io:
        endpoints:
          - http://10.0.0.1:8086
      cr.fluentbit.io:
        endpoints:
          - http://10.0.0.1:8087
      docker-registry3.mariadb.com:
        endpoints:
          - http://10.0.0.1:8088
    config:
      "10.0.0.1:8082":
        tls:
          insecureSkipVerify: true
        auth:
          username: myuser
          password: mypass

Of course, the values for config.[0].auth.* are given as examples, and you need to use real credentials. Make sure your local registry proxies mirror all required images for Talos and Kubernetes components.

3. Add CA Certificate

To use a private Certificate Authority, you need to add its certificate to the nodes.

# talm: nodes=["10.10.10.10"], endpoints=["10.10.10.10"], templates=["templates/controlplane.yaml"]
# THIS FILE IS AUTOGENERATED. PREFER TEMPLATE EDITS OVER MANUAL ONES.
machine:
# ...
# ...
  discovery:
    enabled: false
  etcd:
    advertisedSubnets:
      - 10.4.100.10/24
  allowSchedulingOnControlPlanes: true
---
apiVersion: v1alpha1
kind: TrustedRootsConfig
name: my-enterprise-ca
certificates: |
  -----BEGIN CERTIFICATE-----
  ...
  -----END CERTIFICATE-----  

4. Apply Changes

After you have made the changes above, you can apply the configuration and bootstrap a cluster:

Using Talm

Rebuild the node configuration files and apply them to each node:

talm template -e <ip> -n <ip> -t templates/controlplane.yaml -i > nodes/node1.yaml
talm apply -f nodes/node1.yaml
# repeat for each node

Finally, bootstrap the cluster as usual:

talm bootstrap -f nodes/node1.yaml

Read the Talm configuration guide to learn more.

Using talosctl

Apply the configuration to each node:

talosctl apply -f controlplane.yaml -n <ip> -e <ip> -i

Finally, bootstrap the cluster using one of the nodes:

talosctl bootstrap -n <ip> -e <ip>

Read the talosctl configuration guide to learn more.

5. Configure Container Registry Mirrors for Tenant Kubernetes

To use registry mirrors, tenant Kubernetes clusters need to be configured separately, although in the same way as the Talos nodes.

To perform this configuration, you first need to deploy a Cozystack cluster of (or upgrade your cluster to) version v0.32.0 or later. Check your current cluster version with:

kubectl get deploy -n cozy-system cozystack -oyaml | grep installer

Generate a Kubernetes Secret named patch-containerd using the following example:

apiVersion: v1
kind: Secret
metadata:
  name: patch-containerd
  namespace: cozy-system
type: Opaque
stringData:
  docker.io.toml: |
    server = "https://registry-1.docker.io"
    [host."http://10.0.0.1:8082"]
      capabilities = ["pull", "resolve"]
      skip_verify = true    
  ghcr.io.toml: |
    server = "https://ghcr.io"
    [host."http://10.0.0.1:8083"]
      capabilities = ["pull", "resolve"]
      skip_verify = true    
  gcr.io.toml: |
    server = "https://gcr.io"
    [host."http://10.0.0.1:8084"]
      capabilities = ["pull", "resolve"]
      skip_verify = true    
  registry.k8s.io.toml: |
    server = "https://registry.k8s.io"
    [host."http://10.0.0.1:8085"]
      capabilities = ["pull", "resolve"]
      skip_verify = true    
  quay.io.toml: |
    server = "https://quay.io"
    [host."http://10.0.0.1:8086"]
      capabilities = ["pull", "resolve"]
      skip_verify = true    
  cr.fluentbit.io.toml: |
    server = "https://cr.fluentbit.io"
    [host."http://10.0.0.1:8087"]
      capabilities = ["pull", "resolve"]
      skip_verify = true    
  docker-registry3.mariadb.com.toml: |
    server = "https://docker-registry3.mariadb.com"
    [host."http://10.0.0.1:8088"]
      capabilities = ["pull", "resolve"]
      skip_verify = true    

This secret will be copied for every tenant Kubernetes cluster deployed in Cozystack.

It’s possible to configure registry mirrors for a particular tenant Kubernetes cluster:

  • The tenant cluster must be deployed with a Kubernetes package version 0.23.0 or later, which is available since Cozystack 0.32.0.
  • Before deploying the tenant cluster, create a Kubernetes Secret named kubernetes-<cluster name> with the same contents as shown above.
  • When deploying the tenant cluster, set useCustomSecretForPatchContainerd: true in the values.

To learn more about registry configuration values, read the CRI Plugin configuration guide

Last modified 2025-06-23: Update Talos configuration (ef11e2a)