k3s Beginner Journey (From Scratch)¶
Goal: take a brand-new control machine and stand up this full k3s environment end-to-end.
Audience: first-time operators.
Primary mode: run commands in order, do not skip phases.
Journey Map¶
Tags: #journey #order #start-here
- Prepare your control machine.
- Create Proxmox VM templates.
- Configure Terraform variables.
- Provision VMs.
- Bootstrap k3s.
- Set up storage + ingress.
- Create runtime secrets.
- Deploy apps.
- Validate and operate.
Phase 0: Understand What This Repo Does¶
Tags: #phase/00 #concepts #beginner
This repo automates:
- Proxmox VM provisioning with Terraform.
- k3s bootstrap with Ansible.
- MetalLB + Traefik ingress.
- TrueNAS-backed persistent storage.
- App deployments via Kubernetes manifests.
Main entry points:
scripts/00-install-tools.sh-> toolsscripts/01-provision.sh-> VMsscripts/02-cluster-setup.sh-> k3s clusterscripts/03-storage-setup.sh-> NFS + MetalLB + Traefikscripts/04-deploy-apps.sh-> apps
Phase 1: Prepare Control Machine¶
Tags: #phase/01 #prerequisites #macos #linux
Required access¶
- SSH access to Proxmox hosts.
- Proxmox API token.
- SSH key at
~/.ssh/id_ed25519. - TrueNAS SSH access.
Run¶
Success check¶
Phase 2: Create Proxmox Templates (One-Time)¶
Tags: #phase/02 #proxmox #templates
This project supports:
- Template
9000: Ubuntu 22.04 - Template
9001: Debian 12 (recommended interraform.tfvars.example)
Run:
If you want both:
Phase 3: Configure Terraform Inputs¶
Tags: #phase/03 #terraform #configuration
Create your local vars file:
Edit terraform/terraform.tfvars and set at minimum:
proxmox_host1_ipproxmox_host2_ipproxmox_api_tokenssh_public_keyvm_template_idandvm_user(must match)- node IPs and gateway/DNS for your LAN
Security rule:
- Never commit
terraform/terraform.tfvars.
Phase 4: Provision VMs¶
Tags: #phase/04 #terraform #provisioning
Run:
Notes:
- The script runs
terraform init,plan, then asks for confirmation. - Type
yeswhen prompted to apply.
Success check:
Phase 5: Bootstrap k3s¶
Tags: #phase/05 #ansible #k3s
Set a cluster token (20+ characters):
Run:
Success check:
Phase 6: Install Storage + Ingress¶
Tags: #phase/06 #storage #ingress #truenas #metallb #traefik
Run:
What this installs/configures:
- TrueNAS NFS shares.
nfs-subdir-external-provisioner.- MetalLB.
- Traefik.
Success checks:
kubectl get storageclass
kubectl get pods -n storage
kubectl get pods -n metallb-system
kubectl get svc -n traefik
Phase 7: Create Runtime Secrets (Mandatory)¶
Tags: #phase/07 #secrets #security #required
Before app deployment, create required secrets.
Reference:
manifests/secrets/README.md
Examples:
# apps namespace
kubectl create secret generic code-server-secret -n apps \
--from-literal=PASSWORD='CHANGE_ME' \
--dry-run=client -o yaml | kubectl apply -f -
kubectl create secret generic pangolin-secret -n apps \
--from-literal=PANGOLIN_APP_SECRET='CHANGE_ME_LONG_RANDOM_SECRET' \
--dry-run=client -o yaml | kubectl apply -f -
# authentik namespace
kubectl create secret generic authentik-secret -n authentik \
--from-literal=AUTHENTIK_SECRET_KEY='CHANGE_ME_LONG_RANDOM_SECRET' \
--from-literal=POSTGRES_DB='authentik' \
--from-literal=POSTGRES_USER='authentik' \
--from-literal=POSTGRES_PASSWORD='CHANGE_ME' \
--dry-run=client -o yaml | kubectl apply -f -
Phase 8: Deploy Apps¶
Tags: #phase/08 #apps #deployment
Run:
This script:
- Applies namespaces.
- Applies local secret overlays from
manifests/secrets/*.yml(if present). - Applies ingress defaults.
- Deploys app manifests.
- Waits for common deployments to be ready.
Optional after deploy:
Phase 9: Validate Everything¶
Tags: #phase/09 #validation #checklist
Cluster health:
kubectl get nodes -o wide
kubectl get pods -A
kubectl get events -A --field-selector type=Warning --sort-by=.lastTimestamp | tail -n 40
Ingress path:
Manifests + security checks:
cd /Users/dre/k3s-cluster
bash -n scripts/*.sh
pre-commit run --all-files
python3 scripts/security_scrub.py --no-history
kubectl apply --dry-run=server -R -f manifests/apps
Local DNS / Hosts for First Access¶
Tags: #networking #dns #first-login
If you do not yet have internal DNS records, map your Traefik LB IP manually in /etc/hosts:
192.168.100.120 home.lab.local vault.lab.local auth.lab.local n8n.lab.local code.lab.local blog.lab.local obsidian.lab.local forum.lab.local k8s.lab.local
Then test:
https://home.lab.localhttps://k8s.lab.local
Day-2 Operations¶
Tags: #operations #maintenance #day2
Deploy manifest changes:
Periodic checks:
Destructive teardown:
Common Beginner Pitfalls¶
Tags: #troubleshooting #pitfalls
kubectlcannot connect: check~/.kube/configand rerunscripts/02-cluster-setup.sh.- Workers do not join: verify
K3S_TOKENlength and node reachability from Ansible. - PVCs pending: validate NFS and storage provisioner (
kubectl -n storage get pods). - App starts but URL fails: check DNS/hosts and Traefik service IP.
- App deploy fails: missing secret in the app namespace.
Fast Rebuild Checklist¶
Tags: #checklist #recovery #reproducible
- [ ] Tools installed (
00-install-tools.sh) - [ ] Templates created (
00-create-proxmox-template.sh) - [ ]
terraform/terraform.tfvarsfilled locally - [ ] VMs provisioned (
01-provision.sh) - [ ] k3s bootstrapped (
02-cluster-setup.sh) - [ ] Storage/ingress configured (
03-storage-setup.sh) - [ ] Secrets created (
manifests/secrets/README.md) - [ ] Apps deployed (
04-deploy-apps.sh) - [ ] Health validated (
kubectl get pods -A)