Teaching myself Kubernetes, live
Monitoring started as a Docker Compose stack: Prometheus, Grafana, node_exporter and mysqld_exporter, with named volumes and automatic restarts. When I wanted to learn Kubernetes, I moved that stack over in phases, with zero downtime.
I installed a lightweight k3s cluster right next to the existing Compose stack, and switched off its bundled Traefik ingress so it wouldn’t fight Apache for ports. Each service became proper Kubernetes objects (Deployments, a DaemonSet for node_exporter, persistent volume claims), while the Compose stack kept running as a live fallback the whole time.
Grafana’s database (dashboards, alert rules, contact points) moved over with a careful stop, copy and restart, losing nothing. Then Apache’s reverse proxy cut over to the new Grafana, and once everything checked out end to end, the old Compose stack was retired.
Working on the net, with a safety net
Backups are encrypted with restic and shipped over SFTP to a second physical machine. The SSH key they use is restricted to SFTP only, so even if it leaked it couldn’t be used for anything else.
A backup job that finishes isn’t the same as a backup that works. So every month, an automated job restores the database dumps into throwaway databases and checks file counts against the live data, catching real corruption instead of just a happy exit code. systemd timers run the whole routine (nightly backups, weekly integrity checks, monthly restore tests) with tiered daily, weekly and monthly retention so storage doesn’t grow forever.
What gets backed up lives in one short config file that every script reads, so adding a new project to the regimen is a single edit.