Dave's Blog

I use both Amazon AWS and Cloudflare, primarily all my compute is on Amazon and I just use Cloudflare for DNS and tunnelling to my K3s cluster at home.

Cloudflare has been growing it's compute infrastructer over the last few years and I'm curious as to what it can offer me as a PHP / Symfony developer.

Here's my simple guide to getting a POC running on Cloudflare.

Prerequisites: You pay $5 a month for Cloudflare Workers.

npm i -D wrangler@latest

npm create cloudflare@latest -- --template=cloudflare/templates/containers-template

It will ask you to name your project, I went with 'symfony-worker-poc'.

I chose to deploy my app, which does so immediately, but I guess you can do this later if you want.

cd symfony-worker-poc

(I've assumed you already have symfony cli installed, if not do that first) symfony new symfonyapp --version="7.3.x" --webapp

Now edit your Dockerfile:

# ------------------------------------------
# Stage 1: build vendor/ with Composer only
# ------------------------------------------
FROM composer:latest AS vendor
WORKDIR /app

# Copy only dependency manifests first for layer caching
COPY symfonyapp/composer.json symfonyapp/composer.lock* /app/

# Install prod deps (skip scripts; bin/console not present here)
# BuildKit cache (optional) speeds rebuilds if you enable DOCKER_BUILDKIT=1
RUN --mount=type=cache,target=/tmp/composer-cache \
    composer install \
      --no-dev --no-interaction --prefer-dist --no-progress --no-scripts \
      --no-ansi \
      --working-dir=/app \
      --no-cache

# (No need to copy the whole app in this stage; we'll do it in final)

# ------------------------------------------
# Stage 2: final runtime (FrankenPHP)
# ------------------------------------------
FROM dunglas/frankenphp:1.9.1-php8.3

# Keep your extensions
RUN install-php-extensions intl opcache

ENV APP_ENV=prod \
    PHP_OPCACHE_ENABLE=1 \
    PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \
    SERVER_NAME=":8080"

WORKDIR /app

# Bring only vendor from builder
COPY --from=vendor /app/vendor /app/vendor
COPY --from=vendor /app/composer.json /app/composer.json
COPY --from=vendor /app/composer.lock /app/composer.lock

# Copy only what's needed at runtime (avoid tests, docs, node_modules, etc.)
# Adjust if your app uses additional dirs (e.g., translations/)
COPY symfonyapp/bin/        /app/bin/
COPY symfonyapp/config/     /app/config/
COPY symfonyapp/public/     /app/public/
COPY symfonyapp/src/        /app/src/
COPY symfonyapp/templates/  /app/templates/
COPY symfonyapp/.env*       /app/
# COPY symfonyapp/translations/ /app/translations/

# Minimal Caddyfile for FrankenPHP
RUN printf ':8080 {\n  root * /app/public\n  encode zstd gzip\n  php_server\n  try_files {path} /index.php\n}\n' > /etc/caddy/CaddyFILE

# Warm cache now that bin/console exists (safe to ignore failures for POC)
RUN php bin/console cache:clear --env=prod || true

CMD ["frankenphp", "run", "--config", "/etc/caddy/CaddyFILE"]

After that you should be able to run: npx wrangler deploy

At the end you'll get a URL you can hit, something like: https://symfony-worker-poc.madelin.workers.dev/

Just be aware you don't yet have a controller, so it will show the Symfony error page.

I've noticed the cold start is quite long, ~1 second ish but a bigger app will be longer.

You have some control over this, it's the sleepAfter value in the index.ts file. It can be “0s” so it doesn't sleep however, that could get expensive. Other acceptable values are: “30s”, “60s” and “120s”.

You could also ping it periodically to keep it alive.

In the next installment we'll look at how we handle classic SaaS stuff like uploads, databases and all that jazz. Keeping it as Cloudflare native as possible.

It's been a while since I last posted about the Pi Cluster. So what's on it now?

From memory there is:

  • This blog, which is a WriteFreely instance. Super simple blogging platform that uses markdown.
  • A matrix, synapse server. Which is essentially a self hosted WhatsApp, quite blown away by how good it is. It's obviously not quite as polished as WhatsApp but it's not far off, all of the core functionality is there, sharing pictures, location, polls and more. I wanted it so I could send automated messages to myself or my partner without having to rely on Telegram (which also makes that relatively easy but supposedly has Russian links – there are further alternatives I'm just a control freak).
  • A demo WordPress instance for testing plugins and stuff.
  • A single MySQL instance to handle DB storage over the cluster. Not exactly super sophisticated or fault tolerant but fine for my needs.
  • Graphana for monitoring stuff, though largely unused yet.
  • n8n, this is probably my most used self hosted tool, it's an automation platform with a lovely drag and drop interface. I've set up quite a few things now, for work I have some automations that monitor emails that summarize and report back anything important with the help of AI. Converting emails into Asana tasks and things like that. On a personal level there's a few YouTube channels I watch for stock market tips and they are often long and dull, so I monitor those through their RSS feeds and grab the transcript, summarize it and the send myself a note through the matrix server. I also have another that monitors a Google Sheet with my portfolio in it, alerting me to any major movements.
  • For work I'm looking at stream monitoring tech so I rolled my own API with Node/Express and a Rust script and that's being tested there before deployment.
  • A few puppeteer based node scripts for monitoring/interacting with sites that don't have API's. I won't mention it here as I'm surely breaking ToS but it summarizes my interactions with people over the course of the day and pops a note in my journal.
  • Speaking of journals, that's an instance of SilverBullet which I previously blogged about.

There is definitely more but that's all I can remember for now.

It's getting to the point where it needs a few new nodes. I have a Pi 5 that's not really doing anything so might add that to it. If I had the money I would make another case of 4 x 16GB Pi5's for more demanding tasks (puppeteer for example is quite a heavy load on a Pi 4). 🤔

I've been looking for a self hosted journaling solution for a few weeks now on and off and have tried a few. But today I've settled on SilverBullet. It's incredibly flexible and developer-friendly.

It's also really easy to host in k8s, here's a quick intro into what it can do:

Here's my no frills deployment: (I don't think you really need any frills to be fair)

You probably want to change the PVC storageClassName unless you also use Longhorn.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: silverbullet-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: longhorn

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: silverbullet
spec:
  replicas: 1
  selector:
    matchLabels:
      app: silverbullet
  template:
    metadata:
      labels:
        app: silverbullet
    spec:
      containers:
        - name: silverbullet
          image: ghcr.io/silverbulletmd/silverbullet:v2
          ports:
            - containerPort: 3000
          volumeMounts:
            - mountPath: /space
              name: silverbullet-data
      volumes:
        - name: silverbullet-data
          persistentVolumeClaim:
            claimName: silverbullet-pvc

---
apiVersion: v1
kind: Service
metadata:
  name: silverbullet
spec:
  type: ClusterIP
  selector:
    app: silverbullet
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000

All of my services run as ClusterIP as I expose them externally with a Cloudflared Tunnel from inside the cluster. If just using locally then NodePort would suffice.

In the process of moving house (a year ago) my original Raspberry Pi cluster that housed this blog died, or became so overly complicated and cluttered that I decided to start a fresh.

Previously this blog was a WordPress install, which is great, very powerful but overkill for my simple needs. The journey that got me here didn't start out as “let's resurrect my blog”, I was actually looking for a light weight journalling tool.

My as yet unrealised plan was to take all the messages of the day from people relevant to work and personal life be it on email, slack and possibly other platforms and through the power of AI create a journal entry each day – automatically.

Anyway, that has yet to materialise but my journaling requirements were that I wanted an easy to use tool that I could self host, had an API and supported markdown for simple AI-friendly formatting.

There are probably hundreds, definitely tens of options. Eventually I settled on writefreely. It's written in go, is super light weight, has an API, you can self host and it's based around markdown.

Anywho, it's day one of using it. So far so good.

If you want to have a play yourself I crudely documented my setup here:

https://github.com/pintofbeer/writefreely

#cluster #tech #raspberrypi #k8s #k3s