Talos Linux: serwer bez SSH i dlaczego to dobrze

Po dwóch tygodniach z k3s przebudowałem domowy klaster od zera na Talos Linux, systemie, na który nie da się zalogować. Trzy maszyny wirtualne zamieniłem w jedną, a cała jej konfiguracja mieści się w jednym pliku w gicie.

· 4 min czytania

Spisane w listopadzie 2026 na podstawie notatek z tamtego czasu.

Na domowym serwerze z IncusOS miałem w sierpniu trzy maszyny wirtualne: starszą z usługami domowymi (Samba, Jellyfin, Frigate), osobną z komunikatorem Matrix i świeżą z k3s, na której uczyłem się Kubernetesa. Każda miała swoje aktualizacje, swoje logi i swoje „tu kiedyś coś zmieniłem ręcznie”. Postanowiłem zastąpić je jedną maszyną z Kubernetesem, a jako system wybrać Talos Linux.

System, na który nie da się zalogować

Talos nie ma SSH, powłoki ani menedżera pakietów. Nie ma nawet bash. Cały system to jądro, kilka usług i Kubernetes, a zarządza się nim wyłącznie przez API z narzędziem talosctl. Konfiguracja maszyny to jeden plik YAML: sieć, dyski, ustawienia kubeleta, rozszerzenia. Zmieniasz plik, wysyłasz go do węzła, węzeł się dostosowuje.

Dla mnie to rozwiązanie konkretnego problemu. Na zwykłym serwerze najgroźniejsze zmiany to te, które ktoś zrobił ręcznie i nigdzie nie zapisał. W Talosie takiej możliwości nie ma. Jeśli coś jest na węźle, to jest w pliku konfiguracyjnym, a ten plik leży w repozytorium. Przy okazji znika cała klasa ataków: nie ma usługi SSH, której trzeba pilnować, i nie ma powłoki, do której ktoś mógłby się dostać.

Zanim zdecydowałem: dwa testy na maszynie do wyrzucenia

Nie chciałem wybierać systemu z opisu na stronie. Dwie rzeczy mogły Talosa zdyskwalifikować, więc sprawdziłem je na tymczasowej maszynie wirtualnej, zanim dotknąłem czegokolwiek prawdziwego.

Pierwsza to dane. Zdjęcia, pliki i nagrania leżą na lustrze ZFS z dwóch dysków na hoście, a maszyny wirtualne widzą je przez virtiofs. Dopiero niedawne wydania Talosa mają dokument ExternalVolumeConfig, który montuje taki wolumen. Test: pod zapisał plik, a talosctl read zobaczył go po stronie węzła.

Druga to karta graficzna. Jellyfin i Frigate potrzebują sprzętowego dekodowania wideo. Talos nie pozwala doinstalować sterownika ręcznie, ale Image Factory buduje obraz systemu z wybranymi rozszerzeniami, w moim przypadku z firmware’em dla zintegrowanej grafiki Intela. Przy okazji wyszło na jaw, że poprzednia maszyna nigdy nie miała dostępu do prawdziwego GPU, tylko do wirtualnej karty, więc Frigate przez cały czas dekodował obraz na procesorze.

Oba testy przeszły i dopiero wtedy decyzja zapadła.

Cała maszyna w jednym pliku

Tak wygląda fragment łatki konfiguracji (nazwy uogólnione). Trzy dokumenty w jednym pliku: nazwa hosta, wolumen z RAID-u przez virtiofs i osobny dysk wybrany wyrażeniem, a nie nazwą urządzenia.

# Nowsze wydania Talosa generują ten dokument domyślnie: nazwę hosta ustawia się tu,
# a nie w machine.network (oba naraz nie przejdą walidacji)
apiVersion: v1alpha1
kind: HostnameConfig
auto: "off"
hostname: lab-node
---
# wolumen z hosta przez virtiofs, montowany w /var/mnt/data
apiVersion: v1alpha1
kind: ExternalVolumeConfig
name: data
filesystemType: virtiofs
mount:
  virtiofs:
    tag: incus_data
---
# osobny dysk na duże dane: wybrany po rozmiarze, nie po /dev/sdX, który potrafi się zmienić
apiVersion: v1alpha1
kind: UserVolumeConfig
name: bulk
provisioning:
  diskSelector:
    match: '!system_disk && disk.size > 149u * GiB && disk.size < 151u * GiB'
  minSize: 149GiB
  grow: true
filesystem:
  type: xfs

Sekrety klastra (PKI i plik dostępowy do API) leżą w tym samym repozytorium, ale zaszyfrowane git-cryptem. Łatki są jawne, żeby dało się je czytać w przeglądzie zmian.

Typowa zmiana wygląda tak:

Najpierw podgląd różnic, potem próba z automatycznym wycofaniem, dopiero potem na stałe
$ talosctl -n $NODE apply-config --file rendered/controlplane.yaml --dry-run
$ talosctl -n $NODE apply-config --file rendered/controlplane.yaml --mode=try
$ talosctl -n $NODE apply-config --file rendered/controlplane.yaml --mode=no-reboot
# zamiast ssh i less: logi i pliki też idą przez API
$ talosctl -n $NODE dmesg | tail
$ talosctl -n $NODE read /var/mnt/data/test.txt

Co się zmienia, gdy nie ma hosta

Brak powłoki ma konsekwencje, o których warto wiedzieć przed migracją. CI, które budowało obrazy na hoście przez gniazdo Dockera, nie ma tu czego użyć, więc runner musiał zamieszkać w klastrze, w osobnym namespace z podwyższonymi uprawnieniami. Talos domyślnie wymusza PodSecurity na poziomie „baseline”, więc każdy pod z hostPath albo dostępem do /dev/dri wymaga świadomego oznaczenia namespace’u jako uprzywilejowanego. To dobrze: wyjątki są widoczne w gicie, a nie rozsiane po systemie.

Migrację rozpisałem na fazy i zrobiłem w dwa dni, 7 i 8 sierpnia: platforma, aplikacje z danymi, usługi z dawnego hosta, ruch z internetu razem z komunikatorem, sprzątanie. Stare maszyny zatrzymałem i zostawiłem jako zabezpieczenie na wypadek powrotu.

Czego się nauczyłem

  • Testuj rzeczy dyskwalifikujące, zanim się zaangażujesz. Test na maszynie do wyrzucenia odpowiedział na pytania, na które żaden opis nie odpowie, i przy okazji odkryła problem z GPU, który istniał od dawna.
  • Wersja klienta musi pasować do wersji klastra. talosctl wpisuje do renderowanej konfiguracji obraz instalatora we własnej wersji. Nowszy klient po cichu wygeneruje więc konfigurację, która przy następnej instalacji węzła podniesie system i Kubernetesa. Renderuję zawsze tą wersją, na której stoi klaster.
  • „Bez SSH” to nie mniej kontroli, tylko kontrola w jednym miejscu. Wszystko, co mogłem zrobić ręcznie na serwerze, robię teraz przez plik w repozytorium, a to oznacza historię zmian, przegląd i powtarzalność.

Wszystkie wpisy