Timoni

The same deployment as Flux, but client-side and with the value schema in your hands: each release pushes the Timoni module (CUE) to oci://forgejo.lihnet.mallon.ie/eagraiclainne/timoni, defaulting to that release's server image. No in-cluster operator.

Prerequisites

  • A Kubernetes cluster and kubectl pointed at it
  • timoni installed locally
  • A Forgejo token with package:read. Timoni reads the Docker credential store, so docker login forgejo.lihnet.mallon.ie covers the module pull

Namespace & registry access

The pods pull the server image from the private registry, and the module does not set imagePullSecrets — attach a pull secret to the namespace's default ServiceAccount:

kubectl create namespace eagraiclainne

kubectl -n eagraiclainne create secret docker-registry gitea-registry-auth \
  --docker-server=forgejo.lihnet.mallon.ie \
  --docker-username=your-username \
  --docker-password=your-token

kubectl -n eagraiclainne patch serviceaccount default \
  -p '{"imagePullSecrets":[{"name":"gitea-registry-auth"}]}'

Storage

The module defaults to SQLite: a 1Gi PVC mounted at /data, the server owning /data/eagraiclainne.db and applying its schema on boot. Single replica, Recreate rollouts. With that default there is nothing to do here — skip to Values.

To run the PostgreSQL backend instead, set database: driver: "postgres" in your values and stand up a CloudNativePG cluster first — the module binds to the generated eagraiclainne-db-rw Service and eagraiclainne-db-app Secret by default:

# install the operator (version-specific manifest, pinned)
kubectl apply --server-side -f \
  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.25/releases/cnpg-1.25.0.yaml
kubectl -n cnpg-system rollout status deployment/cnpg-controller-manager
# eagraiclainne-db-cluster.yaml
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: eagraiclainne-db
  namespace: eagraiclainne
spec:
  instances: 2
  imageName: ghcr.io/cloudnative-pg/postgresql:17.2
  bootstrap:
    initdb:
      database: eagraiclainne
      owner: app
  storage:
    size: 1Gi
  resources:
    requests:
      cpu: 100m
      memory: 256Mi
    limits:
      memory: 512Mi
kubectl apply -f eagraiclainne-db-cluster.yaml
kubectl -n eagraiclainne wait --for=condition=Ready cluster/eagraiclainne-db --timeout=300s

Values

The defaults deploy the released image on the SQLite PVC, with telemetry off (stdout exporters). The overlay is where you switch the storage engine, opt in to an OTLP collector, or pin the JWT secret outside the database — or apply with no values file at all and take every default:

cat > prod-values.cue <<'EOF'
values: {
  // Optional: grow the sqlite PVC (default 1Gi), or switch engines
  // entirely — "postgres" binds the CNPG names above instead.
  // database: storage: "5Gi"
  // database: driver: "postgres"

  // Optional: export telemetry to your collector (see the install
  // overview's Telemetry section). Left out, everything goes to stdout.
  // config: otlp: { endpoint: "lgtm.observability:4317", insecure: true }

  // Optional: pin the signing secret (32+ bytes). Left out, the server
  // generates one on first boot and keeps it in the database.
  // secret: jwtSecret: "a-real-32-byte-or-longer-secret-value!!"
}
EOF

The full schema lives in deploy/timoni/eagraiclainne — replicas, resources, service port, database secret name, timeouts.

Apply

timoni -n eagraiclainne apply eagraiclainne \
  oci://forgejo.lihnet.mallon.ie/eagraiclainne/timoni \
  --version v1.0.0 \
  --values prod-values.cue

Verify

timoni -n eagraiclainne status eagraiclainne
kubectl -n eagraiclainne rollout status deployment/eagraiclainne
kubectl -n eagraiclainne port-forward svc/eagraiclainne 8080:8080 &
curl -sS http://localhost:8080/ | head -c 200

Then expose the Service through your ingress of choice and open it — first sign-in takes it from there. To upgrade, run apply again with the next --version. Schema changes apply themselves when the new pod boots.