Ghost is a simple, powerful publishing platform for creating blogs and online publications. This Helm chart deploys Ghost on Kubernetes with optional MariaDB support.
To install the chart with the release name my-ghost:
helm install my-ghost oci://registry-1.docker.io/cloudpirates/ghostTo install with custom values:
helm install my-ghost oci://registry-1.docker.io/cloudpirates/ghost -f my-values.yamlOr install directly from the local chart:
helm install my-ghost ./charts/ghostThe output should show you the URL's for your Ghost instance and Admin interface accoriding to your settings in my-values.yaml
URLS:
1) Website: https://ghost.localhost
2) Admin: https://admin.ghost.localhost/ghost
To uninstall/delete the my-ghost deployment:
helm uninstall my-ghostThis Helm chart is cryptographically signed with Cosign to ensure authenticity and prevent tampering.
Public Key:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5U+rM2d3hDjgP5T3cLShuuQIU9vR
Z4/G+Nug6q5vRa+C3qUA1wXjbaJFAfcIrv5VjmYAYOj13shnPpp3Zh4fnQ==
-----END PUBLIC KEY-----
To verify the helm chart before installation, copy the public key to the file cosign.pub and run cosign:
cosign verify --key cosign.pub registry-1.docker.io/cloudpirates/ghost:<version>By default, this chart expects a second ingress host for the admin interface. If you want to customize the admin URL or use a different configuration, you can set it explicitly:
config:
admin:
url: "https://custom-admin.example.com"If config.admin.url is not set (empty string or not defined), the chart will automatically use the second ingress host defined in ingress.hosts[1].host.
If you want to use an external database (e.g., AWS RDS, DigitalOcean Managed Databases), you need to set the following values in your my-values.yaml:
mariadb:
enabled: false
config:
database:
client: "mysql"
externalConnection:
host: "ghost-mariadb"
port: 3306
user: "ghost"
password: "changeme"
database: "ghost"
pool:
min: 2
max: 10
...For external databases:
Create a Kubernetes secret with your database credentials:
kubectl create secret generic ghost-db-credentials \
--from-literal=username=ghost \
--from-literal=password=your-secure-passwordReference this secret in your my-values.yaml:
mariadb:
enabled: false
config:
database:
client: "mysql"
externalConnection:
host: "my-external-db.example.com"
port: 3306
database: "ghost"
existingSecret:
name: "ghost-db-credentials"
usernameKey: "username"
passwordKey: "password"For bundled MariaDB:
You can also use an existing secret with the bundled MariaDB deployment. Create a secret with the required keys:
kubectl create secret generic ghost-mariadb-credentials \
--from-literal=username=ghost \
--from-literal=mariadb-password=your-secure-passwordReference it in your my-values.yaml:
mariadb:
enabled: true
auth:
database: ghost
existingSecret: "ghost-mariadb-credentials"Note: The secret must contain the following keys:
username: database usernamemariadb-password: database password for the user
Create a secret for SMTP credentials:
kubectl create secret generic ghost-smtp-credentials \
--from-literal=user=postmaster@example.mailgun.org \
--from-literal=pass=your-smtp-passwordReference it in your values:
config:
mail:
transport: "SMTP"
options:
service: "Mailgun"
host: "smtp.mailgun.org"
port: 465
secure: true
auth:
existingSecret:
name: "ghost-smtp-credentials"
userKey: "user"
passKey: "pass"
from: "support@example.com"The following tables list the configurable parameters of the Ghost chart organized by category:
| Parameter | Description | Default |
|---|---|---|
global.imageRegistry |
Global Docker image registry | "" |
global.imagePullSecrets |
Global Docker registry secret names as array | [] |
| Parameter | Description | Default |
|---|---|---|
nameOverride |
String to partially override ghost.fullname | "" |
fullnameOverride |
String to fully override ghost.fullname | "" |
namespaceOverride |
String to override the namespace for all resources | "" |
commonLabels |
Labels to add to all deployed objects | {} |
commonAnnotations |
Annotations to add to all deployed objects | {} |
replicaCount |
Number of Ghost replicas to deploy | 1 |
| Parameter | Description | Default |
|---|---|---|
image.registry |
Ghost image registry | docker.io |
image.repository |
Ghost image repository | ghost |
image.tag |
Ghost image tag | 6.9.1@sha256:8a30cacb126262887f4db101e438271ade0b51437917b8165d26b0fede72ccf2 |
image.pullPolicy |
Ghost image pull policy | Always |
| Parameter | Description | Default |
|---|---|---|
containerPorts |
List of container ports | [{name: http, containerPort: 2368}] |
service.type |
Kubernetes service type | ClusterIP |
service.ports |
List of service ports | [{port: 80, targetPort: http}] |
ingress.enabled |
Enable ingress record generation | true |
ingress.className |
IngressClass for the ingress record | "" |
ingress.annotations |
Additional ingress annotations | {} |
ingress.hosts |
List of ingress hosts | [{host: ghost.localhost, paths:[{path: /, pathType: Prefix}]}, {host: admin.ghost.localhost, paths:[{path: /ghost, pathType: Prefix}]}] |
ingress.tls |
TLS configuration for ingress | [] |
| Parameter | Description | Default |
|---|---|---|
persistence.enabled |
Enable persistence using PVC | true |
persistence.annotations |
Annotations for PVC | {} |
persistence.existingClaim |
Use an existing PVC | "" |
persistence.storageClass |
Storage class of backing PVC | "" |
persistence.accessModes |
PVC access modes | [ReadWriteOnce] |
persistence.size |
Size of persistent volume claim | 8Gi |
| Parameter | Description | Default |
|---|---|---|
mariadb.enabled |
Deploy MariaDB as dependency | true |
mariadb.auth.database |
MariaDB database name | ghost |
mariadb.auth.username |
MariaDB username | ghost |
mariadb.auth.password |
MariaDB password | changeme |
mariadb.auth.existingSecret |
Name of existing secret with MariaDB credentials (must contain keys: username, mariadb-password) | "" |
mariadb.auth.allowEmptyRootPassword |
Allow empty root password | false |
config.database.externalConnection.existingSecret.name |
Name of existing secret for external DB credentials | "" |
config.database.externalConnection.existingSecret.usernameKey |
Key in secret containing database username | username |
config.database.externalConnection.existingSecret.passwordKey |
Key in secret containing database password | password |
| Parameter | Description | Default |
|---|---|---|
config.mail.options.auth.existingSecret.name |
Name of existing secret for SMTP credentials | "" |
config.mail.options.auth.existingSecret.userKey |
Key in secret containing SMTP username | user |
config.mail.options.auth.existingSecret.passKey |
Key in secret containing SMTP password | pass |
| Parameter | Description | Default |
|---|---|---|
resources |
Resource limits and requests for pod | {} |
nodeSelector |
Node selector for pod assignment | {} |
tolerations |
Tolerations for pod assignment | [] |
affinity |
Affinity for pod assignment | {} |
podSecurityContext.fsGroup |
Set pod's Security Context fsGroup | 1000 |
containerSecurityContext.runAsUser |
Set container's Security Context runAsUser | 1000 |
containerSecurityContext.runAsNonRoot |
Run as non-root user | true |
containerSecurityContext.allowPrivilegeEscalation |
Allow privilege escalation | false |
priorityClassName |
Priority class for the ghost instance | "" |
| Parameter | Description | Default |
|---|---|---|
livenessProbe.enabled |
Enable liveness probe | true |
livenessProbe.type |
Probe type (tcpSocket or httpGet) | tcpSocket |
livenessProbe.initialDelaySeconds |
Initial delay seconds | 30 |
livenessProbe.periodSeconds |
Period seconds | 10 |
readinessProbe.enabled |
Enable readiness probe | true |
readinessProbe.type |
Probe type (tcpSocket or httpGet) | tcpSocket |
readinessProbe.initialDelaySeconds |
Initial delay seconds | 5 |
readinessProbe.periodSeconds |
Period seconds | 12 |
| Parameter | Description | Default |
|---|---|---|
initContainers.waitForMariadb.image |
MariaDB init container image | mariadb:12.0.2 |
| Parameter | Description | Default |
|---|---|---|
autoscaling.enabled |
Enable autoscaling | false |
autoscaling.minReplicas |
Minimum number of replicas | "" |
autoscaling.maxReplicas |
Maximum number of replicas | "" |
autoscaling.targetCPUUtilizationPercentage |
Target CPU utilization | "" |
autoscaling.targetMemoryUtilizationPercentage |
Target memory utilization | "" |
| Parameter | Description | Default |
|---|---|---|
extraEnvVars |
Additional environment variables | [] |
extraVolumes |
Additional volumes | [] |
extraVolumeMounts |
Additional volume mounts | [] |
extraObjects |
Extra Kubernetes objects to deploy | [] |
config |
Ghost configuration (database, mail, etc.) | See values.yaml |
config:
database:
client: "mysql"
externalConnection:
host: "ghost-mariadb"
port: 3306
user: "ghost"
password: "changeme"
database: "ghost"
pool:
min: 2
max: 10
mail:
transport: "SMTP"
options:
service: "Mailgun"
host: "smtp.mailgun.org"
port: 465
secure: true
auth:
user: "postmaster@example.mailgun.org"
pass: "1234567890"
from: "support@example.com"
admin:
url: "" # Optional: Set custom admin URL. Defaults to second ingress host if not set.
server:
host: "0.0.0.0"
port: 2368
privacy:
useUpdateCheck: false
useGravatar: false
useRpcPing: false
useStructuredData: false
security:
staffDeviceVerification: false
paths:
contentPath: "content/"
referrerPolicy: "origin-when-crossorigin"
logging:
path: "content/logs/"
useLocalTime: true
level: "info"
rotation:
enabled: true
count: 10
period: "1d"
transports:
- "stdout"
- "file"
caching:
frontend:
maxAge: 0
contentAPI:
maxAge: 10
robotstxt:
maxAge: 3600
sitemap:
maxAge: 3600
sitemapXSL:
maxAge: 86400
wellKnown:
maxAge: 86400
cors:
maxAge: 86400
publicAssets:
maxAge: 31536000
threeHundredOne:
maxAge: 31536000
customRedirects:
maxAge: 31536000
compress: true
imageOptimization:
resize: false
storage:
active: "local-file-store"
adapters:
cache:
imageSizes:
adapter: "Redis"
ttl: 3600
keyPrefix: "image-sizes:"
portal:
url: "https://cdn.jsdelivr.net/npm/@tryghost/portal@~{version}/umd/portal.min.js"
sodoSearch:
url: "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/sodo-search.min.js"
styles: "https://cdn.jsdelivr.net/npm/@tryghost/sodo-search@~{version}/umd/main.css"
comments:
url: "https://cdn.jsdelivr.net/npm/@tryghost/comments-ui@~{version}/umd/comments-ui.min.js"
styles: "https://cdn.jsdelivr.net/npm/@tryghost/comments-ui@~{version}/umd/main.css"- Check deployment and service status:
kubectl get deployment -l app.kubernetes.io/name=ghost
kubectl get svc -l app.kubernetes.io/name=ghost
kubectl get pods -l app.kubernetes.io/name=ghost- Check pod logs:
kubectl logs <pod-name>- Ensure MariaDB is running and accessible if using the bundled database.
- Check database credentials in your values file.
- Adjust resources in
values.yamlfor production workloads. - Use persistent storage for content.
- Monitor pod health and logs for errors.