Setting up Firefly3#

*Kanboard is a lightweight, self-hosted Kanban-style project management tool.

It lets you organize tasks on boards with columns (e.g., To Do → Doing → Done), focusing on simplicity, minimal dependencies, and low resource usage.*

I wanted to show a few services, but for the most part after K3s is up and online the services are self explanitory through code. Each service is likely to vary in its own way, but I wanted to present a few.

hostPath v Local#

One of the biggest differences between this service and the others is the use of hostPath within the PersistentVolume.

Below is one of the better explanations I have seen with a table explaining the differences:

hostPath is a simple, node-specific mount that bypasses scheduler awareness, so Kubernetes does not enforce where Pods land relative to the data. In contrast, a local PersistentVolume is modeled as a node-bound resource, requiring node affinity so the scheduler can place Pods on the correct node. This makes local suitable for multi-node clusters, while hostPath is primarily a single-node or development convenience.

AspecthostPathlocal PersistentVolume
Scheduler awarenessNoYes
Node affinity requiredNoYes
Multi-node safetyNoYes (with constraints)
Typical usageDev / single-node / debuggingProduction local storage
Data locality handlingManualEnforced by scheduler

Based on the above information I will likely swap the other PersistentVolumes to this configuration as this setup will only be running on one host.

To Note and To Fix#

One of the things I want to fix is environment settings for the domain name. I need to be able to pass in the variables for the domain.

This will be something to fix in the near future.

# -------------------------------------------------------------------
# 1. STORAGE: PV and PVC (Points to /var/lib/kanboard)
# -------------------------------------------------------------------
apiVersion: v1
kind: PersistentVolume
metadata:
  name: kanboard-pv
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: kanboard-local
  local:
    path: /var/lib/nik3sx/kanboard
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - l-nodet

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: kanboard-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: kanboard-local
  resources:
    requests:
      storage: 2Gi
---
# -------------------------------------------------------------------
# 2. DATABASE: MariaDB Deployment & Internal Service
# -------------------------------------------------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: db
spec:
  selector:
    matchLabels:
      app: kanboard
      tier: db
  template:
    metadata:
      labels:
        app: kanboard
        tier: db
    spec:
      containers:
        - name: mariadb
          image: mariadb:latest
          args: ["--default-authentication-plugin=mysql_native_password"]
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "secret"
            - name: MYSQL_DATABASE
              value: "kanboard"
            - name: MYSQL_USER
              value: "kb"
            - name: MYSQL_PASSWORD
              value: "kb-secret"
            - name: MARIADB_MYSQL_LOCALHOST_USER
              value: "1"
          ports:
            - containerPort: 3306
          volumeMounts:
            - name: storage
              mountPath: /var/lib/mysql
              subPath: database # Maps to /var/lib/kanboard/database
      volumes:
        - name: storage
          persistentVolumeClaim:
            claimName: kanboard-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: db
spec:
  selector:
    app: kanboard
    tier: db
  ports:
    - protocol: TCP
      port: 3306
      targetPort: 3306
---
# -------------------------------------------------------------------
# 3. APPLICATION: Kanboard Deployment & External Service
# -------------------------------------------------------------------
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kanboard
spec:
  selector:
    matchLabels:
      app: kanboard
      tier: frontend
  template:
    metadata:
      labels:
        app: kanboard
        tier: frontend
    spec:
      containers:
        - name: kanboard
          image: kanboard/kanboard:latest
          env:
            - name: DATABASE_URL
              value: "mysql://kb:kb-secret@db/kanboard"
          ports:
            - containerPort: 80
          volumeMounts:
            - name: storage
              mountPath: /var/www/app/data
              subPath: data # Maps to /var/lib/kanboard/data
            - name: storage
              mountPath: /var/www/app/plugins
              subPath: plugins # Maps to /var/lib/kanboard/plugins
            - name: storage
              mountPath: /etc/nginx/ssl
              subPath: ssl # Maps to /var/lib/kanboard/ssl
      volumes:
        - name: storage
          persistentVolumeClaim:
            claimName: kanboard-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: kanboard
spec:
  type: ClusterIP
  selector:
    app: kanboard
    tier: frontend
  ports:
    - port: 80
      targetPort: 80

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kanboard
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
spec:
  tls:
    - hosts:
        - test-kb.jmoore53.dev
      secretName: boot-unlock-tls
  rules:
    - host: test-kb.jmoore53.dev
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: kanboard
                port:
                  number: 80