4 Commits

Author SHA1 Message Date
240af34978 Updating Helm documentation (#209)
* Updating Helm documentation

* No version number
2026-05-29 22:06:53 +00:00
1a4f1d94b6 Fix pod security (#204)
* Fix pod security

* Extra security context

* Use correct security context
2026-05-29 18:06:47 +00:00
6612d0d02b Fix install directory for helm chart (#207)
* Fixing helm publish path

* Fixing install dir for helm chart
2026-05-29 17:46:22 +00:00
d77966351b Fixing helm publish path (#206) 2026-05-29 17:37:12 +00:00
5 changed files with 206 additions and 54 deletions

View File

@ -33,5 +33,4 @@ jobs:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
with:
charts_dir: helm
charts_repo_url: https://gothenburgbitfactory.org/taskchampion-sync-server/helm-chart
pages_index_path: helm-charts
install_dir: helm-chart

View File

@ -5,56 +5,127 @@ A Helm chart is available for deploying taskchampion-sync-server on Kubernetes.
## Adding the Repository
```sh
helm repo add taskchampion https://gothenburgbitfactory.org/taskchampion-sync-server/helm-chart
helm repo add taskchampion https://gothenburgbitfactory.org/taskchampion-sync-server
helm repo update
```
## Installing the Chart
The chart requires exactly one storage backend to be enabled. To install with
the SQLite backend:
## Installing
```sh
# SQLite backend
helm install taskchampion-sync-server taskchampion/taskchampion-sync-server \
--set sqlite.enabled=true
```
To install with the Postgres backend, provide the connection details:
```sh
# PostgreSQL backend
helm install taskchampion-sync-server taskchampion/taskchampion-sync-server \
--set postgres.enabled=true \
--set postgres.host=my-postgres \
--set postgres.database=taskchampion \
--set postgres.db=taskchampion \
--set postgres.username=myuser \
--set postgres.password=mypassword
```
Alternatively, pass an existing Secret name via `postgres.existingSecret`. The
secret must contain a `connection` key holding a [LibPQ-style connection
URI](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS).
## Storage Backends
## Configuration
Exactly one storage backend must be enabled. The chart fails validation if
neither or both are enabled.
The chart does not implement TLS. The expectation is that a Kubernetes ingress
or gateway will terminate TLS and proxy HTTP traffic to the server container.
Enable one of the built-in options or configure your own:
### SQLite
```sh
# NGINX ingress
--set ingress.enabled=true --set ingress.hosts[0]=taskchampion.example.com
Mounts a volume at `sqlite.dataDir` (default `/var/lib/taskchampion-sync-server/data`).
The volume defaults to `emptyDir` but can be backed by a PVC or
existing PersistentVolumeClaim:
# Gateway API HTTPRoute
--set httpRoute.enabled=true \
--set httpRoute.gateway=my-gateway \
--set httpRoute.host=taskchampion.example.com
| Value | Description |
|-------|-------------|
| `sqlite.persistence.enabled` | Create a PVC (default: `false`) |
| `sqlite.existingPV` | Use an existing PVC by name |
| `sqlite.emptyDir` | emptyDir volume settings (default fallback) |
### PostgreSQL
Creates a Deployment with optional replicas, an auto-generated or existing
secret for the connection string, and an init container that waits for
PostgreSQL, applies the schema, and optionally seeds client IDs.
**Secret handling** — When `postgres.existingSecret` is empty (default), the
chart creates a secret named `{release-name}-connection` with a `conn` key.
When set, the chart reads the named secret. It accepts either a `conn` key
with a full [LibPQ-style URI](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS)
or individual fields (`host`, `port`, `username`, `password`, `database`) that
override the corresponding `postgres.*` values.
**Init container** — Enabled by default. Waits for PG readiness, downloads
the schema from `https://raw.githubusercontent.com/GothenburgBitFactory/taskchampion-sync-server/v0.7.0/postgres/schema.sql`,
applies it, and seeds client IDs if `clientIdSecret` is set. Override the
schema URL with `postgres.initContainer.schemaUrl`.
**Replicas** — Replicas only apply with PostgreSQL:
`replicas.enabled=true`, `replicas.count=N`. SQLite is always single-replica.
## Secrets
### Client ID Secret
Optional. Restrict which client IDs the server accepts. Create a Secret with
comma-separated UUIDs (base64-encoded) under a `client-ids` key:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: my-client-ids
type: Opaque
data:
client-ids: <base64-encoded comma-separated UUIDs>
```
To restrict which client IDs the server accepts, create a Secret containing a
newline-separated list of UUIDs and reference it:
Reference it via `clientIdSecret: "my-client-ids"`. The value is available to
the server as `CLIENT_ID` and to the init container as `CLIENT_IDS`.
```sh
--set clientIdSecret=my-client-ids-secret
## Networking
The chart does not implement TLS. Terminate TLS at the ingress or gateway and
proxy HTTP to port 8080.
### Ingress (NGINX)
```yaml
ingress:
enabled: true
hosts:
- taskchampion.example.com
```
See the chart's `values.yaml` for the full set of configuration options.
### HTTPRoute (Kubernetes Gateway API)
Use `parentRefs`, `hostnames`, and `rules`:
```yaml
httpRoute:
enabled: true
parentRefs:
- name: my-gateway
hostnames:
- tasks.example.com
rules:
- path:
type: PathPrefix
value: /
backendPort: 8080
```
The deprecated fields `httpRoute.gateway`, `httpRoute.host`,
`httpRoute.path`, and `httpRoute.port` are used as a fallback when the
structured arrays are empty.
### ServiceAccount and RBAC
When `serviceAccount.create` is `true` (default), the chart creates a
ServiceAccount, a Role with `get`/`create`/`update`/`patch` on secrets, and
a RoleBinding. Set `serviceAccount.name` to use an existing SA.
## Reference
See the chart's [values.yaml](https://github.com/GothenburgBitFactory/taskchampion-sync-server/blob/main/helm/taskchampion-sync-server/values.yaml)
for all configurable options.

View File

@ -2,7 +2,7 @@ apiVersion: v2
name: taskchampion-sync-server
description: A Helm chart for deploying TaskChampion Sync Server on Kubernetes
type: application
version: 0.1.1
version: 0.1.2
appVersion: "0.7.0"
keywords:
- taskchampion

View File

@ -7,28 +7,55 @@ Deploy the [TaskChampion Sync Server](https://github.com/GothenburgBitFactory/ta
- Kubernetes 1.23+
- Helm 3+
## Installing
```console
helm repo add taskchampion https://gothenburgbitfactory.org/taskchampion-sync-server
helm repo update
helm install my-release taskchampion/taskchampion-sync-server --set sqlite.enabled=true
```
## Storage Backends
Exactly one storage backend must be enabled. The chart will fail validation if both or neither are enabled.
Exactly one storage backend must be enabled. The chart fails validation if neither or both are enabled.
### SQLite
```console
helm install my-release ./helm/taskchampion-sync-server -f helm/taskchampion-sync-server/examples/sqlite-values.yaml
```
| Parameter | Default | Description |
|-----------|---------|-------------|
| `sqlite.enabled` | `false` | Enable SQLite backend |
| `sqlite.dataDir` | `/var/lib/taskchampion-sync-server/data` | Data directory path |
| `sqlite.existingPV` | `""` | Use an existing PVC name |
| `sqlite.persistence.enabled` | `false` | Create a PVC |
| `sqlite.persistence.size` | `1Gi` | PVC size |
| `sqlite.persistence.accessMode` | `ReadWriteOnce` | PVC access mode |
| `sqlite.emptyDir` | — | emptyDir volume settings |
### PostgreSQL
```console
helm install my-release ./helm/taskchampion-sync-server -f helm/taskchampion-sync-server/examples/postgres-values.yaml
```
| Parameter | Default | Description |
|-----------|---------|-------------|
| `postgres.enabled` | `false` | Enable PostgreSQL backend |
| `postgres.host` | `""` | PostgreSQL host |
| `postgres.port` | `5432` | PostgreSQL port |
| `postgres.db` | `taskchampion` | Database name |
| `postgres.username` | `""` | Database user |
| `postgres.password` | `""` | Database password |
| `postgres.sslMode` | `disable` | SSL mode |
| `postgres.existingSecret` | `""` | Use existing secret by name |
**Secret** — When `existingSecret` is empty (default), a secret named `{release-name}-connection` is created with a `conn` key. When set, the chart reads that secret. It accepts either a `conn` key with a full URI or individual keys (`host`, `port`, `username`, `password`, `database`) that override `postgres.*` values.
**Init container** — Enabled by default. Waits for PG readiness, downloads and applies the schema (from the chart's `appVersion` URL), and seeds client IDs if `clientIdSecret` is set. Override with `postgres.initContainer.schemaUrl`.
**Replicas** — Only apply with PostgreSQL (`replicas.enabled=true`, `replicas.count=N`). SQLite is single-replica.
## Secrets
The chart expects pre-created secrets referenced by name:
### Client ID Secret
Restrict which client IDs the server accepts. Create a Secret with comma-separated UUIDs (base64-encoded) under a `client-ids` key:
```yaml
apiVersion: v1
kind: Secret
@ -39,22 +66,73 @@ data:
client-ids: <base64-encoded comma-separated UUIDs>
```
Reference it via `clientIdSecret: "my-client-ids"`.
Reference via `clientIdSecret: "my-client-ids"`.
### PostgreSQL Secret
## Networking
For PostgreSQL, the chart can automatically create a secret with the connection string, or use an existing secret.
The chart does not implement TLS. Terminate TLS at the ingress or gateway.
**Automatic Secret Creation**:
- When `postgres.existingSecret` is empty (default), the chart automatically creates a secret
- Secret is named using Helm naming convention: `release-name-taskchampion-sync-server`
- Secret contains only a `connection` key with the built connection string
### Service
**Existing Secret Usage**:
- When `postgres.existingSecret` is provided, the chart uses that secret
- The secret **must** contain a `connection` key with the PostgreSQL connection string
- If the secret doesn't have a `connection` key, the deployment will fail with a clear error
| Parameter | Default | Description |
|-----------|---------|-------------|
| `service.type` | `ClusterIP` | Service type |
| `service.port` | `8080` | Service port |
| `service.targetPort` | `8080` | Container port |
## Configuration
### Ingress (NGINX)
See [values.yaml](values.yaml) for all configurable options.
| Parameter | Default | Description |
|-----------|---------|-------------|
| `ingress.enabled` | `false` | Enable NGINX ingress |
| `ingress.className` | `""` | Ingress class name |
| `ingress.annotations` | `{}` | Ingress annotations |
| `ingress.hosts` | `[]` | Host list |
| `ingress.tls` | `[]` | TLS configuration |
### HTTPRoute (Kubernetes Gateway API)
| Parameter | Default | Description |
|-----------|---------|-------------|
| `httpRoute.enabled` | `false` | Enable HTTPRoute |
| `httpRoute.parentRefs` | `[]` | Parent gateway references (primary) |
| `httpRoute.hostnames` | `[]` | Hostnames |
| `httpRoute.rules` | `[]` | Routing rules |
| `httpRoute.gateway` | `""` | (Deprecated) Single gateway name |
| `httpRoute.host` | `""` | (Deprecated) Single hostname |
| `httpRoute.path` | `"/"` | (Deprecated) Single path |
| `httpRoute.port` | `8080` | (Deprecated) Single port |
**Recommended** — use `parentRefs`, `hostnames`, `rules`. The deprecated
single-value fields (`gateway`, `host`, `path`, `port`) are used as a fallback
when the arrays are empty.
## ServiceAccount and RBAC
| Parameter | Default | Description |
|-----------|---------|-------------|
| `serviceAccount.create` | `true` | Create SA, Role, and RoleBinding |
| `serviceAccount.name` | `""` | Use existing SA name |
When created, the chart provisions a ServiceAccount, a Role with
`get`/`create`/`update`/`patch` on secrets, and a RoleBinding.
## Image Configuration
| Parameter | Default | Description |
|-----------|---------|-------------|
| `image.repo` | `ghcr.io/gothenburgbitfactory/taskchampion-sync-server` | Image repository |
| `image.tag` | `"0.7.0"` | Image tag |
| `image.pullPolicy` | `IfNotPresent` | Pull policy |
| `image.pullSecrets` | `[]` | Pull secrets |
PostgreSQL appends `-postgres` to the image repo automatically.
## Environment Variables
Custom env vars are passed via `env`. `DATA_DIR` (SQLite) and `conn`
(PostgreSQL) are set automatically and must not be set manually.
## Full Configuration
See [values.yaml](values.yaml).

View File

@ -30,6 +30,8 @@ spec:
- name: postgres-init
image: "{{ .Values.postgres.initContainer.image }}"
imagePullPolicy: {{ .Values.postgres.initContainer.imagePullPolicy }}
securityContext:
allowPrivilegeEscalation: false
env:
- name: PGURI
valueFrom:
@ -86,6 +88,8 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
{{- end }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext:
allowPrivilegeEscalation: false
env:
{{- range $name, $value := .Values.env }}
- name: {{ $name }}