Skip to content

Latest commit

 

History

History

README.md

Ghost Helm Chart

Ghost is a simple, powerful publishing platform for creating blogs and online publications. This Helm chart deploys Ghost on Kubernetes with optional MariaDB support.

Installation

To install the chart with the release name my-ghost:

helm install my-ghost oci://registry-1.docker.io/cloudpirates/ghost

To install with custom values:

helm install my-ghost oci://registry-1.docker.io/cloudpirates/ghost -f my-values.yaml

Or install directly from the local chart:

helm install my-ghost ./charts/ghost

The 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

Uninstalling

To uninstall/delete the my-ghost deployment:

helm uninstall my-ghost

Security & Signature Verification

This 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>

Configuration

Custom Admin URL

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.

External database support

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
  ...

Using Kubernetes Secrets for Credentials

Database Credentials from Secret

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-password

Reference 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-password

Reference 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 username
  • mariadb-password: database password for the user

SMTP Credentials from Secret

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-password

Reference 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:

Global Parameters

Parameter Description Default
global.imageRegistry Global Docker image registry ""
global.imagePullSecrets Global Docker registry secret names as array []

Common Parameters

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

Image Parameters

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

Network Parameters

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 []

Persistence Parameters

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

Database Parameters

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

Mail Configuration Parameters

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

Pod Parameters

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 ""

Health Check Parameters

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

Init Container Parameters

Parameter Description Default
initContainers.waitForMariadb.image MariaDB init container image mariadb:12.0.2

Autoscaling Parameters

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 ""

Additional Configuration

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

Example: Custom Ghost Configuration

https://docs.ghost.org/config

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"

Troubleshooting

Connection Issues

  1. 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
  1. Check pod logs:
kubectl logs <pod-name>

Database Issues

  • Ensure MariaDB is running and accessible if using the bundled database.
  • Check database credentials in your values file.

Performance Tuning

  • Adjust resources in values.yaml for production workloads.
  • Use persistent storage for content.
  • Monitor pod health and logs for errors.

Useful Links