---
title: "Setup K3s Single Node Cluster, GitOps dengan Flux"
description: "Seperti yang saya ceritakan sebelum-nya disini dan disini,\nsaya sedang mengembangkan Homelab pribadi untuk men-support workflow development sehari-hari.\nSalah satu komponen penting dari setup ini adal..."
publishedAt: 2026-02-20
locale: id
urlSlug: setup-k3s-single-node-gitops-dengan-flux
isDraft: false
defaultLocale: en
---
[TOC]

## Latar Belakang

Seperti yang saya ceritakan sebelum-nya [disini](membangun-local-kubernetes-development-dengan-lima-vm) dan [disini](linux-minipc-untuk-remote-dev),
saya sedang mengembangkan Homelab pribadi untuk men-support workflow development sehari-hari.
Salah satu komponen penting dari setup ini adalah bagaimana saya mengelola deployment aplikasi
ke cluster Kubernetes dengan cara yang lebih deklaratif menggunakan konsep GitOps.

GitOps adalah cara mengelola infrastruktur dan aplikasi melalui Git, di mana state yang diinginkan
didefinisikan di repository dan tool seperti Flux akan memastikan cluster sesuai dengan definisi tersebut.

Ada beberapa tool GitOps yang populer saat ini seperti [ArgoCD](https://argo-cd.readthedocs.io/) dan [Flux](https://fluxcd.io/).
Saya memilih Flux karena integrasi native-nya dengan SOPS untuk enkripsi secret, serta kemampuan image automation
yang memungkinkan deployment otomatis ketika ada image Docker baru di push ke registry.

Namun saya tidak bisa share 1:1 bagaimana setup saya karena repo github nya saya buat private.
Untuk itu saya coba replicate bagaimana saya setup awal, barangkali temen2 disini ada yang tertarik untuk bikin serupa wqwq.

## Persyaratan
Install:
- lima-vm
- kubectl
- sops
- flux
- [age](https://github.com/FiloSottile/age)

Semua tools diatas bisa di-install manual atau pakai [arkade](https://github.com/alexellis/arkade),
saya sendiri nyaman pakai arkade. Contohnya:

```bash
arkade get kubectl flux sops
```

terkait lima-vm, bisa cek tulisan saya sebelum-nya [membangun-local-kubernetes-development-dengan-lima-vm](membangun-local-kubernetes-development-dengan-lima-vm).

## Setup K3s Single Node

Buat dulu lima-vm instance-nya
```bash
limactl create template:k3s \
    --name=k3s-demo \
    --cpus=2 \
    --memory=2 \
    --disk=20 \
    --yes
```

setelah instance dibuat, output dibagian akhir akan menunjukan file kubeconfig yang akan digunakan untuk mengatur cluster lewat `kubectl`

```bash
export KUBECONFIG="/home/jimbo/.lima/k3s-demo/copied-from-guest/kubeconfig.yaml"
kubectl...
```

### Manajemen Environment Variable

pastikan untuk export variable diatas sebelum execute perintah terkait `kubectl`, buat kenyamanan saya saran pakai [direnv](https://github.com/direnv/direnv)

buat file `.direnv` seperti ini
```bash
export KUBECONFIG="/home/jimbo/.lima/k3s-demo/copied-from-guest/kubeconfig.yaml"
```

semua variable yang diperlukan, nanti kita bisa tambahkan seiring dengan keperluan di file diatas.

### Cek koneksi ke k3s cluster dengan kubectl

Eksekusi perintah berikut
```bash
$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:6443
CoreDNS is running at https://127.0.0.1:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://127.0.0.1:6443/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
```

## Installing Flux CD

Buatlah sebuah git repo, yang nanti-nya repo ini adalah sebagai source of truth dari state k8s yang kita manage, ini repo contoh yang saya buat https://github.com/wayanjimmy/k3s-demo.

Sekarang kita perlu boostrap fluxcd dulu ke k3s cluster nya.
```bash
flux bootstrap git \
    --url=ssh://git@github.com/wayanjimmy/k3s-demo.git \
    --private-key-file=/home/jimbo/.ssh/id_ed25519 \
    --branch=main \
    --path=clusters/k3s-demo \
    --components-extra image-reflector-controller,image-automation-controller
```

pastikan ssh key yang kita gunakan sudah terdaftar di github, sehingga proses boostrap dapat mengakses repo, setelah bootstrap berhasil, eksekusi perintah berikut memastikan flux sudah ready.

```bash
$ flux check
 ► checking prerequisites
 ✔ Kubernetes 1.34.4+k3s1 >=1.32.0-0
 ► checking version in cluster
 ✔ distribution: flux-v2.7.5
 ✔ bootstrapped: true
 ► checking controllers
 ✔ helm-controller: deployment ready
 ► ghcr.io/fluxcd/helm-controller:v1.4.5
 ✔ image-automation-controller: deployment ready
 ► ghcr.io/fluxcd/image-automation-controller:v1.0.4
 ✔ image-reflector-controller: deployment ready
 ► ghcr.io/fluxcd/image-reflector-controller:v1.0.4
 ✔ kustomize-controller: deployment ready
 ► ghcr.io/fluxcd/kustomize-controller:v1.7.3
 ✔ notification-controller: deployment ready
 ► ghcr.io/fluxcd/notification-controller:v1.7.5
 ✔ source-controller: deployment ready
 ► ghcr.io/fluxcd/source-controller:v1.7.4
 ► checking crds
 ✔ alerts.notification.toolkit.fluxcd.io/v1beta3
 ✔ buckets.source.toolkit.fluxcd.io/v1
 ✔ externalartifacts.source.toolkit.fluxcd.io/v1
 ✔ gitrepositories.source.toolkit.fluxcd.io/v1
 ✔ helmcharts.source.toolkit.fluxcd.io/v1
 ✔ helmreleases.helm.toolkit.fluxcd.io/v2
 ✔ helmrepositories.source.toolkit.fluxcd.io/v1
 ✔ imagepolicies.image.toolkit.fluxcd.io/v1
 ✔ imagerepositories.image.toolkit.fluxcd.io/v1
 ✔ imageupdateautomations.image.toolkit.fluxcd.io/v1
 ✔ kustomizations.kustomize.toolkit.fluxcd.io/v1
 ✔ ocirepositories.source.toolkit.fluxcd.io/v1
 ✔ providers.notification.toolkit.fluxcd.io/v1beta3
 ✔ receivers.notification.toolkit.fluxcd.io/v1
 ✔ all checks passed
```

kalau mau cek workload terkait flux bisa pakai perintah berikut

```bash
$ kubectl get pods -n flux-system
NAME                                       READY   STATUS    RESTARTS   AGE
helm-controller-68578f8447-t6v2k           1/1     Running   0          64s
kustomize-controller-7ddfbb5875-jvlvm      1/1     Running   0          64s
notification-controller-6d766f87cf-5lrvf   1/1     Running   0          64s
source-controller-6679d8bdb-dgdgs          1/1     Running   0          64s
```

sampai disini flux sudah terinstal, lanjut ke proses deployment apps.

## Testing deploy Aplikasi Demo

Kita akan menulis yaml menggunakan kustomization, yg merupakan semacam layer abstraksi untuk manajemen yaml k8s, namun tidak akan dibahas ditulisan ini.

Buat file berikut.

clusters/k3s-demo/demo-apps-kustomization.yaml
```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: demo-apps
  namespace: flux-system
spec:
  interval: 1m
  path: ./clusters/k3s-demo/demo-apps
  prune: true
  sourceRef:
    kind: GitRepository
    name: flux-system
  # decryption:
  #   provider: sops
  #   secretRef:
  #     name: sops-age
```

clusters/k3s-demo/demo-apps/namespace.yaml
```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: demo-apps
```

clusters/k3s-demo/demo-apps/nginx-demo.yaml
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-demo
  namespace: demo-apps
  labels:
    app: nginx-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-demo
  template:
    metadata:
      labels:
        app: nginx-demo
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-demo
  namespace: demo-apps
spec:
  type: NodePort
  selector:
    app: nginx-demo
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30081
```

clusters/k3s-demo/demo-apps/kustomization.yaml
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: demo-apps
resources:
- namespace.yaml
- nginx-demo.yaml
# - secret.yaml
```

Deployment nginx-demo sudah di expose melalui nodeport sehingga nanti kita bisa cek melalui port tersebut apakah aplikasi berjalan normal.

Nah bagaimana cara deployment apps ini melalui flow git ops ?
Flux akan secara periodik, berdasarkan interval `clusters/k3s-demo/demo-apps-kustomization.yaml`, memastikan apakah definisi git repo sudah sync dengan state yang sebenarnya.

Yang perlu kita lakukan hanyalah commit semua file yaml ini dan push ke git, berikut adalah gambaran kondisi repo setelah melakukan semua file diatas di-commit [https://github.com/wayanjimmy/k3s-demo/tree/81635f0f880c53a63bf8c8c53752b5d1b873a416/clusters/k3s-demo](https://github.com/wayanjimmy/k3s-demo/tree/81635f0f880c53a63bf8c8c53752b5d1b873a416/clusters/k3s-demo)

Tunggu beberapa saat maka flux akan rekonsiliasi state-nya dan pod akan muncul

```bash
$ kubectl get pods -n demo-apps
NAME                          READY   STATUS    RESTARTS   AGE
nginx-demo-6fd7fbb8f4-4jhjj   1/1     Running   0          136m
```


## Enkripsi Secret dengan SOPS

Flux memiliki integrasi dengan sops yang bisa kita gunakan agar secret yang di commit ke git repo tidak telanjang.
Untuk referensi lebih lanjut tentang Flux dan SOPS, bisa cek tulisan [Budiman JoJo](https://budimanjojo.com/2021/10/23/flux-secret-management-with-sops-age/).

```bash
mkdir -p secrets
```

tambahkan .gitignore di di folder secrets menghindari private key yg akan kita generate ikut ke-commit.

secrets/.gitignore
```text
*
!.gitignore
```

Generate sops private & public key pair
```bash
$ age-keygen -o secrets/flux-age-key.txt
```

kalau kamu cek struktur file `flux-age-key.txt` akan seperti ini

```text
# created: 2026-02-20T07:19:42Z
# public key: age1h44rlvy9c2ffytt78msmhjtuyuwtpyc6jje7fgjkg4x7l0hpt5dsfxlf9v
AGE-SECRET-KEY-1ECFASJKZTGVTM0K2GV4QYUXGSKL0S2DG43SWV68GWLC455LAFMYQSLGHQA
```

ada public key dan secret key, public key bisa kita tambahkan di-repo agar semua orang bisa melakukan enkripsi, sedangkan private akan kita tambahkan sebagai secret di k8s cluster, agar cluster dapat melakukan dekripsi.

```bash
$ kubectl create secret generic sops-age \
     --namespace=flux-system \
     --from-file=age.agekey=$(pwd)/secrets/flux-age-key.txt
secret/sops-age created
```

Pastikan secret sops-age sudah terbuat

```bash
$ kubectl get secret sops-age -n flux-system
NAME       TYPE     DATA   AGE
sops-age   Opaque   1      2m28s
```

Ok ditahap ini kita tinggal mengenalkan ke demo-apps kustomization agar mengenali sops-age sebagai key untuk decrypt secret, hilangkan tanda komentar terkait dekripsi yg sebelum-nya dalam tag komentar.

clusters/k3s-demo/demo-apps-kustomization.yaml
```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: demo-apps
  namespace: flux-system
spec:
  interval: 1m
  path: ./clusters/k3s-demo/demo-apps
  prune: true
  sourceRef:
    kind: GitRepository
    name: flux-system
  decryption:                       # [!code ++]
    provider: sops                  # [!code ++]
    secretRef:                      # [!code ++]
      name: sops-age                # [!code ++]
```

commit semua changes dan push dan tunggu hingga flux melakukan rekonsiliasi, kamu bisa watch dengan perintah flux untuk lebih yakin.

```bash
$ flux get kustomizations -n flux-system --watch
NAME            REVISION                SUSPENDED       READY   MESSAGE
demo-apps       main@sha1:f33a03f7      False           True    Applied revision: main@sha1:f33a03f7
flux-system     main@sha1:f33a03f7      False   True    Applied revision: main@sha1:f33a03f7
```

Jika sudah delete pods agar konfigurasi yang baru ter-aplikasi di pod yang baru

```bash
$ kubectl delete pods -l app=demo-app -n demo-apps
```

### Memastikan Pod bisa Decrypt Secret

Sampai pada titik ini kita tinggal memastikan apakah workload demo-apps sudah bisa melakukan decrypt terhadap secret yang sudah di encrypt menggunakan sops.

Buat file secret.

```bash
$ cat clusters/k3s-demo/demo-apps/secret.yaml
apiVersion: v1
stringData:
  database: somedatabase
  password: secret
  username: admin
kind: Secret
metadata:
  name: db-credentials
  namespace: demo-apps
```

Encrypt file secret dengan sops

```bash
$ sops --encrypt \
    --age $(echo $SOPS_PUBLIC_KEY) \
    --encrypted-regex '^(data|stringData)$' \
    --in-place clusters/k3s-demo/demo-apps/secret.yaml
```

setelah itu file akan ter-enkripsi seperti [berikut](https://github.com/wayanjimmy/k3s-demo/blob/main/clusters/k3s-demo/demo-apps/secret.yaml)

daftar-kan secret

clusters/k3s-demo/demo-apps/kustomization.yaml
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: demo-apps
resources:
- namespace.yaml
- nginx-demo.yaml
- secret.yaml                    # [!code ++]
```

Nah bagaimana memastikan kalau pod sudah dapat mengakses secret dalam keadaan decrypted?
Kita bisa tambah initContainers yang mana containers ini akan dijalankan terlebih dahulu sebelum containers nginx dalam nginx-demo dijalankan.

clusters/k3s-demo/demo-apps/nginx-demo.yaml
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-demo
  namespace: demo-apps
  labels:
    app: nginx-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-demo
  template:
    metadata:
      labels:
        app: nginx-demo
    spec:
      initContainers:                    # [!code ++]
      - name: secret-test                # [!code ++]
        image: busybox:1.36             # [!code ++]
        command:                         # [!code ++]
        - sh                             # [!code ++]
        - -c                             # [!code ++]
        - |                              # [!code ++]
          echo "=== SOPS Secret Decryption Test ==="
          echo "DB_HOST: $DB_HOST"
        envFrom:                         # [!code ++]
        - secretRef:                     # [!code ++]
            name: db-credentials         # [!code ++]
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-demo
  namespace: demo-apps
spec:
  type: NodePort
  selector:
    app: nginx-demo
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30081
```

Commit semua changes dan push, tunggu flux rekonsiliasi, kamu bisa watch log init containers tadi dengan perintah 

```bash
jimbo@ser4:~/.../k3s-demo$ kubectl logs -f -l app=nginx-demo -c secret-test -n demo-apps
=== SOPS Secret Decryption Test ===
DB_HOST: postgres.demo-apps.svc.cluster.local
```

nah disana kamu akan lihat kalau env `DB_HOST` dapat diakses dalam keadaan decrypted, sedangkan secret yang di git repo juga aman dalam keadaan encrypted.

Sampai saat ini proses setup flux dengan sops sudah beres!

## Kesimpulan

Sampai disini kita sudah memiliki single-node Kubernetes cluster yang dikelola dengan GitOps
menggunakan Flux. Dengan setup ini, semua deployment aplikasi dapat dilakukan hanya dengan
git push, sementara secret tetap aman dengan enkripsi SOPS.

Kombinasi k3s + lima-vm sangat cocok untuk ngoprek Homelab tanpa harus punya hardware khusus,
semuanya bisa berjalan di laptop yang kita gunakan sehari-hari.
