- Shell 100%
| helm | ||
| manifests | ||
| install.sh | ||
| README.md | ||
k8s-dev
Getting started
Storage
This example requires an LVM volume group named k8s-storage to be created before running the install.
There should be at least 100GBs of available storage!
You can either create this Volume Group during installation, or to create it after the fact on a spare drive:
pvcreate /dev/sdb
vgcreate k8s-storage /dev/sdb
You should be able to run vgs and see the k8s-storage volume group as available -
# vgs
VG #PV #LV #SN Attr VSize VFree
k8s-storage 1 0 0 wz--n- <100.00g <100.00g
Network Configuration
k8s nodes do not like it when they change IP addresses. It's best to create a static DHCP reservation or use static IPs when deploying k8s.
Manifest deployments
Manifest deployments utilize kubectl and a group of manifest files to deploy services.
Deploy Entire Service
kubectl apply -f manifests/jellyfin
Deploy individual manifests
kubectl apply -f manifests/jellyfin/namespace.yaml
Deploy ALL services at once
kubectl apply --recursive -f manifests
To Remove Manifest Deployments
You can use the same syntax but with delete instead of apply. i.e. -
kubectl delete -f manifests/jellyfin
Or deleting the namespace for a deployment will remove all the resources contained within.
kubectl delete namespace jellyfin
Helm deployments
Helm is a tool used to simply deployments. You can use Helm 'Charts', which automatically handle the resources required for a deployment.
You may need to modify the default behavior of a Helm Chart. To do so, you'll use a 'values.yaml` file.
I've created bash scripts with examples of Helm deployments. Check out helm/zabbix for a good example.
cd helm/zabbix
./install.sh
Deployed Resources
This repo automatically sets up Pi-Hole running within the cluster. It also utilizes External-DNS to add local records to the Pi-Hole deployment. As such, you can point your laptop's DNS nameservers to your k8s-dev server to automatically enable DNS names for the following resources:
| Service | Desc | DNS Hostname | Creds |
|---|---|---|---|
| garage-s3 | S3 Storage Provider | s3.local | Auto-Generated |
| grafana | Grafana Dashboard | grafana.local | admin/admin |
| hastebin | Web Text Editor/Pastebin Alt | hastebin.local | N/A |
| jellyfin | Media Server | jellyfin.local | Created during first visit |
| nextcloud | Cloud Storage | nextcloud.local | admin/password (Defined in deployment.yaml) |
| pihole | Pi-Hole Admin Dashboard | pihole.local | password |
| plex | Media Server | plex.local | N/A |
| zabbix | Monitoring Service | zabbix.local | Admin/Zabbix |
We also deploy a Wireguard Operator which dynamically allocates Load Balancer ports.
Cronjobs
We install a couple cronjobs to show examples of how they work. They can be found in manifests/cronjob
Here's an example of viewing their output -
root@k8s-dev:~/k8s-dev/manifests/cronjobs# kubectl get cronjob
NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE
curl-an-api */5 * * * * <none> False 2 65s 8m15s
do-some-math * * * * * <none> False 0 5s 28m
root@k8s-dev:~/k8s-dev/manifests/cronjobs# kubectl get pods
NAME READY STATUS RESTARTS AGE
curl-an-api-29451211-2c5lc 0/1 CrashLoopBackOff 5 (2m ago) 5m10s
curl-an-api-29451212-72995 0/1 CrashLoopBackOff 5 (78s ago) 4m10s
curl-an-api-29451215-5w94w 0/1 Completed 0 70s
do-some-math-29451214-dbb6k 0/1 Completed 0 2m10s
do-some-math-29451215-rvjtj 0/1 Completed 0 70s
do-some-math-29451216-bjqqw 0/1 Completed 0 10s
root@k8s-dev:~/k8s-dev/manifests/cronjobs# kubectl logs curl-an-api-29451215-5w94w
{"latitude":52.52,"longitude":13.419998,"generationtime_ms":0.024199485778808594,"utc_offset_seconds":0,"timezone":"GMT","timezone_abbreviation":"GMT","elevation":38.0,"current_units":{"time":"iso8601","interval":"seconds","temperature_2m":"°F"},"current":{"time":"2025-12-30T05:30","interval":900,"temperature_2m":30.7}}