> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siderolabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# UpCloud

> Creating a cluster via the CLI (upctl) on UpCloud.com.

export const release_v1_13 = 'v1.13.0';

export const VersionWarningBanner = () => {
  const latestVersion = "v1.13";
  const [latestUrl, setLatestUrl] = useState(null);
  const [currentVersion, setCurrentVersion] = useState(null);
  const [isBeta, setIsBeta] = useState(false);
  const parseVersion = v => v.replace("v", "").split(".").map(Number);
  const isGreaterVersion = (a, b) => {
    const [aMajor, aMinor] = parseVersion(a);
    const [bMajor, bMinor] = parseVersion(b);
    if (aMajor > bMajor) return true;
    if (aMajor === bMajor && aMinor > bMinor) return true;
    return false;
  };
  useEffect(() => {
    if (typeof window === "undefined") return;
    const {pathname, hash, search} = window.location;
    const match = pathname.match(/\/talos\/(v\d+\.\d+)\//);
    if (!match) return;
    const detectedVersion = match[1];
    if (detectedVersion === latestVersion) return;
    setCurrentVersion(detectedVersion);
    if (isGreaterVersion(detectedVersion, latestVersion)) {
      setIsBeta(true);
    }
    const newPath = pathname.replace(`/talos/${detectedVersion}/`, `/talos/${latestVersion}/`);
    setLatestUrl(`${newPath}${search}${hash}`);
  }, []);
  if (!latestUrl || !currentVersion) return null;
  return <div className="not-prose sticky top-6 z-50 my-6">
      <div className="border border-yellow-500/30 bg-yellow-500/10 px-4 py-3 rounded-xl">
        <div className="text-sm">
          {isBeta ? <>
              ⚠️ You are viewing a <strong>beta version</strong> of Talos ({currentVersion}).
              This version may be unstable.
              <a href={latestUrl} className="ml-2 underline text-yellow-400 hover:text-yellow-300 font-medium">
                View latest stable version {latestVersion} →
              </a>
            </> : <>
              ⚠️ You are viewing an older version of Talos ({currentVersion}).
              <a href={latestUrl} className="ml-2 underline text-yellow-400 hover:text-yellow-300 font-medium">
                View the latest version {latestVersion} →
              </a>
            </>}
        </div>
      </div>
    </div>;
};

<VersionWarningBanner />

In this guide we will create an HA Kubernetes cluster 3 control plane nodes and 1 worker node.
We assume some familiarity with UpCloud.
If you need more information on UpCloud specifics, please see the [official UpCloud documentation](https://upcloud.com/resources/docs).

## Create the image

The best way to create an image for UpCloud, is to build one using
[Hashicorp packer](https://www.packer.io/docs/builders/hetzner-cloud), with the
`upcloud-amd64.raw.xz` image available from the [Image Factory](https://factory.talos.dev/).
Using the general ISO is also possible, but the UpCloud image has some UpCloud
specific features implemented, such as the fetching of metadata and user data to configure the nodes.

To create the cluster, you need a few things locally installed:

1. [UpCloud CLI](https://github.com/UpCloudLtd/upcloud-cli)
2. [Hashicorp Packer](https://learn.hashicorp.com/tutorials/packer/get-started-install-cli)

> NOTE: Make sure your account allows API connections.
> To do so, log into
> [UpCloud control panel](https://hub.upcloud.com/login) and go to **People**
> -> **Account** -> **Permissions** -> **Allow API connections** checkbox.
> It is recommended
> to create a separate subaccount for your API access and *only* set the API permission.

To use the UpCloud CLI, you need to create a config in `$HOME/.config/upctl.yaml`

```yaml theme={null}
username: your_upcloud_username
password: your_upcloud_password
```

To use the UpCloud packer plugin, you need to also export these credentials to your
environment variables, by e.g. putting the following in your `.bashrc` or `.zshrc`

```shell theme={null}
export UPCLOUD_USERNAME="<username>"
export UPCLOUD_PASSWORD="<password>"
```

Next create a config file for packer to use:

<CodeBlock lang="hcl">
  {`
    # upcloud.pkr.hcl

    packer {
    required_plugins {
      upcloud = {
        version = ">=v1.0.0"
        source  = "github.com/UpCloudLtd/upcloud"
      }
    }
    }

    variable "talos_version" {
    type    = string
    default = "${release_v1_13}"
    }

    locals {
    image = "https://factory.talos.dev/image/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba/\${var.talos_version}/upcloud-amd64.raw.xz"
    }

    variable "username" {
    type        = string
    description = "UpCloud API username"
    default     = "\${env("UPCLOUD_USERNAME")}"
    }

    variable "password" {
    type        = string
    description = "UpCloud API password"
    default     = "\${env("UPCLOUD_PASSWORD")}"
    sensitive   = true
    }

    source "upcloud" "talos" {
    username        = "\${var.username}"
    password        = "\${var.password}"
    zone            = "us-nyc1"
    storage_name    = "Debian GNU/Linux 11 (Bullseye)"
    template_name   = "Talos (\${var.talos_version})"
    }

    build {
    sources = ["source.upcloud.talos"]

    provisioner "shell" {
      inline = [
        "apt-get install -y wget xz-utils",
        "wget -q -O /tmp/talos.raw.xz \${local.image}",
        "xz -d -c /tmp/talos.raw.xz | dd of=/dev/vda",
      ]
    }

    provisioner "shell-local" {
        inline = [
        "upctl server stop --type hard custom",
        ]
    }
    }
    `}
</CodeBlock>

Now create a new image by issuing the commands shown below.

```bash theme={null}
packer init .
packer build .
```

After doing this, you can find the custom image in the console interface under storage.

## Creating a cluster via the CLI

### Create an endpoint

To communicate with the Talos cluster you will need a single endpoint that is used
to access the cluster.
This can either be a loadbalancer that will sit in front of
all your control plane nodes, a DNS name with one or more A or AAAA records pointing
to the control plane nodes, or directly the IP of a control plane node.

Which option is best for you will depend on your needs.
Endpoint selection has been further documented [here](../../getting-started/getting-started#decide-the-kubernetes-endpoint).

After you decide on which endpoint to use, note down the domain name or IP, as
we will need it in the next step.

### Create the machine configuration files

#### Generating base configurations

Using the DNS name of the endpoint created earlier, generate the base
configuration files for the Talos machines:

```bash theme={null}
$ talosctl gen config talos-upcloud-tutorial https://<load balancer IP or DNS>:<port> --install-disk /dev/vda
created controlplane.yaml
created worker.yaml
created talosconfig
```

At this point, you can modify the generated configs to your liking.
Depending on the Kubernetes version you want to run, you might need to select a different Talos version, as not all versions are compatible.
You can find the support matrix [here](../../getting-started/support-matrix).

Optionally, you can specify [machine configuration patches](../../configure-your-talos-cluster/system-configuration/patching#configuration-patching-with-talosctl-cli)
which will be applied during the config generation.

#### Validate the configuration files

```bash theme={null}
$ talosctl validate --config controlplane.yaml --mode cloud
controlplane.yaml is valid for cloud mode
$ talosctl validate --config worker.yaml --mode cloud
worker.yaml is valid for cloud mode
```

### Create the servers

#### Create the control plane nodes

Run the following to create three total control plane nodes:

<CodeBlock lang="sh">
  {`
    for ID in $(seq 3); do
      upctl server create \
        --zone us-nyc1 \
        --title talos-us-nyc1-master-$ID \
        --hostname talos-us-nyc1-master-$ID \
        --plan 2xCPU-4GB \
        --os "Talos (${release_v1_13})" \
        --user-data "$(cat controlplane.yaml)" \
        --enable-metada
    done
    `}
</CodeBlock>

> Note: modify the zone and OS depending on your preferences.
> The OS should match the template name generated with packer in the previous step.

Note the IP address of the first control plane node, as we will need it later.

#### Create the worker nodes

Run the following to create a worker node:

<CodeBlock lang="sh">
  {`
    upctl server create \
    --zone us-nyc1 \
    --title talos-us-nyc1-worker-1 \
    --hostname talos-us-nyc1-worker-1 \
    --plan 2xCPU-4GB \
    --os "Talos (${release_v1_13})" \
    --user-data "$(cat worker.yaml)" \
    --enable-metada
    `}
</CodeBlock>

### Bootstrap etcd

To configure `talosctl` we will need the first control plane node's IP, as noted earlier.
We only add one node IP, as that is the entry into our cluster against which our commands will be run.
All requests to other nodes are proxied through the endpoint, and therefore not
all nodes need to be manually added to the config.
You don't want to run your commands against all nodes, as this can destroy your
cluster if you are not careful [(further documentation)](../../getting-started/getting-started#configure-your-talosctl-client).

Set the `endpoints` and `nodes`:

```bash theme={null}
talosctl --talosconfig talosconfig config endpoint <control plane 1 IP>
talosctl --talosconfig talosconfig config node <control plane 1 IP>
```

Bootstrap `etcd`:

```bash theme={null}
talosctl --talosconfig talosconfig bootstrap
```

### Retrieve the `kubeconfig`

At this point we can retrieve the admin `kubeconfig` by running:

```bash theme={null}
talosctl --talosconfig talosconfig kubeconfig
```

It will take a few minutes before Kubernetes has been fully bootstrapped, and is accessible.

You can check if the nodes are registered in Talos by running

```bash theme={null}
talosctl --talosconfig talosconfig get members
```

To check if your nodes are ready, run

```bash theme={null}
kubectl get nodes
```
