# Case Study: Migrating a 19-Service Self-Hosted Stack from Docker Compose to a Tailscale-Private K3s Cluster

**Author:** Javid (Project Level Up Javid)
**Date:** 2026-08-17 (start) — ongoing
**Status:** ACTIVE — Week 1 of 5-week migration
**Project Type:** Career-dev portfolio piece + CKA exam prep

---

## 🎯 The objective

Migrate 19 self-hosted services from Docker Compose to a 2-node k3s cluster, where:

1. **Both nodes are Tailscale-only** — zero public ports on the new node
2. **Orchestration happens from one box** — this server is the mission control
3. **The cluster doubles as a CKA exam lab** — covers ~80% of exam domains
4. **Risk is minimized** — Docker Compose keeps running in parallel throughout

This is a Project Level Up Javid deliverable — career dev, not just sysadmin.

---

## 🧠 Why this approach (and why it matters for career dev)

The Project Level Up Javid goal: transition from "sysadmin who runs a homelab" to "Senior Platform Engineer with production-grade skills." The standard career advice is "spin up Kubernetes," but most paths either:

- Cost $50+/month for a managed cluster (CloudSQL + EKS + etc.)
- Use public IPs that contradict zero-public-port stack invariants
- Single-node toy clusters that don't teach real cluster ops

**Path A+ solves all three**: Tailscale mesh makes a 2-node cluster free (or near-free), private, and operationally identical to how production private clusters actually work (Teleport, WireGuard, Headscale).

The narrative for hiring managers: "I migrated 19 production services to k3s, with multi-node HA, Tailscale CNI, Longhorn replicated storage, and a 5-minute rollback strategy — using only Tailscale for node-to-node networking."

That's a senior-tier story. **Not a junior story.**

---

## 📦 What's being migrated

### Inventory (snapshot 2026-08-17)

19 Docker services + 13 Tailscale sidecars = 32 containers total.

**Top RAM users (current state):**
| Service | RAM |
|---|---|
| openotebk-surrealdb-1 | 911MiB |
| openotebk-open_notebook-1 | 696MiB |
| app-authentik | 485MiB |
| app-homarr | 410MiB |
| n8n | 400MiB |
| worker-authentik | 306MiB |
| vectordb-embeddings | 289MiB |
| vectordb-qdrant | 134MiB |
| db-authentik | 122MiB |
| app-gitea | 111MiB |
| (others <100MiB each) | — |
| **Total** | ~5.5GiB |

### Migration groups (5 weeks)

1. **Admin UIs** (homarr, portainer, pocket-id, glances, redisinsight) — ~800Mi, stateless
2. **Stateful** (redis, gitea, postgres, authentik-redis) — DB dumps required
3. **Critical** (authentik, open_notebook, n8n, vectordb) — 30-60min downtime each
4. **Long-tail** (audiobookshelf, ignis, agent-vault)
5. **Stays on host** (crowdsec + bouncer — kernel-level)

---

## 🏗️ Architecture decision: why Tailscale mesh, not Flannel

k3s by default uses **Flannel** as the CNI (Container Network Interface), which creates a VXLAN overlay network between pods. This works, but:

- VXLAN traffic goes through the kernel network stack
- Adds complexity (MTU tuning, VXLAN encapsulation)
- Doesn't teach you anything about modern private-cluster networking

**Tailscale CNI** replaces Flannel with:
- WireGuard-encrypted pod-to-pod traffic
- Zero additional firewall rules needed
- Real-world pattern: many production clusters run this way (Teleport, Tailscale, Headscale are all variations)

**Educational value:** Migrating from Flannel to Tailscale CNI teaches you modern service mesh patterns that you'd use at any senior-tier job.

---

## 📅 Timeline

| Week | Phase | Status |
|---|---|---|
| **1** | Provision 2nd node, install k3s, Tailscale ACL | 🔄 In progress |
| **2** | 5 admin UIs to k3s | ⏳ Pending |
| **3** | 4 stateful services to k3s | ⏳ Pending |
| **4** | 4 critical services to k3s | ⏳ Pending |
| **5** | Long-tail cleanup | ⏳ Pending |
| **6+** | CKA exam study using live cluster | ⏳ Pending |

---

## 🛡️ Rollback strategy

The single most important property of this plan: **Docker Compose keeps running in parallel throughout.**

At any point, if k3s fails:
```bash
ssh cp-1 "sudo systemctl stop k3s"     # 30 seconds
sudo systemctl stop k3s-agent            # 10 seconds
docker compose ps                         # all 19 services STILL HEALTHY
```

That's it. Zero data loss. Five minutes total.

This is what makes the plan risk-free: every k3s experiment is reversible in less time than it takes to make a coffee.

---

## 💰 Cost analysis

| Path | Monthly cost | One-time | Reversible |
|---|---|---|---|
| Contabo public VPS | $5-8/mo | $0 | Yes |
| **Old laptop (Path A+)** | **$0** | **$0** | **Yes** |
| Raspberry Pi 5 8Gi | $0 | $80 | Yes |
| Managed k8s (EKS/GKE) | $75-150/mo | $0 | Yes (with effort) |

Path A+ is the lowest-cost option that still gives production-grade multi-node k3s experience.

---

## 🎓 CKA exam alignment

After completion of this migration, you'll have hands-on experience with:

| Domain | Weight | Coverage |
|---|---|---|
| Cluster Architecture, Install, Configure | 15% | ✅ Multi-node k3s, swap disabled, systemd unit management |
| Workloads & Scheduling | 20% | ✅ Deployments, StatefulSets, DaemonSets |
| Services & Networking | 20% | ✅ Tailscale CNI, ClusterIP, NetworkPolicy |
| Storage | 10% | ✅ Longhorn PVs/PVCs/StorageClasses |
| Troubleshooting | 30% | ✅ Built into every phase (kubectl logs, debug, exec) |

**Total: ~80% of CKA exam material** is hands-on practiced, not theoretical.

---

## 🚀 Why this works as a portfolio piece

A hiring manager reading this case study sees:

1. **Real migration experience** — not a tutorial, actual production code
2. **Risk management thinking** — rollback strategy, parallel run, phased rollout
3. **Modern infrastructure choices** — Tailscale mesh, Longhorn storage, GitOps-ready
4. **Cost-conscious engineering** — Path A+ beats Contabo + EKS by 90%
5. **Career alignment** — directly maps to CKA cert + Senior PE job tier

This is the difference between "I have a homelab" and "I run a production-grade platform."

---

## 📊 Success metrics

The migration is successful when:

- [ ] All 19 services running in k3s (excluding crowdsec on host)
- [ ] Zero downtime longer than 60 min for any service during migration
- [ ] Rollback tested and works in <5 min
- [ ] CKA exam passed (target: Q2 2027)
- [ ] Blog post published: "Migrating 19 services from Docker Compose to k3s"
- [ ] Cluster IDP/Backstage layer added (Phase 6)

---

## 📂 Related files

- `k3s-migration-plan.md` — Step-by-step install commands
- `k3s-acl.json` — Tailscale ACL policy
- `~/.hermes/career-levelup-javid/` — Tracking directory in git

---

## 🎯 What's next

1. **Provision the 2nd node** (any old laptop, Pi, or VPS)
2. **Install Tailscale + tag as k3s-cp**
3. **Apply k3s-acl.json** to the Tailnet
4. **Run the install commands** from this box via SSH

Once the cluster is up, this case study gets updated with:
- Real benchmarks (k3s install time, first pod launch, etc.)
- Lessons learned (what surprised me)
- Comparison: docker compose vs k3s for the same workload
- Cost/time savings after 30 days in production

---

*This case study is part of Project Level Up Javid — career development through real engineering, not just certifications. The cert (CKA) is the proof; the journey is the substance.*