Skip to content

Instantly share code, notes, and snippets.

@sergsoares
Created March 10, 2026 15:32
Show Gist options
  • Select an option

  • Save sergsoares/93db66a269dc3898eb5b6909e28bc858 to your computer and use it in GitHub Desktop.

Select an option

Save sergsoares/93db66a269dc3898eb5b6909e28bc858 to your computer and use it in GitHub Desktop.
Plan to create Matrix server

Plan: Deploy Matrix Server in FluxCD with Picoclaw Integration


Task 1: Create FluxCD Folder Structure for Matrix

Scaffold all manifest files needed for the Matrix (Synapse) server under the FluxCD apps directory.

  • Open the FluxCD repository locally
  • Create folder: clusters/prod/apps/matrix/
  • Create the following empty files inside clusters/prod/apps/matrix/:
    • namespace.yaml
    • deployment.yaml
    • service.yaml
    • persistentvolume.yaml
    • persistentvolumeclaim.yaml
    • configmap.yaml
    • kustomization.yaml

Task 2: Create Matrix Namespace

Define the matrix namespace that will isolate all Matrix server resources.

  • Populate clusters/prod/apps/matrix/namespace.yaml:
    apiVersion: v1
    kind: Namespace
    metadata:
      name: matrix
      labels:
        app.kubernetes.io/name: matrix

Task 3: Create HostPath PersistentVolume and PVC

Provision persistent storage for Matrix data using a HostPath volume on the node.

  • Populate clusters/prod/apps/matrix/persistentvolume.yaml:
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: matrix-data-pv
    spec:
      capacity:
        storage: 20Gi
      accessModes:
        - ReadWriteOnce
      hostPath:
        path: /data/matrix
      storageClassName: manual
      persistentVolumeReclaimPolicy: Retain
  • Ensure the host path /data/matrix exists on the node:
    mkdir -p /data/matrix && chown 991:991 /data/matrix
  • Populate clusters/prod/apps/matrix/persistentvolumeclaim.yaml:
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: matrix-data-pvc
      namespace: matrix
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 20Gi
      storageClassName: manual

Task 4: Create Matrix ConfigMap

Store the Synapse homeserver.yaml configuration so the container can mount it.

  • Populate clusters/prod/apps/matrix/configmap.yaml with a base Synapse config:
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: matrix-config
      namespace: matrix
    data:
      homeserver.yaml: |
        server_name: "matrix.poc.ourlab.cc"
        pid_file: /data/homeserver.pid
        listeners:
          - port: 8008
            tls: false
            type: http
            x_forwarded: true
            resources:
              - names: [client, federation]
                compress: false
        database:
          name: sqlite3
          args:
            database: /data/homeserver.db
        log_config: "/data/matrix.log.config"
        media_store_path: /data/media_store
        registration_shared_secret: "<CHANGE_ME_REGISTRATION_SECRET>"
        report_stats: false
        signing_key_path: "/data/matrix.poc.ourlab.cc.signing.key"
        trusted_key_servers:
          - server_name: "matrix.org"
  • Replace <CHANGE_ME_REGISTRATION_SECRET> with a securely generated value:
    openssl rand -hex 32

Task 5: Create Matrix Deployment

Deploy the matrix-synapse container with the config and persistent volume mounted.

  • Populate clusters/prod/apps/matrix/deployment.yaml:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: matrix-synapse
      namespace: matrix
      labels:
        app: matrix-synapse
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: matrix-synapse
      strategy:
        type: Recreate
      template:
        metadata:
          labels:
            app: matrix-synapse
        spec:
          securityContext:
            fsGroup: 991
          initContainers:
            - name: generate-keys
              image: matrixdotorg/synapse:latest
              command: ["python", "-m", "synapse.app.homeserver", "--config-path", "/data/homeserver.yaml", "--generate-keys"]
              volumeMounts:
                - name: data
                  mountPath: /data
                - name: config
                  mountPath: /data/homeserver.yaml
                  subPath: homeserver.yaml
          containers:
            - name: synapse
              image: matrixdotorg/synapse:latest
              ports:
                - containerPort: 8008
                  name: http
              volumeMounts:
                - name: data
                  mountPath: /data
                - name: config
                  mountPath: /data/homeserver.yaml
                  subPath: homeserver.yaml
              resources:
                requests:
                  memory: "256Mi"
                  cpu: "100m"
                limits:
                  memory: "1Gi"
                  cpu: "500m"
          volumes:
            - name: data
              persistentVolumeClaim:
                claimName: matrix-data-pvc
            - name: config
              configMap:
                name: matrix-config

Task 6: Create Matrix Service

Expose the Matrix Synapse pod internally so Caddy and other pods can reach port 8008.

  • Populate clusters/prod/apps/matrix/service.yaml:
    apiVersion: v1
    kind: Service
    metadata:
      name: matrix-synapse
      namespace: matrix
      labels:
        app: matrix-synapse
    spec:
      selector:
        app: matrix-synapse
      ports:
        - name: http
          port: 8008
          targetPort: 8008
      type: ClusterIP

Task 7: Create Matrix Kustomization

Wire all matrix manifests together under a single Kustomize entrypoint for FluxCD to reconcile.

  • Populate clusters/prod/apps/matrix/kustomization.yaml:
    apiVersion: kustomize.config.k8s.io/v1beta1
    kind: Kustomization
    namespace: matrix
    resources:
      - namespace.yaml
      - persistentvolume.yaml
      - persistentvolumeclaim.yaml
      - configmap.yaml
      - deployment.yaml
      - service.yaml
  • Add a FluxCD Kustomization resource to the cluster entrypoint (e.g., clusters/prod/apps/kustomization.yaml) referencing ./matrix

Task 8: Add matrix.poc.ourlab.cc to Caddy Namespace CaddyFile

Configure the Caddy reverse proxy to route HTTPS traffic for matrix.poc.ourlab.cc to the matrix service.

  • Locate the existing Caddy ConfigMap in the caddy namespace:
    kubectl get configmap -n caddy
  • Edit the Caddyfile entry inside the FluxCD managed ConfigMap (e.g., clusters/prod/apps/caddy/configmap.yaml) and add the following block:
    matrix.poc.ourlab.cc {
        reverse_proxy matrix-synapse.matrix.svc.cluster.local:8008
    }
    
  • Verify the Caddy deployment mounts and reloads the updated ConfigMap on change (check for --watch flag or a reload sidecar in the Caddy deployment)
  • Commit and push the Caddy ConfigMap change:
    git add clusters/prod/apps/caddy/configmap.yaml
    git commit -m "feat(caddy): add matrix.poc.ourlab.cc reverse proxy"
    git push

Task 9: Configure Picoclaw NetworkPolicy to Allow Matrix Communication

Extend the picoclaw Kustomize NetworkPolicy so picoclaw pods can initiate connections to the matrix namespace.

  • Locate the existing NetworkPolicy files under the picoclaw kustomize path:
    find clusters/prod/apps/picoclaw -name "*.yaml" | xargs grep -l "NetworkPolicy"
  • Add or update an egress rule in clusters/prod/apps/picoclaw/networkpolicy.yaml to permit outbound traffic to the matrix namespace on port 8008:
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: picoclaw-egress-matrix
      namespace: picoclaw
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
        - to:
            - namespaceSelector:
                matchLabels:
                  kubernetes.io/metadata.name: matrix
              podSelector:
                matchLabels:
                  app: matrix-synapse
          ports:
            - protocol: TCP
              port: 8008
  • Verify the matrix namespace has the label kubernetes.io/metadata.name: matrix (auto-applied in K8s 1.21+):
    kubectl get namespace matrix --show-labels
  • Add an ingress rule in a NetworkPolicy in the matrix namespace to accept traffic from the picoclaw namespace:
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: matrix-ingress-from-picoclaw
      namespace: matrix
    spec:
      podSelector:
        matchLabels:
          app: matrix-synapse
      policyTypes:
        - Ingress
      ingress:
        - from:
            - namespaceSelector:
                matchLabels:
                  kubernetes.io/metadata.name: picoclaw
          ports:
            - protocol: TCP
              port: 8008
  • Save this file as clusters/prod/apps/matrix/networkpolicy.yaml and add it to clusters/prod/apps/matrix/kustomization.yaml resources list
  • Add the new NetworkPolicy to the picoclaw kustomization resources list in clusters/prod/apps/picoclaw/kustomization.yaml

Task 10: Update Picoclaw Configuration to Connect to Matrix Server

Point the Picoclaw application configuration at the internal Matrix Synapse service endpoint.

  • Locate the Picoclaw ConfigMap or Secret that holds application config:
    kubectl get configmap,secret -n picoclaw
  • Edit the Picoclaw config file in FluxCD (e.g., clusters/prod/apps/picoclaw/configmap.yaml) and set the Matrix homeserver URL:
    matrix_homeserver_url: "http://matrix-synapse.matrix.svc.cluster.local:8008"
    matrix_server_name: "matrix.poc.ourlab.cc"
  • If Picoclaw uses an access token to authenticate with Matrix, generate an admin user on Synapse first:
    kubectl exec -n matrix deployment/matrix-synapse -- \
      register_new_matrix_user -c /data/homeserver.yaml \
      -u picoclaw-bot -p <STRONG_PASSWORD> -a http://localhost:8008
  • Store the bot credentials as a Kubernetes Secret (use SealedSecret or equivalent):
    kubectl create secret generic picoclaw-matrix-creds \
      --from-literal=username=picoclaw-bot \
      --from-literal=password=<STRONG_PASSWORD> \
      -n picoclaw --dry-run=client -o yaml | kubeseal > sealedsecret-matrix-creds.yaml
  • Reference the secret in the Picoclaw deployment's env vars or volume mounts
  • Commit all picoclaw config changes:
    git add clusters/prod/apps/picoclaw/
    git commit -m "feat(picoclaw): connect to matrix.poc.ourlab.cc homeserver"
    git push

Task 11: Validate the Full Stack

Confirm all components are running and reachable end-to-end.

  • Watch FluxCD reconcile all kustomizations:
    flux get kustomizations --watch
  • Verify Matrix pod is Running:
    kubectl get pods -n matrix
  • Confirm PVC is Bound:
    kubectl get pvc -n matrix
  • Test internal DNS resolution from a debug pod:
    kubectl run -it --rm debug --image=alpine --restart=Never -- \
      wget -qO- http://matrix-synapse.matrix.svc.cluster.local:8008/_matrix/static/
  • Test public endpoint responds via Caddy:
    curl https://matrix.poc.ourlab.cc/_matrix/static/
  • Verify Picoclaw pods restarted cleanly and logs show successful Matrix connection:
    kubectl logs -n picoclaw -l app=picoclaw --tail=50

Notes

  • Replace <CHANGE_ME_REGISTRATION_SECRET> and <STRONG_PASSWORD> with securely generated values — never commit plaintext secrets
  • The HostPath PV binds to a specific node; ensure the Matrix pod is scheduled to that node using a nodeSelector or nodeName if your cluster has multiple nodes
  • Caddy must have an egress NetworkPolicy rule allowing it to reach matrix.svc.cluster.local:8008 — add a similar egress rule to the caddy namespace NetworkPolicy if one exists
  • If Picoclaw uses a config.yaml file mounted from a ConfigMap, the key names (matrix_homeserver_url, etc.) must match Picoclaw's expected config schema exactly
  • FluxCD will reconcile changes within its configured interval (typically 1–5 minutes); use flux reconcile kustomization <name> to force immediate sync
  • Ensure DNS for matrix.poc.ourlab.cc points to the Caddy ingress IP/LoadBalancer before testing the public endpoint
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment