Getting Started
This guide walks you through provisioning a production-ready k3s cluster from two fresh VPS nodes.
Prerequisites
Local machine
| Tool | Purpose | Install |
|---|---|---|
kubectl | Kubernetes CLI | brew install kubectl |
helm | Helm package manager | brew install helm |
ssh / scp | VPS access | Pre-installed on macOS/Linux |
htpasswd | BasicAuth secret generation | brew install httpd |
envsubst | Variable substitution in manifests | brew install gettext |
bats | Optional: run tests locally | brew install bats-core |
VPS nodes
| Requirement | Value |
|---|---|
| OS | Ubuntu 22.04+ or Debian 12+ |
| Architecture | x86_64 or ARM64 |
| Master: vCPU / RAM | 2 vCPU / 2 GB minimum (4 GB recommended) |
| Worker: vCPU / RAM | 2 vCPU / 1 GB minimum (2 GB recommended) |
| Disk | 20 GB+ (master), 20 GB+ (worker) |
| Public IP | Required on each node |
| DNS | A records pointing to MASTER_IP for all subdomains |
DNS records (before deploy)
Set the following A records to MASTER_IP:
dashboard.example.com → MASTER_IP
grafana.example.com → MASTER_IP
app.example.com → MASTER_IP (your apps)DNS must propagate before TLS certificates can be issued. Use
dig dashboard.example.comto verify.
Step 1 — Configure environment
cp .env.example .envEdit .env and fill in at minimum:
MASTER_IP=1.2.3.4
WORKER_IP=5.6.7.8
SSH_USER=ubuntu
SSH_KEY=~/.ssh/id_ed25519
K3S_VERSION=v1.32.2+k3s1
DOMAIN=example.com
EMAIL=you@example.com
DASHBOARD_DOMAIN=dashboard.example.com
DASHBOARD_PASSWORD=your-secure-password
GRAFANA_DOMAIN=grafana.example.com
GRAFANA_PASSWORD=your-secure-password
KUBECONFIG_CONTEXT=k3s-labSee Configuration for the full variable reference.
Step 2 — Bootstrap master node
make k3s-masterThis installs k3s server on MASTER_IP with:
- Traefik and built-in LB disabled (managed via Helm)
--tls-sanset to the public IP for remotekubectlaccess- Secrets encryption at rest
- Flannel VXLAN overlay network
- UFW rules for HTTP/HTTPS/API server
When it completes, K3S_NODE_TOKEN is automatically saved to .env.
⏱️ Takes ~5 minutes on a typical VPS.
Step 3 — Join worker node
make k3s-workerThis:
- Opens the master UFW for the worker IP (VXLAN + kubelet ports)
- Installs k3s agent on
WORKER_IP
⏱️ Takes ~3 minutes.
Step 4 — Fetch kubeconfig
make kubeconfig
kubectl config use-context k3s-labVerify both nodes are ready:
make nodes
# NAME STATUS ROLES AGE VERSION
# master Ready control-plane,master 5m v1.32.2+k3s1
# worker Ready <none> 2m v1.32.2+k3s1Step 5 — Deploy base stack
Create the dashboard secret
make deploy-dashboard-secretDeploy Traefik + cert-manager
make deployThis deploys in order:
- Namespaces —
ingress,cert-manager,monitoring,apps - Traefik — Helm chart with values from
kubernetes/ingress/traefik-values.yaml - cert-manager — with CRDs installed
- ClusterIssuers — Let's Encrypt staging + production
- Traefik dashboard — secured IngressRoute at
DASHBOARD_DOMAIN
⏱️ Takes ~3 minutes. TLS certificate issuance happens in the background and takes ~30s after DNS resolves.
Step 6 — Deploy monitoring
Create Grafana admin secret
make deploy-grafana-secretDeploy observability stack
make deploy-monitoringThis deploys:
- kube-prometheus-stack — Prometheus + Grafana + Alertmanager
- Loki — centralized log storage
- Promtail — log collector DaemonSet
- Grafana IngressRoute — HTTPS at
GRAFANA_DOMAIN
⏱️ Takes ~10 minutes (large chart images).
Step 7 — Verify
make statusAll pods should be Running or Completed.
Access points:
| Service | URL | Credentials |
|---|---|---|
| Traefik dashboard | https://DASHBOARD_DOMAIN/dashboard/ | admin / DASHBOARD_PASSWORD |
| Grafana | https://GRAFANA_DOMAIN | admin / GRAFANA_PASSWORD |
| Prometheus | kubectl port-forward svc/prometheus-operated -n monitoring 9090:9090 | — |
Deploy an example app
The kubernetes/apps/ directory contains an example app:
# Deploy example app with ingress + TLS
envsubst < kubernetes/apps/deployment.yaml | kubectl apply -f -
envsubst < kubernetes/apps/service-ingress.yaml | kubectl apply -f -See Traefik → Deploying your own services for the IngressRoute pattern.
Next steps
- Configuration reference — all
.envvariables - k3s details — install flags, firewall, sysctl
- Traefik — IngressRoute, middlewares, TLS
- cert-manager — Let's Encrypt, staging vs production
- Monitoring — Grafana dashboards, LogQL, Prometheus
- Make targets — full reference
- Troubleshooting — common issues