0x7b2.net

My personal blog about tech and stuff.

Quick update on the site

Tuesday, 14 July 2020
Last edited Icon depciting pen for last edited post 15 July 2020

My first planned post about how I publish this blog using Tekton Pipelines and fluxcd proved to take quite some time to write. In the meantime I have implemented privacy conscious tracking on the site using Plausible Analytics1. Plausible is a simple tracking script to give me some metrics on how many people visit my site, and where they have been referred from.

If you wish to read more about what data I collect and how to block requests to my Plausible instance please visit Privacy.

Modifications to Plausible

Getting plausible to run on my mixed arch cluster proved to require some additional work, but after some fixes I managed to get it running as shown here:

Image showing kubectl output of services used on this site.

The first problem I ran in to was that the official Dockerfile didn’t compile on arm64. This was due to a bug in the nodejs package provided by Debian Buster.2 The fix for this was to install the official nodejs repos for Debian manually and installing the latest LTS version of nodejs.3 After these changes I got the Dockerfile to build for both amd64 and arm64 with no further issues.

The second problem I had to deal with before being able to host Plausible comfortably on Kubernetes was that by default the application was setup to load the database connection configuration through a postgres url, instead of via separate variables for each field.4 To fix this I needed to edit the elixir config files to optionally read database connection variables through DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_USER, and DATABASE_PASSWORD instead of just DATABASE_URL.

I’m planning to submit a pull request with my changes to the Plausible Analytics upstream, but until then they can be found in my fork of Plasubile.5 The docker images built with these changes can also be found on dockerhub.6

Running Plausible on Kubernetes

With the changes made to plausible all I had left was to construct the Kubernetes yaml files to spin up the required services. These are nothing out of the ordinary, but I’ll still share them here if someone else wants some tips for how to run Plausible analytics on kubernetes.

Here’s some starter yaml for the base analytics services; keep in mind that it’s important to change the default values provided in this secret. This deployment also require a ConfigMap with any configuration values not found in the secrets.

---                             # analytics/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: plausible
    tier: frontend
  name: analytics
spec:
  replicas: 1
  selector:
    matchLabels:
      app: plausible
      tier: frontend
  template:
    metadata:
      labels:
        app: plausible
        tier: frontend
    spec:
      containers:
        - name: analytics
          image: llndqvst/plausibleanalytics:master-d819a3a
          envFrom:
            - configMapRef:
                name: analytics-env
            - secretRef:
                name: analytics-secret
          env:
            - name: DATABASE_USER
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_USER
            - name: DATABASE_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_PASSWORD
          ports:
            - name: analytics-web
              containerPort: 8080
          readinessProbe:
            httpGet:
              path: /
              port: analytics-web
              httpHeaders:
                - name: Host
                  value: localhost
            initialDelaySeconds: 3
            periodSeconds: 3
            failureThreshold: 10
          livenessProbe:
            httpGet:
              path: /
              port: analytics-web
              httpHeaders:
                - name: Host
                  value: localhost
            initialDelaySeconds: 15
            periodSeconds: 30
            failureThreshold: 2

---                             # analytics/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: analytics
spec:
  ports:
    - name: analytics
      port: 8080
      targetPort: analytics-web
      protocol: TCP
  selector:
    app: plausible
    tier: frontend

---                             # analytics/sample-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: analytics-secret
  labels:
    app: plausible
type: Opaque
stringData:
  SECRET_KEY_BASE: iYb1mP5cnmY+gUxo7C/h6XMigossPhzwd8/ic6LFnQ9Y58Fl1xduSWaPq0fHDdbn
  SIGNING_SALT: PL/THF0VMOzuv1bOcldjDzYFBLryvXNs
  ADMIN_USER_NAME: admin
  ADMIN_USER_EMAIL: admin@0x7b2.net
  ADMIN_USER_PWD: test1234!
  SMTP_USER_NAME: fakeuser@plausible.local
  SMTP_USER_PWD: password

To get plausible analytics running you also need a running Clickhouse event database, as well as a postgres database.

Here is a snippet for how to run the postgres database;

---                             # postgres/statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app: plausible
    tier: db
  name: postgres
spec:
  serviceName: postgres
  replicas: 1
  selector:
    matchLabels:
      app: plausible
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: plausible
        tier: db
    spec:
      containers:
        - name: postgres
          image: postgres:12
          env:
            - name: POSTGRES_DB
              value: plausible
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_PASSWORD
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRES_USER
          ports:
            - containerPort: 5432
              name: postgres
              protocol: TCP
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: postgres-data
          resources:
            limits:
              cpu: 500m
              memory: 1.5Gi
            requests:
              cpu: 50m
              memory: 256Mi
      nodeSelector:
        kubernetes.io/arch: arm64
  volumeClaimTemplates:
    - metadata:
        name: postgres-data
      spec:
        accessModes: [ "ReadWriteOnce" ]
        storageClassName: "nfs-client"
        resources:
          requests:
            storage: 10Gi

---                             # postgres/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  ports:
    - name: postgres
      port: 5432
      targetPort: postgres
      protocol: TCP
  selector:
    app: plausible
    tier: db

---                             # postgres/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: postgres-secret
  labels:
    app: plausible
type: Opaque
stringData:
  POSTGRES_PASSWORD: postgres
  POSTGRES_USER: postgres

and here’s a snippet for the Clickhouse event database:

---                             # events-db/statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app: plausible
    tier: events-db
  name: events-db
spec:
  serviceName: events-db
  replicas: 1
  selector:
    matchLabels:
      app: plausible
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: plausible
        tier: events-db
    spec:
      containers:
        - name: clickhouse-server
          image: yandex/clickhouse-server:20.5.2.7
          ports:
            - containerPort: 8123
              name: clickhouse
              protocol: TCP
          volumeMounts:
            - name: clickhouse-data
              mountPath: /var/lib/clickhouse
          resources:
            limits:
              cpu: 500m
              memory: 1.5Gi
            requests:
              cpu: 50m
              memory: 256Mi
      nodeSelector:
        kubernetes.io/arch: amd64
      volumes:
        - name: clickhouse-data
          emptyDir: {}

---                             # events-db/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: events-db
spec:
  ports:
    - name: clickhouse
      port: 8123
      targetPort: clickhouse
      protocol: TCP
  selector:
    app: plausible
    tier: events-db

With these snippets as a base you should have something to get started to host Plausible on Kubernetes. Also be aware that you need to run database migrations before running the analytics container for the first time. This can be done with either a Kubernetes job, or launching the analytics container with a shell and manually running /entrypoint.sh db createdb ; /entrypoint.sh db migrate.

I realize that the amount of boilerplate in Kubernetes templates makes snippets like these quite unpractical, but at the moment my Kubernetes templates repo isn’t quite ready for sharing. When I get around to cleaning my repo I’ll probably update this post with a link to that instead of these huge code-snippets. In the meantime you’ll have to do with copying and pasting these snippets if you want to use them.

Closing thoughts

Running Plausible on Kubernetes proved to be quite easy, although I had to make some changes to run it smoothly. Everything seems to be running smoothly and I haven’t stumbled upon any problems, although it should be noted that my site hasn’t exactly received much traffic.

Image showing plausible analytics with 5 unique visitors.

If you have any questions regarding this post or my experience running Plausible Analytics on Kubernetes please contact me. My github profile can be found at the top of this site, other contact information can be found under about.


  1. https://plausible.io/ ↩︎

  2. https://github.com/nodejs/docker-node/issues/813#issuecomment-590599414 ↩︎

  3. https://github.com/nodesource/distributions/blob/master/README.md#debinstall ↩︎

  4. https://github.com/plausible/analytics/blob/1c501db394cacf03256a42c7756a7b9161feb8ba/HOSTING.md#database ↩︎

  5. https://github.com/llndqvst/analytics ↩︎

  6. https://hub.docker.com/r/llndqvst/plausibleanalytics ↩︎