Back to Posts

    Migrating Homelab Workloads from Docker Compose to Talos Kubernetes

    Posted November 8, 2025Updated May 12, 2026

    Why I moved from a single Docker Compose host to a 3-node Talos cluster, what broke in the process, and the surprising places the migration actually paid off.

    TalosKubernetesCiliumLonghornProxmox
    // Impact: Zero-downtime workload migration; cluster upgrades are now boring

    Migrating Homelab Workloads from Docker Compose to Talos Kubernetes

    Why migrate

    The Docker Compose setup was fine for a long time. The reason I moved was operational: every OS upgrade on the host became a nervous ritual where I'd take everything down, hope apt upgrade didn't break Docker, and bring things back up. With a multi-node cluster, host upgrades become rolling — drain one node, upgrade it, move on. No more all-or-nothing windows.

    Why Talos specifically

    Talos is a Linux distro where SSH is intentionally absent. You can't log into the nodes. Configuration is declarative (YAML), applied over a gRPC API. Sounds inconvenient until you realize that "I can't SSH in" is also "an attacker can't SSH in" and "I can't accidentally rm -rf something."

    OS upgrades become talosctl upgrade --image ghcr.io/siderolabs/installer:v1.x.y and you're done. The OS itself is read-only; there's nothing to drift.

    What broke

    • Storage. Compose volumes are local-disk and "just work." Kubernetes storage classes are not. I tried Longhorn first; it works, but on consumer hardware with mixed disks the replica placement becomes tricky.
    • Reverse proxy. Caddy as a single binary is delightful. The Kubernetes equivalent (Ingress + cert-manager + a controller) is more pieces but the resulting setup is actually more maintainable.
    • Backups. Compose backups were "tar the volumes directory." Cluster backups need Velero or similar, and you have to think about whether you're backing up the workloads or the cluster state or both.

    What surprised me

    Service discovery just works. Every pod gets a DNS name. No more reading docker-compose.yml to remember what hostname the database is on.

    // © 2026 Chisom Onuegbu