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.yamldeployment.yamlservice.yamlpersistentvolume.yamlpersistentvolumeclaim.yamlconfigmap.yamlkustomization.yaml
Define the
matrixnamespace 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
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/matrixexists 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
Store the Synapse
homeserver.yamlconfiguration so the container can mount it.
- Populate
clusters/prod/apps/matrix/configmap.yamlwith 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
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
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
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
Kustomizationresource to the cluster entrypoint (e.g.,clusters/prod/apps/kustomization.yaml) referencing./matrix
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
caddynamespace:kubectl get configmap -n caddy
- Edit the
Caddyfileentry 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
--watchflag 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
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.yamlto permit outbound traffic to thematrixnamespace 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
matrixnamespace has the labelkubernetes.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
matrixnamespace to accept traffic from thepicoclawnamespace: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.yamland add it toclusters/prod/apps/matrix/kustomization.yamlresources list - Add the new NetworkPolicy to the picoclaw kustomization resources list in
clusters/prod/apps/picoclaw/kustomization.yaml
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
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
- 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
nodeSelectorornodeNameif 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 thecaddynamespace NetworkPolicy if one exists - If Picoclaw uses a
config.yamlfile 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.ccpoints to the Caddy ingress IP/LoadBalancer before testing the public endpoint