Ghost CMS in Kubernetes
Let's get right down to business: Can Ghost CMS run in Kubernetes? Yes! This site is proof. It's running on my personal cluster in Digital Ocean (previously Azure Kubernetes Service, but Digital Ocean costs about half the price).
How to do it
It's relatively simple. Once you get things running in a container (which is extremely simple), you can move on to defining your Kubernetes manifests. Here's my Dockerfile:
FROM ghost:3.40.2-alpine COPY content content COPY config.production.json . COPY scripts/addAppInsights.sh .
This simply copies all of my relevant content and configuration into the container. Now here's my Kubernetes manifest:
apiVersion: apps/v1 kind: Deployment metadata: name: personal-ghost namespace: ghost labels: app: personal-ghost spec: replicas: 1 selector: matchLabels: app: personal-ghost template: metadata: labels: app: personal-ghost spec: containers: - name: personal-ghost image: docker.pkg.github.com/justin-vanwinkle/ghost-website/ghost:latest imagePullPolicy: Always ports: - containerPort: 2368 readinessProbe: httpGet: scheme: HTTP path: / port: 2368 httpHeaders: - name: X-Forwarded-Proto value: https initialDelaySeconds: 60 periodSeconds: 5 resources: requests: memory: "300Mi" cpu: "300m" limits: memory: "600Mi" cpu: "600m" env: - name: NODE_ENV value: production - name: AZURE_STORAGE_CONNECTION_STRING valueFrom: secretKeyRef: name: personal-website key: storage-connectionString - name: database__connection__password valueFrom: secretKeyRef: name: personal-website key: db-password volumeMounts: - mountPath: /var/lib/ghost/content/images name: images-volume volumes: - name: images-volume persistentVolumeClaim: claimName: pvc-ghost-personal restartPolicy: Always imagePullSecrets: - name: github-registry-credentials
There's a lot there, but the relevant parts are the image that I use, the environment variables that are used to pass sensitive information into the container, and the volume/volume mount that is used to store data that I wish to persist (i.e. directories that receive uploaded content).
Beyond that, it's just a matter of defining your service and ingress. If you need some guidance on those, take a look at my full manifest. And don't be afraid to ask questions in the comments. I'd love to help!
Feel free to take a look at the repository for my Ghost site. While I don't recommend forking my site as the base of your own, it can be a great starting point and a nice place to copy/paste some things to speed up the process. If you see anything that serves you, feel free to copy it as your own!
Ghost is architected to only have a single instance running per site. No clustering, sharding, nor any other multi-server setups. This drastically reduces possibilities for safe rollouts to ensure zero downtime.
As a side note, don't bother attempting to use Digital Ocean for your MySQL database. Ghost has tables without primary keys, but Digital Ocean does not allow you to disable the MySQL setting for this.