Files
apps/AGENTS.md
Morten Olsen d5a0803eee add database
2026-01-01 20:34:24 +01:00

13 KiB

Application Helm Charts Guide

This document provides guidelines for creating and maintaining Helm charts in this homelab project.

Project Structure

apps/
├── charts/           # Individual application Helm charts
│   ├── app-name/
│   │   ├── Chart.yaml
│   │   ├── values.yaml
│   │   └── templates/
│   │       ├── deployment.yaml
│   │       ├── service.yaml
│   │       ├── pvc.yaml
│   │       ├── client.yaml      # OIDC client configuration
│   │       ├── database.yaml    # Database provisioning
│   │       ├── secret.yaml      # Secret generation
│   │       └── external-http-service.yaml
│   └── ...
└── root/            # ArgoCD ApplicationSet for auto-discovery
    ├── Chart.yaml
    ├── values.yaml
    └── templates/
        ├── applicationset.yaml
        └── project.yaml

foundation/
├── charts/          # Foundation service Helm charts
│   └── ...
└── root/            # ArgoCD ApplicationSet for foundation services

shared/
├── charts/          # Shared service Helm charts
│   └── ...
└── root/            # ArgoCD ApplicationSet for shared services

ArgoCD ApplicationSets

This project uses three separate ArgoCD ApplicationSets to manage different categories of services:

  1. apps/ - Individual applications (web apps, tools, services)
  2. foundation/ - Core infrastructure for the cluster (monitoring, certificates, operators)
  3. shared/ - Infrastructure shared between applications (databases, message queues, caches)

Each category has its own root/ chart containing an ApplicationSet that auto-discovers and deploys charts from its respective charts/ directory.

Creating a New Application Chart

1. Basic Chart Structure

Create a new directory under apps/charts/ with the following structure:

mkdir -p apps/charts/my-app/templates

Chart.yaml

apiVersion: v2
version: 1.0.0
name: my-app

values.yaml

image:
  repository: docker.io/org/my-app
  tag: v1.0.0
  pullPolicy: IfNotPresent
subdomain: my-app

2. Core Templates

Deployment Template

Create templates/deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: "{{ .Release.Name }}"
spec:
  strategy:
    type: Recreate
  replicas: 1
  revisionHistoryLimit: 0
  selector:
    matchLabels:
      app: "{{ .Release.Name }}"
  template:
    metadata:
      labels:
        app: "{{ .Release.Name }}"
    spec:
      containers:
        - name: "{{ .Release.Name }}"
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: "{{ .Values.image.pullPolicy }}"
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          livenessProbe:
            tcpSocket:
              port: http
          readinessProbe:
            tcpSocket:
              port: http
          volumeMounts:
            - mountPath: /data
              name: data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: "{{ .Release.Name }}-data"

Service Template

Create templates/service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: "{{ .Release.Name }}"
spec:
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: "{{ .Release.Name }}"

Persistent Volume Claim

Create templates/pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: "{{ .Release.Name }}-data"
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Custom Resource Definitions (CRDs)

This project uses several custom resources that are managed by operators in the cluster:

1. OIDC Client (OpenID Connect Authentication)

The OidcClient resource automatically provisions OAuth2/OIDC clients with your identity provider.

Create templates/client.yaml:

apiVersion: homelab.mortenolsen.pro/v1
kind: OidcClient
metadata:
  name: '{{ .Release.Name }}'
spec:
  environment: '{{ .Values.globals.environment }}'
  redirectUris:
    - path: /oauth/oidc/callback
      subdomain: '{{ .Values.subdomain }}'
      matchingMode: strict

What it does:

  • Creates an OIDC client in your identity provider (e.g., Authentik)
  • Generates a Kubernetes secret named {{ .Release.Name }}-client containing:
    • clientId: The OAuth client ID
    • clientSecret: The OAuth client secret
    • configuration: The OIDC provider URL

Using in deployment:

env:
  - name: OAUTH_CLIENT_ID
    valueFrom:
      secretKeyRef:
        name: "{{ .Release.Name }}-client"
        key: clientId
  - name: OAUTH_CLIENT_SECRET
    valueFrom:
      secretKeyRef:
        name: "{{ .Release.Name }}-client"
        key: clientSecret
  - name: OPENID_PROVIDER_URL
    valueFrom:
      secretKeyRef:
        name: "{{ .Release.Name }}-client"
        key: configuration

2. PostgreSQL Database

The PostgresDatabase resource automatically provisions PostgreSQL databases.

New Version (Recommended):

Create templates/database.yaml:

{{ include "common.database" . }}

Add to values.yaml:

database:
  enabled: true

Legacy Version (Deprecated):

The legacy homelab.mortenolsen.pro/v1 API version is deprecated. For new charts, use the common library template which uses the new postgres.homelab.mortenolsen.pro/v1 API version.

What it does:

  • Creates a PostgreSQL database with the same name as your release
  • Creates a user with appropriate permissions
  • Generates a Kubernetes secret named {{ .Release.Name }}-connection containing:
    • url: Complete PostgreSQL connection URL
    • host: Database hostname
    • port: Database port
    • database: Database name
    • username: Database username
    • password: Database password

Using in deployment:

env:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: "{{ .Release.Name }}-connection"
        key: url

Note: The secret name changed from {release}-pg-connection (legacy) to {release}-connection (new version). The common library template handles this automatically.

3. Secret Generation

The GenerateSecret resource creates secure random secrets.

Create templates/secret.yaml:

apiVersion: homelab.mortenolsen.pro/v1
kind: GenerateSecret
metadata:
  name: "{{ .Release.Name }}-secrets"
spec:
  fields:
    - name: encryptionkey
      encoding: hex      # Options: hex, base64, alphanumeric
      length: 64        # Length in bytes (before encoding)
    - name: apitoken
      encoding: base64
      length: 32

What it does:

  • Generates cryptographically secure random values
  • Creates a Kubernetes secret with the specified fields
  • Supports different encoding formats for different use cases

Using in deployment:

env:
  - name: ENCRYPTION_KEY
    valueFrom:
      secretKeyRef:
        name: "{{ .Release.Name }}-secrets"
        key: encryptionkey

4. External HTTP Service

The ExternalHttpService resource configures ingress routing for your application.

Create templates/external-http-service.yaml:

apiVersion: homelab.mortenolsen.pro/v1
kind: ExternalHttpService
metadata:
  name: '{{ .Release.Name }}'
spec:
  environment: '{{ .Values.globals.environment }}'
  subdomain: '{{ .Values.subdomain }}'
  destination:
    host: '{{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local'
    port:
      number: 80

What it does:

  • Creates ingress routes for your application
  • Configures subdomain routing (e.g., myapp.yourdomain.com)
  • Handles TLS termination automatically
  • Integrates with your service mesh (if applicable)

Best Practices

1. Naming Conventions

  • Use {{ .Release.Name }} consistently for all resource names
  • Suffix resource names appropriately: -data, -secrets, -client, -database

2. Container Configuration

  • Always specify health checks (liveness and readiness probes)
  • Use named ports (e.g., http, grpc) instead of port numbers
  • Set revisionHistoryLimit: 0 to prevent accumulation of old ReplicaSets

3. Environment Variables

  • Never hardcode secrets in values.yaml
  • Use secretKeyRef to reference generated secrets
  • Group related environment variables together

4. Persistent Storage

  • Always use PVCs for stateful data
  • Consider storage requirements carefully (start with reasonable defaults)
  • Mount data at standard paths for the application

5. OIDC Integration

  • Set ENABLE_SIGNUP: "false" if using OIDC
  • Enable OIDC signup with ENABLE_OAUTH_SIGNUP: "true"
  • Configure email merging if needed with OAUTH_MERGE_ACCOUNTS_BY_EMAIL

6. Database Usage

  • Only include database.yaml if the app needs PostgreSQL
  • Applications should support DATABASE_URL environment variable
  • Consider connection pooling settings for production

Disabling Applications

To temporarily disable an application, rename its directory with .disabled suffix:

mv apps/charts/my-app apps/charts/my-app.disabled

The ArgoCD ApplicationSet will automatically exclude directories matching *.disabled.

Testing Your Chart

  1. Lint your chart:

    helm lint apps/charts/my-app
    
  2. Render templates locally:

    helm template my-app apps/charts/my-app
    
  3. Dry run installation:

    helm install my-app apps/charts/my-app --dry-run --debug
    

Deployment Workflow

IMPORTANT: There is no test environment. When creating or modifying applications:

  1. Make changes directly to the files - The agent will write changes to the actual chart files
  2. User deploys the changes - After changes are made, the user must deploy them to the cluster
  3. Debug with kubectl - If issues arise after deployment, agents can use kubectl to:
    • Check pod status and logs
    • Inspect generated resources
    • Verify secret creation
    • Troubleshoot configuration issues

Note: Agents cannot deploy applications themselves. They can only:

  • Create and modify chart files
  • Use kubectl to investigate deployment issues
  • Provide debugging assistance and recommendations

Common Patterns

Application with OIDC + Database

For apps requiring both authentication and database:

  • Include client.yaml for OIDC
  • Include database.yaml for PostgreSQL
  • Reference both secrets in deployment

Stateless Applications

For simple stateless apps:

  • Omit pvc.yaml
  • Remove volume mounts from deployment
  • Consider using Deployment scaling if appropriate

Background Services

For services without web interface:

  • Omit external-http-service.yaml
  • Omit client.yaml (no OIDC needed)
  • Focus on service discovery within cluster

Troubleshooting

Secret Not Found

If secrets are not being created:

  1. Check that the CRD controller is running
  2. Verify the environment value matches your setup
  3. Check controller logs for provisioning errors

OIDC Issues

  1. Verify redirect URIs match exactly
  2. Check that the identity provider is accessible
  3. Ensure the client secret is being properly mounted

Database Connection

  1. Verify the database operator is running
  2. Check network policies between namespaces
  3. Ensure the database server has capacity

Global Values

Applications can access global values through {{ .Values.globals }}:

  • environment: The deployment environment (e.g., "production", "staging")
  • Additional values can be added at the root chart level

Maintenance

Updating Images

  1. Update the tag in values.yaml:
    tag: v1.0.0  # Use semantic version tags only
    
  2. Note: Do not include SHA digests in tags. Immutable digests are automatically added later by Renovate

Renovate Integration

The project uses Renovate for automated dependency updates. Configure in renovate.json5 to:

  • Auto-update container images
  • Create pull requests for updates
  • Group related updates

Backup Considerations

For applications with persistent data:

  1. Consider implementing backup CronJobs
  2. Use volume snapshots if available
  3. Export data regularly for critical applications

Contributing

When adding new applications:

  1. Follow the existing patterns and conventions
  2. Document any special requirements in the chart's README
  3. Consider security implications of all configurations
  4. Update this document if introducing new patterns

Maintaining This Document

IMPORTANT: When making changes to the project structure, patterns, or custom resources:

  • Keep this AGENTS.md file up to date with any changes
  • Document new CRDs or custom resources as they are added
  • Update examples if the patterns change
  • Add new sections for significant new features or patterns
  • Ensure all code examples remain accurate and tested

This document serves as the primary reference for creating and maintaining applications in this project. Keeping it current ensures consistency and helps onboard new contributors.