Self-hosted service
k0sproject/k0sctl avatar
k0sproject/k0sctl

k0sctl: declarative bootstrapping for k0s Kubernetes clusters

A bootstrapping and management tool for k0s clusters.

588 stars115 forksGoNOASSERTION

At a glance

What is it?
k0sctl is a command line tool that applies a YAML description of a k0s cluster to a set of hosts, connecting over SSH, detecting what is there, installing binaries and joining workers in a documented sequence of phases. It is a stateful tool, which is its strength and also its risk, and the restore path has a limitation worth reading before you need it.
Who is it for?
k0sctl suits an operator who wants k0s clusters described in one YAML file and applied to bare or cloud hosts over SSH, and who is comfortable with a tool that reconfigures machines rather than only creating resources. It does not suit a team that wants a declarative cloud resource graph, since k0sctl manages the hosts it is given rather than the cloud around them, and it is a poor fit if you need a restore that can move a cluster to a different control plane address.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One file describes the cluster, the hosts are the input

k0sctl is a command-line bootstrapping and management tool for k0s zero friction Kubernetes clusters. Its main function is the `k0sctl apply` subcommand: provided a configuration file describing the desired cluster state, k0sctl will connect to the listed hosts, determine the current state of the hosts and configure them as needed to form a k0s cluster.

The default location for the configuration file is `k0sctl.yaml` in the current working directory, and a different location can be given with:

sh
k0sctl apply --config path/to/k0sctl.yaml

The state model is what makes this more than a bootstrap script. If the configuration's cluster version `spec.k0s.version` is greater than the version detected on the cluster, a cluster upgrade is performed. If the configuration lists hosts that are not part of the cluster, they are configured to run k0s and joined to the cluster. In other words, the file is a description of the desired end state and the tool reconciles towards it, which is the same contract a configuration management tool offers and a different contract from a script that provisions a cluster once.

The repository is a Go project with a `cmd/`, `configurer/`, `phase/`, `pkg/`, `integration/`, `action/`, `smoke-test/` and `version/` layout, plus a `Dockerfile`, a `Makefile` and an `AGENTS.md` that documents the repository layout, development and testing guidelines, including notes for AI assistants. Example Terraform and bootloose configurations live in `examples/`, with `aws-tf/`, `bootloose/` and `hetzner-tf/` in the tree.

The phase output tells you what it will do to your machines

The example output in the README is the most useful description of what the tool does, because it lists the phases in order:

text
INFO ==> Running phase: Connect to hosts
INFO ==> Running phase: Detect host operating systems
INFO [ssh] 10.0.0.1:22: is running Ubuntu 20.10
INFO [ssh] 10.0.0.2:22: is running Ubuntu 20.10
INFO ==> Running phase: Prepare hosts
INFO ==> Running phase: Gather host facts
INFO [ssh] 10.0.0.1:22: discovered 10.12.18.133 as private address
INFO ==> Running phase: Validate hosts
INFO ==> Running phase: Gather k0s facts
INFO ==> Running phase: Download k0s binaries on hosts
INFO ==> Running phase: Configure k0s

The second half of the run is the cluster itself:

text
INFO ==> Running phase: Configure k0s
INFO ==> Running phase: Initialize the k0s cluster
INFO [ssh] 10.0.0.1:22: installing k0s controller
INFO ==> Running phase: Install workers
INFO [ssh] 10.0.0.1:22: generating token
INFO [ssh] 10.0.0.2:22: installing k0s worker
INFO [ssh] 10.0.0.2:22: waiting for node to become ready
INFO ==> Running phase: Disconnect from hosts

Two things are visible there. First, the tool reaches the hosts over SSH and asks them what they are, since the detection phase reports the operating system of each one, and it discovers the private address of the host as part of gathering host facts. Second, the phases are separated by a validation step, and the workers are installed one by one with a token generated for the cluster and a wait for the node to become ready.

The structure is worth reading as a safety property rather than a log format. The tool does not begin installing anything before it has connected, detected, prepared and validated the hosts, and the cluster is initialised before workers join it. For an operator, that ordering is what makes a partially failed run recoverable, since the tool can be re-run against the same description and pick up where the previous run stopped.

The final lines of the example show the finish and the next step:

text
INFO ==> Finished in 2m2s
INFO k0s cluster version 1.22.3+k0s.0 is now installed
INFO Tip: To access the cluster you can now fetch the admin kubeconfig using:
INFO      k0sctl kubeconfig

init writes the file, apply reconciles it

The other half of the workflow is `k0sctl init`, which generates a configuration template. Use `--k0s` to include an example `spec.k0s.config` k0s configuration block, and you can supply a list of host addresses via arguments or via stdin.

A minimal configuration is written like this:

sh
k0sctl init > k0sctl.yaml

and the same command with the example k0s configuration included:

sh
k0sctl init --k0s > k0sctl.yaml

The third form is the one worth internalising, because it removes the file from the loop entirely. A list of host addresses is piped straight into apply:

sh
k0sctl init 10.0.0.1 10.0.0.2 [email protected]:8022 | k0sctl apply --config -

That form works because apply accepts `--config -` to read from standard input, and the host syntax carries the user and port inline, as in `[email protected]:8022`.

So the tool offers two levels of ceremony. The piped one-liner is for the first cluster on a laptop, and the checked-in `k0sctl.yaml` is for a cluster whose state you want to keep under version control and reapply. The other subcommands fill out the management side: `k0sctl reset` uninstalls k0s from the hosts listed in the configuration, and `k0sctl kubeconfig` connects to the cluster and outputs a kubeconfig file.

Backup and restore, and the externalAddress limitation

The `k0sctl backup & restore` command takes a backup of the cluster control plane state into the current working directory. The files are named with a running unix epoch timestamp, for example `k0s_backup_1623220591.tar.gz`, which means a directory of backups sorts chronologically without any extra bookkeeping.

Restoring is not a separate command. It is done as part of `k0sctl apply` with the `--restore-from` flag:

The flag is `--restore-from k0s_backup_1623220591.tar.gz`, and the command that carries it is `k0sctl apply`.

What is restored is the full control plane state: the etcd datastore content, the certificates and the keys. The documentation is clear about the intent, that restore is intended to be used as a disaster recovery mechanism and thus it expects that no k0s components actually exist on the controllers.

Then comes the limitation, and it is the one to read before you need the feature. The control plane address, `externalAddress`, needs to remain the same between backup and restore, and the reason given is that all worker node components connect to this address and cannot currently be re-configured.

That is a real constraint and it is not a detail you can work around from the configuration side. A cluster that has to move to a new control plane address, for example because the machines behind it changed, cannot be restored as it stands, and the workers will be looking for a control plane that is no longer there. Anyone planning a migration rather than a recovery should treat that as a blocker and confirm the current state of the project before depending on it.

Install paths: binaries, packages and a container

The installation section is thorough, which is appropriate for a tool that touches every machine in a cluster.

From the released binaries, you download the version for your operating system and processor architecture from the releases page, make the file executable and place it in a directory available in your `$PATH`. There is one wrinkle: as the released binaries aren't signed yet, on macOS and Windows you must first run the executable via Open in the context menu and allow running it.

From source, with a working Go toolchain, the documented command is:

sh
go install github.com/k0sproject/k0sctl@latest

which installs to your `$GOPATH/bin`.

The package manager list is long, and one note governs all of them: all packages except for Homebrew and WinGet are community maintained, and any issues should be reported to the respective package maintainers. The commands are `brew install k0sproject/tap/k0sctl` for macOS and Linux, `nix profile install nixpkgs#k0sctl` for Nixpkgs, `apk add k0sctl` from the Alpine community repositories, `zypper install k0sctl` for openSUSE Tumbleweed, `yay -S k0sctl-bin` or `paru -S k0sctl-bin` for the AUR, `choco install k0sctl` for Chocolatey, and `winget install k0sproject.k0sctl` for WinGet.

The container image is published as `ghcr.io/k0sproject/k0sctl:latest`, and the documented use is a backup taken in a container with the working directory and the configuration file mounted:

sh
# pull the image
docker pull ghcr.io/k0sproject/k0sctl:latest

# create a backup
docker run -it --workdir /backup \
  -v ./backup:/backup \
  -v ./k0sctl.yaml:/etc/k0s/k0sctl.yaml \
  ghcr.io/k0sproject/k0sctl:latest k0sctl backup --config /etc/k0s/k0sctl.yaml

There are also shell completions for bash, zsh and fish, each generated by the same command and redirected into the conventional location for that shell, which is a small convenience that matters when you use the tool on many machines.

The build, the licence and what is under the hood

The project states its development status plainly: k0sctl is ready for use and in continuous development. The repository is not archived and the last push was on 2026-10-19, with tagged releases v0.33.1 on 2026-09-21, v0.33.0 on 2026-09-11 and v0.32.2 on 2026-07-28.

The Go module targets a recent toolchain, with `go 1.27.0` and a `toolchain go1.27.1` directive. The dependency list gives a sense of what the tool is built on: `urfave/cli/v2` for the command line, `logrus` and `samber/slog-logrus/v2` for logging, `gopkg.in/yaml.v2` for the configuration, `k8s.io/client-go` and `k8s.io/apimachinery` for talking to Kubernetes, `k0sproject/rig` and `k0sproject/dig` for configuration and dependency injection, `jellydator/validation` for validating the configuration, and `AlecAivazis/survey/v2` for interactive prompts.

The Dockerfile shows the packaging intent. The build stage compiles a static binary with `CGO_ENABLED=0`, the `netgo`, `osusergo` and `static_build` build tags, and linker flags that strip symbols and inject the environment, the git commit and the tag name into the version information. The runtime stage is a plain Alpine image with `tini`, `openssh` and `ca-certificates` added, the binary copied in, `tini` as the entrypoint and a non-root user `65534:65534`.

The licence needs a note. The project is recorded with a custom licence that GitHub cannot classify, with the LICENSE file named as the place to look, and the README does not state the terms. That is the file to read before you put k0sctl into a build pipeline, and it is also the one piece of the evaluation that this documentation does not answer.

Editorial conclusion

k0sctl suits an operator who wants k0s clusters described in one YAML file and applied to bare or cloud hosts over SSH, and who is comfortable with a tool that reconfigures machines rather than only creating resources. It does not suit a team that wants a declarative cloud resource graph, since k0sctl manages the hosts it is given rather than the cloud around them, and it is a poor fit if you need a restore that can move a cluster to a different control plane address. Before relying on it, read the known restore limitation about externalAddress, keep the backup file naming convention in mind, and check the licence file in the repository, since the project is recorded with a custom licence that GitHub cannot classify.

Frequently asked questions

What does k0sctl apply actually do to my hosts?

It connects to the listed hosts over SSH, detects their operating systems, prepares them, gathers and validates host facts, downloads the k0s binaries, configures k0s, initializes the cluster, installs the workers and waits for each node to become ready, then disconnects. The phases are run in that order and the cluster is initialised before workers join.

Where does k0sctl look for its configuration file?

The default is `k0sctl.yaml` in the current working directory, and a different file can be given with `--config path/to/k0sctl.yaml`. You can also read the configuration from standard input with `--config -`, which is what the piped `k0sctl init` to `k0sctl apply` example relies on.

Can a k0s cluster be restored to a different control plane address?

Not with the current restore process. The documentation lists this as a known limitation: the control plane address `externalAddress` needs to remain the same between backup and restore, because all worker node components connect to it and cannot currently be re-configured.

Which package managers provide k0sctl?

Homebrew, Nixpkgs, Alpine, openSUSE, the AUR, Chocolatey and WinGet are listed, and the container image is on GitHub Container Registry as `ghcr.io/k0sproject/k0sctl:latest`. All packages except Homebrew and WinGet are community maintained, and issues should go to the respective maintainers.

How do I back up and restore a k0s cluster with k0sctl?

The backup command writes the control plane state into the current working directory as a timestamped archive such as `k0s_backup_1623220591.tar.gz`, covering the etcd datastore content, the certificates and the keys. Restoring is done through `k0sctl apply --restore-from k0s_backup_1623220591.tar.gz`, and it expects that no k0s components exist on the controllers.

Official sources

  1. Issues
  2. k0sproject/k0sctl on GitHub
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/k0sproject-k0sctl.svg)](https://hysenlabs.com/projects/k0sproject-k0sctl)

Was this useful?