Back to list
ionfury

app-template

by ionfury

Tom's Homelab mono repository

22🍴 5📅 Jan 24, 2026

SKILL.md


name: app-template description: | Deploy applications using bjw-s/app-template Helm chart - a flexible chart for helmifying container images without dedicated charts.

Use when: (1) Deploying container images that lack official Helm charts, (2) Creating HelmRelease manifests for Flux GitOps, (3) Configuring multi-container pods with sidecars, (4) Setting up persistent storage, ingress, services for custom applications, (5) Questions about app-template values structure or patterns, (6) Deploying any custom container to Kubernetes.

Triggers: "deploy with app-template", "helmify this image", "create helm release for", "app-template values", "sidecar container", "multi-container pod helm", "deploy container image", "no helm chart available", "custom container deployment", "bjw-s", "app-template chart", "deploy docker image to kubernetes", "container without helm chart", "generic helm chart"

app-template Helm Chart

The bjw-s/app-template chart deploys containerized applications without requiring a dedicated Helm chart. It provides a declarative interface for common Kubernetes resources.

Chart source: oci://ghcr.io/bjw-s-labs/helm/app-template Schema: https://raw.githubusercontent.com/bjw-s-labs/helm-charts/app-template-4.6.0/charts/other/app-template/values.schema.json

Quick Start

Minimal values.yaml for a single-container deployment:

# yaml-language-server: $schema=https://raw.githubusercontent.com/bjw-s-labs/helm-charts/app-template-4.6.0/charts/other/app-template/values.schema.json
controllers:
  main:
    containers:
      main:
        image:
          repository: nginx
          tag: latest

service:
  main:
    controller: main
    ports:
      http:
        port: 80

Core Structure

Controllers

Controllers define workload types. Each controller creates one Pod spec.

controllers:
  main:                              # Controller identifier (arbitrary name)
    type: deployment                 # deployment|statefulset|daemonset|cronjob|job
    replicas: 1
    strategy: Recreate               # Recreate|RollingUpdate (deployment)

    # Pod-level settings
    pod:
      securityContext:
        fsGroup: 568
        fsGroupChangePolicy: OnRootMismatch

    containers:
      main:                          # Container identifier
        image:
          repository: ghcr.io/org/app
          tag: v1.0.0
        env:
          TZ: UTC
          CONFIG_PATH: /config

Multiple Controllers

Create separate deployments in one release:

controllers:
  web:
    containers:
      main:
        image:
          repository: nginx
          tag: latest

  worker:
    type: deployment
    replicas: 3
    containers:
      main:
        image:
          repository: myapp/worker
          tag: v1.0.0

Sidecar Containers

Add sidecars with dependsOn for ordering:

controllers:
  main:
    containers:
      main:
        image:
          repository: myapp
          tag: v1.0.0

      sidecar:
        dependsOn: main              # Start after main container
        image:
          repository: sidecar-image
          tag: latest
        args: ["--config", "/config/sidecar.yaml"]

Services

Services expose controller pods. Link via controller field.

service:
  main:
    controller: main                 # Links to controllers.main
    type: ClusterIP                  # ClusterIP|LoadBalancer|NodePort
    ports:
      http:
        port: 8080
      metrics:
        port: 9090

  websocket:
    controller: main
    ports:
      ws:
        port: 3012

Ingress

ingress:
  main:
    className: nginx
    hosts:
      - host: app.example.com
        paths:
          - path: /
            pathType: Prefix
            service:
              identifier: main       # References service.main
              port: http             # References port name
    tls:
      - hosts:
          - app.example.com
        secretName: app-tls

Multiple paths to different services:

ingress:
  main:
    hosts:
      - host: app.example.com
        paths:
          - path: /
            service:
              identifier: main
              port: http
          - path: /ws
            service:
              identifier: websocket
              port: ws

Persistence

PersistentVolumeClaim

persistence:
  config:
    type: persistentVolumeClaim
    accessMode: ReadWriteOnce
    size: 1Gi
    globalMounts:
      - path: /config

Existing PVC

persistence:
  config:
    existingClaim: my-existing-pvc
    globalMounts:
      - path: /config

NFS Mount

persistence:
  backup:
    type: nfs
    server: nas.local
    path: /volume/backups
    globalMounts:
      - path: /backup

EmptyDir (Shared Between Containers)

persistence:
  shared-data:
    type: emptyDir
    globalMounts:
      - path: /shared

Advanced Mounts (Per-Controller/Container)

persistence:
  config:
    existingClaim: app-config
    advancedMounts:
      main:                          # Controller identifier
        main:                        # Container identifier
          - path: /config
        sidecar:
          - path: /config
            readOnly: true

Environment Variables

Direct Values

controllers:
  main:
    containers:
      main:
        env:
          TZ: UTC
          LOG_LEVEL: info
          TEMPLATE_VAR: "{{ .Release.Name }}"

From Secrets/ConfigMaps

controllers:
  main:
    containers:
      main:
        env:
          DATABASE_URL:
            valueFrom:
              secretKeyRef:
                name: db-secret
                key: url
        envFrom:
          - secretRef:
              name: app-secrets
          - configMapRef:
              name: app-config

Security Context

Pod-Level

defaultPodOptions:
  securityContext:
    runAsUser: 568
    runAsGroup: 568
    fsGroup: 568
    fsGroupChangePolicy: OnRootMismatch

controllers:
  main:
    containers:
      main:
        image:
          repository: myapp
          tag: v1.0.0

Container-Level (Privileged Sidecar)

controllers:
  main:
    containers:
      main:
        securityContext:
          runAsUser: 568
          runAsGroup: 568

      vpn:
        image:
          repository: vpn-client
          tag: latest
        securityContext:
          capabilities:
            add:
              - NET_ADMIN

Probes

Default probes use TCP on the primary service port. Customize:

controllers:
  main:
    containers:
      main:
        probes:
          liveness:
            enabled: true
            custom: true
            spec:
              httpGet:
                path: /health
                port: 8080
              initialDelaySeconds: 10
              periodSeconds: 30
          readiness:
            enabled: true
            type: HTTP
            spec:
              path: /ready
              port: 8080
          startup:
            enabled: false

StatefulSet with VolumeClaimTemplates

controllers:
  main:
    type: statefulset
    statefulset:
      volumeClaimTemplates:
        - name: data
          accessMode: ReadWriteOnce
          size: 10Gi
          globalMounts:
            - path: /data

CronJob

controllers:
  backup:
    type: cronjob
    cronjob:
      schedule: "0 2 * * *"
      concurrencyPolicy: Forbid
      successfulJobsHistory: 3
      failedJobsHistory: 1
    containers:
      main:
        image:
          repository: backup-tool
          tag: v1.0.0
        args: ["--backup", "/data"]

ServiceMonitor (Prometheus)

serviceMonitor:
  main:
    enabled: true
    serviceName: main
    endpoints:
      - port: metrics
        scheme: http
        path: /metrics
        interval: 30s

Flux HelmRelease Integration

For this homelab, app-template deploys via Flux ResourceSet. Add to kubernetes/platform/helm-charts.yaml:

# In resourcesTemplate, the pattern generates HelmRelease automatically
# For app-template specifically, add to inputs:
- name: "my-app"
  namespace: "default"
  chart:
    name: "app-template"
    version: "4.6.0"
    url: "oci://ghcr.io/bjw-s-labs/helm"  # Note: OCI registry
  dependsOn: [cilium]

Values go in kubernetes/platform/charts/my-app.yaml.

Common Patterns

See references/patterns.md for:

  • VPN sidecar with gluetun
  • Code-server sidecar for config editing
  • Multi-service applications (websocket + http)
  • Init containers for setup tasks

See references/values-reference.md for complete values.yaml documentation.

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon