Using k3s-lab with a Private Infra Repo
This guide explains how to use k3s-lab as a shared toolkit from a private repository that holds only your personal configuration — your IPs, domains, passwords, and custom app manifests.
Architecture overview
k3s-lab (public) infra (private)
────────────────────────────── ──────────────────────────────────
ansible/roles/ Makefile ← thin wrapper
makefiles/ .env ← your secrets
charts/ (Helm charts) terraform/ ← Hetzner VPS
lib/ ansible/ ← inventory + group_vars
kubernetes/ (Kustomize bases) apps/ ← your apps (GitOps)
tests/ platform/ ← chart value overrides
argocd/ ← ApplicationSets
secrets/ ← ExternalSecrets
.mk-cache/ ← auto-fetchedRule: Every file you edit lives in infra/. You never touch k3s-lab/ for daily use.
| Repo | What it holds | You edit? |
|---|---|---|
k3s-lab | Ansible roles, scripts, makefiles, manifests, tests | Only to improve the toolkit |
infra | Terraform, Ansible inventory, .env, your app manifests, Makefile | Yes — always |
k3s-lab makefiles are fetched on-demand via curl into infra/.mk-cache/ the first time you run any make target. No clone, no submodule.
1 — Bootstrap your infra repo
1.1 Create the repo
mkdir ~/dev/infra && cd ~/dev/infra
git init1.2 Create the Makefile
# infra/Makefile
.DEFAULT_GOAL := help
SHELL := /bin/bash
-include .env
export
# ── Your values ───────────────────────────────────────────────────────────────
SSH_USER ?= youruser
SSH_PORT ?= 22
SSH_KEY ?= $(HOME)/.ssh/id_ed25519
SSH_KEY := $(subst ~,$(HOME),$(SSH_KEY))
INITIAL_USER ?= root
SERVER_IP ?=
AGENT_IP ?=
KUBECONFIG_CONTEXT ?= k3s-infra
K3S_VERSION ?= v1.32.2+k3s1
# ── k3s-lab source ─────────────────────────────────────────────────────────────
K3S_LAB :=
K3S_LAB_RAW := https://raw.githubusercontent.com/KevinDeBenedetti/k3s-lab/main
# ── Terminal colors ───────────────────────────────────────────────────────────
GREEN := \033[0;32m
YELLOW := \033[1;33m
CYAN := \033[0;36m
RED := \033[0;31m
RESET := \033[0m
# ── Shared makefiles (auto-fetched from k3s-lab) ──────────────────────────────
MK_CACHE := .mk-cache
SHARED_MKS := 00-lib 10-help 40-kubeconfig 45-security 50-deploy \
51-external-dns 52-argocd 55-vault 60-status 70-ssh 80-dev 90-provision
$(foreach f,$(SHARED_MKS),\
$(if $(wildcard $(MK_CACHE)/$(f).mk),,\
$(shell mkdir -p $(MK_CACHE) && \
curl -fsSL $(K3S_LAB_RAW)/makefiles/$(f).mk \
-o $(MK_CACHE)/$(f).mk 2>/dev/null || true)))
-include $(patsubst %,$(MK_CACHE)/%.mk,$(SHARED_MKS))
# ── Cache management ──────────────────────────────────────────────────────────
.PHONY: mk-update mk-clean
mk-update: ## Force re-fetch all shared makefiles from k3s-lab
@echo "$(YELLOW)→ Refreshing shared makefiles from k3s-lab...$(RESET)"
@rm -rf $(MK_CACHE) && mkdir -p $(MK_CACHE)
@$(foreach f,$(SHARED_MKS),\
curl -fsSL $(K3S_LAB_RAW)/makefiles/$(f).mk -o $(MK_CACHE)/$(f).mk \
&& echo " ✓ $(f).mk";)
@echo "$(GREEN)✅ Shared makefiles updated$(RESET)"
mk-clean: ## Remove cached makefiles (re-fetched on next make invocation)
@rm -rf $(MK_CACHE)
@echo "$(GREEN)✅ .mk-cache cleared$(RESET)"1.3 Create .gitignore
# Secrets
.env
# Auto-fetched toolkit makefiles — do not commit
.mk-cache/1.4 Verify all targets are available
make helpYou should see all 40+ targets from k3s-lab alongside your own mk-update / mk-clean.
2 — Personalize your .env
cp /dev/null .env # or copy from k3s-lab's .env.exampleEdit .env with your values — this is the only file that changes between users:
# VPS nodes
SERVER_IP=1.2.3.4
AGENT_IP=5.6.7.8
# SSH
SSH_USER=kevin
SSH_KEY=~/.ssh/id_ed25519
INITIAL_USER=root
# k3s
K3S_VERSION=v1.32.2+k3s1
# K3S_NODE_TOKEN is auto-read from the server by Ansible
# Helm chart versions (pin to avoid surprise upgrades)
TRAEFIK_CHART_VERSION=34.4.0
CERT_MANAGER_VERSION=v1.17.1
KUBE_PROMETHEUS_VERSION=82.10.3
LOKI_VERSION=6.35.1
PROMTAIL_VERSION=6.17.1
# Your domain + Let's Encrypt email
DOMAIN=example.com
EMAIL=you@example.com
# Traefik dashboard
DASHBOARD_DOMAIN=dashboard.example.com
DASHBOARD_PASSWORD=your-secure-password
# Grafana
GRAFANA_DOMAIN=grafana.example.com
GRAFANA_PASSWORD=your-secure-password
# kubectl context name
KUBECONFIG_CONTEXT=k3s-infra⚠️
.envis in.gitignore. It is never committed. Add.env.examplewith placeholder values instead.
See the Configuration reference for every variable.
3 — Deploy the cluster
Once .env is filled, the full deploy is identical to using k3s-lab directly. All targets are available from infra/:
# First time only — full provisioning
make provisionOr step by step:
make provision-server # common + k3s server + wireguard (Ansible)
make provision-agents # join agent to cluster (Ansible)
make kubeconfig # merge ~/.kube/config
kubectl config use-context k3s-infra
make nodes # verify nodes Ready
make deploy-dashboard-secret
make deploy # Traefik + cert-manager + ClusterIssuers
make deploy-grafana-secret
make deploy-monitoring # Prometheus + Grafana + Loki + PromtailSee Getting Started for the full step-by-step walkthrough.
4 — Deploy your own apps
4.1 Using ArgoCD ApplicationSets (recommended)
Create your app manifests in the apps/ directory of your infra repo. ArgoCD ApplicationSets auto-discover new directories and deploy them:
infra/
apps/
myapp/
deployment.yaml
service.yaml
ingress.yamlJust git push — ArgoCD handles the rest. See Deploying an App.
4.2 Adding your own app manifests
Create a directory in your infra repo for app-specific manifests. These are purely yours — they never go into k3s-lab.
infra/
apps/
myapp/
namespace.yaml
deployment.yaml
service.yaml
ingress.yaml ← IngressRoute with ${DOMAIN}
certificate.yaml ← cert-manager CertificateExample IngressRoute + Certificate:
# infra/apps/myapp/ingress.yaml
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-tls
namespace: apps
spec:
secretName: myapp-tls
issuerRef:
name: letsencrypt-production
kind: ClusterIssuer
dnsNames:
- myapp.${DOMAIN}
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: myapp
namespace: apps
spec:
entryPoints:
- websecure
routes:
- match: Host(`myapp.${DOMAIN}`)
kind: Rule
services:
- name: myapp
port: 80
tls:
secretName: myapp-tlsWith ApplicationSets, these manifests are automatically deployed when pushed to git.
4.3 Add a make target for your app (optional)
Note: With ArgoCD ApplicationSets, manual deploy targets are usually unnecessary. Apps in
apps/are auto-discovered and deployed. Use make targets only for non-GitOps workflows or bootstrapping.
5 — Update the toolkit
When k3s-lab ships improvements, update your cache:
make mk-updateThis re-fetches all shared makefiles from k3s-lab@main. Your .env and apps/ manifests are untouched.
Pin to a specific k3s-lab version
To pin to a commit or branch instead of main, change K3S_LAB_RAW in your infra/Makefile:
# Pin to a specific git ref
K3S_LAB_RAW := https://raw.githubusercontent.com/KevinDeBenedetti/k3s-lab/v1.2.0
# Or test against a development branch
K3S_LAB_RAW := https://raw.githubusercontent.com/KevinDeBenedetti/k3s-lab/my-feature-branchThen run make mk-clean && make mk-update to re-fetch for the new ref.
What gets updated vs what stays yours
Updated by make mk-update | Never touched |
|---|---|
.mk-cache/ — all shared makefiles | Makefile |
| Scripts fetched at runtime via curl | .env |
k3s-lab's kubernetes/ Kustomize bases | apps/ — your apps |
k3s-lab's charts/ defaults | platform/ — your overrides |
6 — Update Helm chart versions
Chart versions are pinned in charts/*/Chart.yaml. To upgrade:
Update the version in the chart dependency:
bash# e.g. in charts/platform-monitoring/Chart.yamlLet ArgoCD sync or re-deploy:
bashgit add -A && git commit -m "chore: bump chart versions" && git push
ArgoCD detects the change and syncs automatically.
7 — Override a k3s-lab target
If you need to customize a shared target (e.g. deploy has special requirements for your stack), add it directly in infra/Makefile after the -include block. Make uses the first definition of a target:
# Override the shared deploy target with infra-specific steps
deploy: ## Deploy base stack + myapp
@helm upgrade --install platform-base ./charts/platform-base -n kube-system
@kubectl apply -f apps/myapp/
@echo "$(GREEN)✅ Stack + myapp deployed$(RESET)"⚠️ Override sparingly — the shared targets are maintained and improved in k3s-lab.
8 — Full workflow summary
# ── First time ────────────────────────────────────────────────────────────────
git clone <your-infra-repo> && cd infra
cp .env.example .env # fill in your IPs, domain, passwords
make help # verify all targets loaded from k3s-lab
# ── Provision cluster ─────────────────────────────────────────────────────────
make provision # one-shot: common → k3s → kubeconfig (via Ansible)
# ── Day-to-day ────────────────────────────────────────────────────────────────
make nodes # check node status
make status # check all pod statuses
make deploy-myapp # deploy your custom app
kubectl logs -n apps deploy/myapp --tail=50
# ── Maintain ─────────────────────────────────────────────────────────────────
make mk-update # pull latest makefiles from k3s-lab
# edit .env to bump TRAEFIK_CHART_VERSION=35.0.0
make deploy # apply Helm upgrade
# ── Debug remotely ────────────────────────────────────────────────────────────
make ssh-server # open SSH shell on server VPS
make ssh-agent # open SSH shell on agent VPS