Apache Answer
Deploy Apache Answer on Kubernetes — an open-source Q&A platform for teams and communities. Supports SQLite (zero-configuration default), PostgreSQL, and MySQL, with automatic unattended installation and database-aware scheduled backups.
With the default configuration (database.mode: auto and no subchart enabled), Answer uses SQLite stored in the
/data PVC. No external database is needed for teams under a few hundred users. Migrate to PostgreSQL or MySQL when
you need concurrent write performance or replication.
Key Features
- Three database backends — SQLite (default), PostgreSQL, MySQL
- Auto-detection — mode is inferred from which subchart or external host is configured
- Auto-install — unattended first-boot setup via environment variables (
autoInstall: true) - Database-aware backup —
tarfor SQLite,pg_dumpfor PostgreSQL,mysqldumpfor MySQL - Admin credentials —
existingSecretfor GitOps-safe secret management
Installation
HTTPS repository:
helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install answer helmforge/answer -f values.yaml
OCI registry:
helm install answer oci://ghcr.io/helmforgedev/helm/answer -f values.yaml
Deployment Examples
# values.yaml — Apache Answer with SQLite (zero database configuration)
answer:
siteName: 'My Q&A'
siteUrl: 'https://qa.example.com'
language: en-US
autoInstall: true
admin:
name: admin
email: [email protected]
existingSecret: answer-admin-credentials # key: admin-password
existingSecretPasswordKey: admin-password
persistence:
enabled: true
size: 10Gi # covers both SQLite file (/data/answer.db) and uploads
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: qa.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: answer-tls
hosts:
- qa.example.com# values.yaml — Apache Answer with bundled PostgreSQL
answer:
siteName: 'My Q&A'
siteUrl: 'https://qa.example.com'
autoInstall: true
admin:
name: admin
email: [email protected]
password: 'change-me'
postgresql:
enabled: true
auth:
database: answer
username: answer
password: 'strong-db-password'
primary:
persistence:
enabled: true
size: 20Gi
persistence:
enabled: true
size: 10Gi # uploads only (SQLite not used when PostgreSQL is active)
ingress:
enabled: true
ingressClassName: traefik
hosts:
- host: qa.example.com
paths:
- path: /
pathType: Prefix# values.yaml — Apache Answer with bundled MySQL
answer:
siteName: 'My Q&A'
siteUrl: 'https://qa.example.com'
autoInstall: true
admin:
name: admin
email: [email protected]
password: 'change-me'
mysql:
enabled: true
auth:
database: answer
username: answer
password: 'strong-db-password'
rootPassword: 'strong-root-password'
primary:
persistence:
enabled: true
size: 20Gi
persistence:
enabled: true
size: 10Gi
ingress:
enabled: true
ingressClassName: traefik
hosts:
- host: qa.example.com
paths:
- path: /
pathType: Prefix# values.yaml — Apache Answer with external PostgreSQL
answer:
siteName: 'My Q&A'
siteUrl: 'https://qa.example.com'
autoInstall: true
admin:
existingSecret: answer-admin-credentials
postgresql:
enabled: false
database:
mode: external
external:
vendor: postgres # or: mysql
host: postgres.database.svc.cluster.local
port: '5432'
name: answer
username: answer
existingSecret: answer-db-credentials
existingSecretPasswordKey: database-password
persistence:
enabled: true
size: 10Gi
backup:
enabled: true
schedule: '0 3 * * *'
s3:
endpoint: https://s3.amazonaws.com
bucket: answer-backups
existingSecret: answer-s3-credentialsConfiguration Reference
Core
| Parameter | Type | Default | Description |
|---|---|---|---|
nameOverride |
string | "" |
Override the chart name. |
fullnameOverride |
string | "" |
Override the full release name. |
commonLabels |
object | {} |
Extra labels added to all resources. |
Image
| Parameter | Type | Default | Description |
|---|---|---|---|
image.repository |
string | docker.io/apache/answer |
Apache Answer container image. |
image.tag |
string | "2.0.0" |
Image tag. |
image.pullPolicy |
string | IfNotPresent |
Image pull policy. |
Answer Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
answer.siteUrl |
string | "" |
Full public URL. Auto-detected from Ingress if empty. |
answer.siteName |
string | Apache Answer |
Site name shown in browser tab and header. |
answer.language |
string | en-US |
Default UI language. |
answer.contactEmail |
string | [email protected] |
Contact email shown in the site footer. |
answer.autoInstall |
boolean | true |
Enable unattended first-boot setup using admin credentials from values. |
answer.logLevel |
string | INFO |
Log level: DEBUG, INFO, WARN, ERROR. |
answer.extraEnv |
array | [] |
Extra environment variables for the container. |
Admin Credentials
| Parameter | Type | Default | Description |
|---|---|---|---|
admin.name |
string | admin |
Admin username for initial setup. |
admin.password |
string | "" |
Admin password. Auto-generated if empty. |
admin.email |
string | [email protected] |
Admin email address. |
admin.existingSecret |
string | "" |
Existing secret with admin credentials. |
admin.existingSecretPasswordKey |
string | admin-password |
Key inside the existing secret for the password. |
Database
Auto-detection precedence (database.mode: auto):
| Priority | Condition | Result |
|---|---|---|
| 1 | database.external.host or external.existingSecret set |
External DB |
| 2 | postgresql.enabled: true |
PostgreSQL subchart |
| 3 | mysql.enabled: true |
MySQL subchart |
| 4 | None of the above | SQLite (default) |
| Parameter | Type | Default | Description |
|---|---|---|---|
database.mode |
string | auto |
Database mode: auto, sqlite, external, postgresql, mysql. |
database.sqlite.file |
string | /data/answer.db |
SQLite file path inside the data volume. |
database.external.vendor |
string | postgres |
External DB vendor: postgres or mysql. |
database.external.host |
string | "" |
External database hostname. |
database.external.port |
string | "" |
External database port. Auto-detected from vendor if empty. |
database.external.name |
string | answer |
Database name. |
database.external.username |
string | answer |
Database username. |
database.external.existingSecret |
string | "" |
Existing secret with database password. |
database.external.existingSecretPasswordKey |
string | database-password |
Key for the password in the existing secret. |
Subcharts
| Parameter | Type | Default | Description |
|---|---|---|---|
postgresql.enabled |
boolean | false |
Deploy the bundled PostgreSQL subchart. |
postgresql.auth.database |
string | answer |
Database name. |
postgresql.auth.password |
string | "" |
Password. Auto-generated if empty. |
postgresql.primary.persistence.size |
string | 8Gi |
PVC size. |
mysql.enabled |
boolean | false |
Deploy the bundled MySQL subchart. |
mysql.auth.database |
string | answer |
Database name. |
mysql.auth.password |
string | "" |
Password. Auto-generated if empty. |
mysql.primary.persistence.size |
string | 8Gi |
PVC size. |
Persistence
| Parameter | Type | Default | Description |
|---|---|---|---|
persistence.enabled |
boolean | true |
Enable PVC for /data (SQLite DB file + uploads). |
persistence.size |
string | 5Gi |
PVC size. Increase if using SQLite as the primary database. |
persistence.storageClass |
string | "" |
StorageClass for the PVC. |
persistence.accessMode |
string | ReadWriteOnce |
PVC access mode. |
persistence.existingClaim |
string | "" |
Use an existing PVC. |
Backup
The backup CronJob detects which database is in use and runs the appropriate tool:
- SQLite →
tararchive of the full/datadirectory (Alpine image) - PostgreSQL →
pg_dump(PostgreSQL Alpine image) - MySQL →
mysqldump(MySQL image)
Only the images needed for the active database vendor are used.
| Parameter | Type | Default | Description |
|---|---|---|---|
backup.enabled |
boolean | false |
Enable scheduled S3 backup CronJob. |
backup.schedule |
string | "0 3 * * *" |
Cron schedule. |
backup.archivePrefix |
string | answer |
Prefix for backup archive filenames. |
backup.images.sqlite |
string | alpine:3.22 |
Image for SQLite backup (tar). |
backup.images.postgresql |
string | postgres:18.3-alpine |
Image for PostgreSQL backup (pg_dump). |
backup.images.mysql |
string | mysql:8.4 |
Image for MySQL backup (mysqldump). |
backup.images.uploader |
string | helmforge/mc:1.0.0 |
Image for S3 upload (MinIO client). |
backup.s3.endpoint |
string | "" |
S3-compatible endpoint URL. |
backup.s3.bucket |
string | "" |
Target bucket name. |
backup.s3.existingSecret |
string | "" |
Existing secret with S3 credentials. |
backup.database.postgresDumpArgs |
string | "" |
Extra arguments for pg_dump. |
backup.database.mysqlDumpArgs |
string | --single-transaction ... |
Extra arguments for mysqldump. |
Service and Ingress
| Parameter | Type | Default | Description |
|---|---|---|---|
service.type |
string | ClusterIP |
Service type. |
service.port |
integer | 80 |
Service port. |
ingress.enabled |
boolean | false |
Enable an Ingress resource. |
ingress.ingressClassName |
string | "" |
Ingress class name. |
ingress.annotations |
object | {} |
Ingress annotations. |
ingress.hosts |
array | [] |
Host and path rules. |
ingress.tls |
array | [] |
TLS configuration. |
Probes
All probes use /healthz as the HTTP path.
| Parameter | Type | Default | Description |
|---|---|---|---|
startupProbe.enabled |
boolean | true |
Enable startup probe. |
startupProbe.failureThreshold |
integer | 30 |
Startup probe failure threshold. |
livenessProbe.enabled |
boolean | true |
Enable liveness probe. |
readinessProbe.enabled |
boolean | true |
Enable readiness probe. |
Resources and Scheduling
| Parameter | Type | Default | Description |
|---|---|---|---|
resources |
object | {} |
CPU and memory requests/limits. |
terminationGracePeriodSeconds |
integer | 30 |
Termination grace period. |
nodeSelector |
object | {} |
Node selector for scheduling. |
tolerations |
array | [] |
Tolerations for scheduling. |
extraVolumes |
array | [] |
Extra volumes. |
extraVolumeMounts |
array | [] |
Extra volume mounts. |
extraManifests |
array | [] |
Extra Kubernetes manifests. |