Fix mail (accounts), matrix (homeserver.yaml), irc (proper image+config)

This commit is contained in:
root
2026-03-09 17:02:59 -05:00
parent ef442e29eb
commit 3c29b0abe5
3 changed files with 1179 additions and 741 deletions

View File

@@ -1,184 +1,554 @@
# UnrealIRCd + Anope IRC Services # UnrealIRCd + Anope IRC Services
# PLACEHOLDER - UnrealIRCd needs config files mounted before running # ArgoCD managed - BlueJay Lab
# ArgoCD managed - BlueJay Lab ---
--- apiVersion: v1
apiVersion: v1 kind: Namespace
kind: Namespace metadata:
metadata: name: irc
name: irc labels:
labels: app.kubernetes.io/part-of: bluejay-infra
app.kubernetes.io/part-of: bluejay-infra ---
--- # TLS Certificate for IRC
# UnrealIRCd PVC apiVersion: cert-manager.io/v1
apiVersion: v1 kind: Certificate
kind: PersistentVolumeClaim metadata:
metadata: name: irc-tls
name: unrealircd-data namespace: irc
namespace: irc spec:
spec: secretName: irc-tls
accessModes: [ReadWriteOnce] issuerRef:
resources: name: step-ca-acme
requests: kind: ClusterIssuer
storage: 1Gi dnsNames:
--- - irc.iamworkin.lan
# Anope PVC ---
apiVersion: v1 # UnrealIRCd configuration
kind: PersistentVolumeClaim apiVersion: v1
metadata: kind: Secret
name: anope-data metadata:
namespace: irc name: unrealircd-config
spec: namespace: irc
accessModes: [ReadWriteOnce] type: Opaque
resources: stringData:
requests: unrealircd.conf: |
storage: 1Gi /* BlueJay Lab IRC - UnrealIRCd 6.x config */
--- /* Managed by ArgoCD */
# UnrealIRCd Deployment
# NOTE: This is a placeholder. UnrealIRCd requires configuration files include "modules.default.conf";
# (unrealircd.conf, TLS certs, etc.) to be present in /data before starting. include "help/help.conf";
# Mount config via ConfigMap/Secret or init container before enabling. include "operclass.default.conf";
apiVersion: apps/v1 include "snomasks.default.conf";
kind: Deployment
metadata: me {
name: unrealircd name "irc.iamworkin.lan";
namespace: irc info "BlueJay Lab IRC Server";
labels: sid 001;
app: unrealircd }
spec:
replicas: 1 admin {
selector: "BlueJay Lab IRC";
matchLabels: "admin@iamwork.in";
app: unrealircd }
template:
metadata: class clients {
labels: pingfreq 90;
app: unrealircd maxclients 500;
spec: sendq 200k;
containers: recvq 8000;
- name: unrealircd }
image: ghcr.io/unrealircd/unrealircd:latest
ports: class opers {
- containerPort: 6667 pingfreq 90;
name: irc-plain maxclients 50;
- containerPort: 6697 sendq 1M;
name: irc-tls recvq 8000;
- containerPort: 8067 }
name: services-link
volumeMounts: class servers {
- name: unrealircd-data pingfreq 60;
mountPath: /data connfreq 15;
resources: maxclients 10;
requests: sendq 20M;
memory: 64Mi }
cpu: 50m
limits: allow {
memory: 256Mi mask *;
cpu: 250m class clients;
volumes: maxperip 5;
- name: unrealircd-data }
persistentVolumeClaim:
claimName: unrealircd-data listen {
--- ip *;
# Anope IRC Services Deployment port 6667;
# NOTE: Placeholder. Anope requires services.conf with link block }
# matching UnrealIRCd's link configuration.
apiVersion: apps/v1 listen {
kind: Deployment ip *;
metadata: port 6697;
name: anope options { tls; }
namespace: irc tls-options {
labels: certificate "/etc/ssl/irc/tls.crt";
app: anope key "/etc/ssl/irc/tls.key";
spec: }
replicas: 1 }
selector:
matchLabels: listen {
app: anope ip *;
template: port 8067;
metadata: }
labels:
app: anope oper bluejay {
spec: mask *;
containers: password "BlueJay-IRC-Oper-2026";
- name: anope operclass netadmin-with-override;
image: anope/anope:latest class opers;
volumeMounts: }
- name: anope-data
mountPath: /data drpass {
resources: restart "BlueJay-IRC-Oper-2026";
requests: die "BlueJay-IRC-Oper-2026";
memory: 64Mi }
cpu: 25m
limits: link services.iamworkin.lan {
memory: 128Mi incoming {
cpu: 100m mask *;
volumes: }
- name: anope-data password "BlueJay-Services-Link-2026";
persistentVolumeClaim: class servers;
claimName: anope-data }
---
# UnrealIRCd Service (ClusterIP for internal + Traefik TCP routing) ulines {
apiVersion: v1 services.iamworkin.lan;
kind: Service }
metadata:
name: unrealircd log {
namespace: irc source {
spec: all;
selector: \!debug;
app: unrealircd }
ports: destination {
- port: 6667 channel "#ops";
targetPort: 6667 }
name: irc-plain }
- port: 6697
targetPort: 6697 set {
name: irc-tls network-name "BlueJayIRC";
- port: 8067 default-server "irc.iamworkin.lan";
targetPort: 8067 services-server "services.iamworkin.lan";
name: services-link stats-server "stats.iamworkin.lan";
--- help-channel "#general";
# Anope Service cloak-keys {
apiVersion: v1 "bluejay-cloak-key-1-aHR0cHM6Ly9pcmM";
kind: Service "bluejay-cloak-key-2-aWFtd29ya2luLmxhbg";
metadata: "bluejay-cloak-key-3-Ymx1ZWpheS1pcmM";
name: anope }
namespace: irc kline-address "admin@iamwork.in";
spec: maxchannelsperuser 25;
selector: anti-flood {
app: anope everyone {
ports: connect-flood 3:60;
- port: 8067 }
targetPort: 8067 }
name: services-link options {
--- hide-ulines;
# Traefik IngressRouteTCP - IRC plain (6667) show-connect-info;
apiVersion: traefik.io/v1alpha1 }
kind: IngressRouteTCP
metadata: /* TLS config */
name: irc-plain tls {
namespace: irc certificate "/etc/ssl/irc/tls.crt";
spec: key "/etc/ssl/irc/tls.key";
entryPoints: }
- irc }
routes: ---
- match: HostSNI(`*`) # Anope configuration
services: apiVersion: v1
- name: unrealircd kind: Secret
port: 6667 metadata:
--- name: anope-config
# Traefik IngressRouteTCP - IRC TLS passthrough (6697) namespace: irc
apiVersion: traefik.io/v1alpha1 type: Opaque
kind: IngressRouteTCP stringData:
metadata: services.conf: |
name: irc-tls define {
namespace: irc name = "services.host"
spec: value = "services.iamworkin.lan"
entryPoints: }
- ircs
routes: uplink {
- match: HostSNI(`*`) host = "unrealircd.irc.svc.cluster.local"
services: port = 8067
- name: unrealircd password = "BlueJay-Services-Link-2026"
port: 6697 }
tls:
passthrough: true serverinfo {
name = "services.iamworkin.lan"
description = "BlueJay IRC Services"
pid = "/data/services.pid"
motd = "/data/services.motd"
}
module {
name = "unreal4"
}
networkinfo {
networkname = "BlueJayIRC"
nicklen = 31
userlen = 10
hostlen = 64
chanlen = 32
mail_from = "noreply@iamwork.in"
}
options {
casemap = "ascii"
seed = 42
strictpasswords
}
module { name = "nickserv" }
module { name = "chanserv" }
module { name = "operserv" }
module { name = "botserv" }
module { name = "hostserv" }
module { name = "memoserv" }
module { name = "global" }
module { name = "db_flatfile" }
module { name = "enc_sha256" }
module { name = "ns_access" }
module { name = "ns_ajoin" }
module { name = "ns_cert" }
module { name = "ns_drop" }
module { name = "ns_group" }
module { name = "ns_identify" }
module { name = "ns_info" }
module { name = "ns_list" }
module { name = "ns_logout" }
module { name = "ns_recover" }
module { name = "ns_register" }
module { name = "ns_set" }
module { name = "ns_suspend" }
module { name = "ns_update" }
module { name = "cs_access" }
module { name = "cs_akick" }
module { name = "cs_ban" }
module { name = "cs_clone" }
module { name = "cs_drop" }
module { name = "cs_enforce" }
module { name = "cs_entrymsg" }
module { name = "cs_flags" }
module { name = "cs_info" }
module { name = "cs_invite" }
module { name = "cs_kick" }
module { name = "cs_list" }
module { name = "cs_log" }
module { name = "cs_mode" }
module { name = "cs_register" }
module { name = "cs_seen" }
module { name = "cs_set" }
module { name = "cs_suspend" }
module { name = "cs_topic" }
module { name = "cs_unban" }
module { name = "os_akill" }
module { name = "os_chankill" }
module { name = "os_defcon" }
module { name = "os_forbid" }
module { name = "os_ignore" }
module { name = "os_info" }
module { name = "os_jupe" }
module { name = "os_kick" }
module { name = "os_kill" }
module { name = "os_list" }
module { name = "os_login" }
module { name = "os_logsearch" }
module { name = "os_mode" }
module { name = "os_modinfo" }
module { name = "os_module" }
module { name = "os_noop" }
module { name = "os_oper" }
module { name = "os_reload" }
module { name = "os_session" }
module { name = "os_set" }
module { name = "os_shutdown" }
module { name = "os_stats" }
module { name = "os_svsnick" }
module { name = "os_sxline" }
module { name = "os_update" }
module { name = "bs_assign" }
module { name = "bs_badwords" }
module { name = "bs_bot" }
module { name = "bs_info" }
module { name = "bs_kick" }
module { name = "bs_set" }
module { name = "hs_del" }
module { name = "hs_group" }
module { name = "hs_list" }
module { name = "hs_off" }
module { name = "hs_on" }
module { name = "hs_request" }
module { name = "hs_set" }
module { name = "ms_cancel" }
module { name = "ms_check" }
module { name = "ms_del" }
module { name = "ms_ignore" }
module { name = "ms_info" }
module { name = "ms_list" }
module { name = "ms_read" }
module { name = "ms_rsend" }
module { name = "ms_send" }
module { name = "ms_set" }
module { name = "gl_global" }
module { name = "m_dns" }
module { name = "m_helpchan" }
module { name = "m_httpd" }
module { name = "m_ldap" }
module { name = "m_xmlrpc" }
module { name = "m_proxyscan" }
nickserv {
nick = "NickServ"
defaults = "kill_quick ns_secure ns_private hide_email"
registration = "none"
expire = 90d
}
chanserv {
nick = "ChanServ"
defaults = "keeptopic peace cs_secure"
expire = 14d
}
operserv {
nick = "OperServ"
}
botserv {
nick = "BotServ"
defaults = "dontkickops fantasy greet"
}
hostserv {
nick = "HostServ"
}
memoserv {
nick = "MemoServ"
maxmemos = 20
}
global {
nick = "Global"
}
service {
nick = "bluejay"
}
oper {
name = "bluejay"
type = "Services Root"
}
db_flatfile {
database = "/data/anope.db"
fork = yes
}
log {
target = "/data/services.log"
admin = "*"
override = "chanserv/* nickserv/* operserv/*"
commands = "chanserv/* nickserv/* operserv/*"
servers = "*"
channels = "*"
users = "connect disconnect"
}
---
# UnrealIRCd PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: unrealircd-data
namespace: irc
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 1Gi
---
# Anope PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: anope-data
namespace: irc
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 1Gi
---
# UnrealIRCd Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: unrealircd
namespace: irc
labels:
app: unrealircd
spec:
replicas: 1
selector:
matchLabels:
app: unrealircd
template:
metadata:
labels:
app: unrealircd
spec:
containers:
- name: unrealircd
image: ircd/unrealircd:latest
ports:
- containerPort: 6667
name: irc-plain
- containerPort: 6697
name: irc-tls
- containerPort: 8067
name: services-link
volumeMounts:
- name: unrealircd-config
mountPath: /ircd/unrealircd.conf
subPath: unrealircd.conf
- name: unrealircd-data
mountPath: /data
- name: irc-tls
mountPath: /etc/ssl/irc
readOnly: true
resources:
requests:
memory: 64Mi
cpu: 50m
limits:
memory: 256Mi
cpu: 250m
volumes:
- name: unrealircd-config
secret:
secretName: unrealircd-config
- name: unrealircd-data
persistentVolumeClaim:
claimName: unrealircd-data
- name: irc-tls
secret:
secretName: irc-tls
---
# Anope IRC Services Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: anope
namespace: irc
labels:
app: anope
spec:
replicas: 1
selector:
matchLabels:
app: anope
template:
metadata:
labels:
app: anope
spec:
containers:
- name: anope
image: anope/anope:latest
volumeMounts:
- name: anope-config
mountPath: /data/conf/services.conf
subPath: services.conf
- name: anope-data
mountPath: /data
resources:
requests:
memory: 64Mi
cpu: 25m
limits:
memory: 128Mi
cpu: 100m
volumes:
- name: anope-config
secret:
secretName: anope-config
- name: anope-data
persistentVolumeClaim:
claimName: anope-data
---
# UnrealIRCd Service
apiVersion: v1
kind: Service
metadata:
name: unrealircd
namespace: irc
spec:
selector:
app: unrealircd
ports:
- port: 6667
targetPort: 6667
name: irc-plain
- port: 6697
targetPort: 6697
name: irc-tls
- port: 8067
targetPort: 8067
name: services-link
---
# Anope Service
apiVersion: v1
kind: Service
metadata:
name: anope
namespace: irc
spec:
selector:
app: anope
ports:
- port: 8067
targetPort: 8067
name: services-link
---
# Traefik IngressRouteTCP - IRC plain (6667)
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: irc-plain
namespace: irc
spec:
entryPoints:
- irc
routes:
- match: HostSNI(`*`)
services:
- name: unrealircd
port: 6667
---
# Traefik IngressRouteTCP - IRC TLS passthrough (6697)
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: irc-tls
namespace: irc
spec:
entryPoints:
- irctls
routes:
- match: HostSNI(`*`)
services:
- name: unrealircd
port: 6697
tls:
passthrough: true

View File

@@ -1,203 +1,222 @@
# docker-mailserver - Postfix + Dovecot + rspamd # docker-mailserver - Postfix + Dovecot + rspamd
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
--- ---
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: mail name: mail
labels: labels:
app.kubernetes.io/part-of: bluejay-infra app.kubernetes.io/part-of: bluejay-infra
--- ---
# Mail data PVC # Mail accounts Secret (postfix-accounts.cf format: user@domain|{SHA512-CRYPT}hash)
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: Secret
metadata: metadata:
name: mail-data name: mail-accounts
namespace: mail namespace: mail
spec: type: Opaque
accessModes: [ReadWriteOnce] stringData:
resources: postfix-accounts.cf: |
requests: admin@iamwork.in|{SHA512-CRYPT}$6$1355214084ba403a$LPA.qkZLpv9RqMu8OenCrgYgyHbMwMIAYOuLrbNX/eeiaOj.8rtj9IlMeLDxSc6FdWK9N/PcNmBzV5fJL7IRn/
storage: 5Gi noreply@iamwork.in|{SHA512-CRYPT}$6$1355214084ba403a$LPA.qkZLpv9RqMu8OenCrgYgyHbMwMIAYOuLrbNX/eeiaOj.8rtj9IlMeLDxSc6FdWK9N/PcNmBzV5fJL7IRn/
--- ---
# Mail state PVC # Mail data PVC
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
name: mail-state name: mail-data
namespace: mail namespace: mail
spec: spec:
accessModes: [ReadWriteOnce] accessModes: [ReadWriteOnce]
resources: resources:
requests: requests:
storage: 1Gi storage: 5Gi
--- ---
# docker-mailserver Deployment # Mail state PVC
apiVersion: apps/v1 apiVersion: v1
kind: Deployment kind: PersistentVolumeClaim
metadata: metadata:
name: mailserver name: mail-state
namespace: mail namespace: mail
labels: spec:
app: mailserver accessModes: [ReadWriteOnce]
spec: resources:
replicas: 1 requests:
strategy: storage: 1Gi
type: Recreate ---
selector: # docker-mailserver Deployment
matchLabels: apiVersion: apps/v1
app: mailserver kind: Deployment
template: metadata:
metadata: name: mailserver
labels: namespace: mail
app: mailserver labels:
spec: app: mailserver
hostname: mail spec:
containers: replicas: 1
- name: mailserver strategy:
image: docker.io/mailserver/docker-mailserver:latest type: Recreate
ports: selector:
- containerPort: 25 matchLabels:
name: smtp app: mailserver
- containerPort: 465 template:
name: smtps metadata:
- containerPort: 587 labels:
name: submission app: mailserver
- containerPort: 143 spec:
name: imap hostname: mail
- containerPort: 993 containers:
name: imaps - name: mailserver
env: image: docker.io/mailserver/docker-mailserver:latest
- name: ENABLE_SPAMASSASSIN ports:
value: "1" - containerPort: 25
- name: ENABLE_CLAMAV name: smtp
value: "0" - containerPort: 465
- name: ENABLE_RSPAMD name: smtps
value: "1" - containerPort: 587
- name: TZ name: submission
value: America/Chicago - containerPort: 143
- name: POSTMASTER_ADDRESS name: imap
value: postmaster@iamwork.in - containerPort: 993
- name: OVERRIDE_HOSTNAME name: imaps
value: mail.iamwork.in env:
- name: ENABLE_FAIL2BAN - name: ENABLE_SPAMASSASSIN
value: "0" value: "1"
- name: ENABLE_POSTGREY - name: ENABLE_CLAMAV
value: "0" value: "0"
- name: ONE_DIR - name: ENABLE_RSPAMD
value: "1" value: "1"
- name: PERMIT_DOCKER - name: TZ
value: network value: America/Chicago
- name: SSL_TYPE - name: POSTMASTER_ADDRESS
value: manual value: postmaster@iamwork.in
- name: SSL_CERT_PATH - name: OVERRIDE_HOSTNAME
value: /etc/ssl/mail/tls.crt value: mail.iamwork.in
- name: SSL_KEY_PATH - name: ENABLE_FAIL2BAN
value: /etc/ssl/mail/tls.key value: "0"
volumeMounts: - name: ENABLE_POSTGREY
- name: mail-data value: "0"
mountPath: /var/mail - name: ONE_DIR
- name: mail-state value: "1"
mountPath: /var/mail-state - name: PERMIT_DOCKER
- name: mail-tls value: network
mountPath: /etc/ssl/mail - name: SSL_TYPE
readOnly: true value: manual
resources: - name: SSL_CERT_PATH
requests: value: /etc/ssl/mail/tls.crt
memory: 512Mi - name: SSL_KEY_PATH
cpu: 200m value: /etc/ssl/mail/tls.key
limits: - name: ACCOUNT_PROVISIONER
memory: 2Gi value: FILE
cpu: "1" volumeMounts:
securityContext: - name: mail-data
capabilities: mountPath: /var/mail
add: - name: mail-state
- NET_ADMIN mountPath: /var/mail-state
- SYS_PTRACE - name: mail-tls
volumes: mountPath: /etc/ssl/mail
- name: mail-data readOnly: true
persistentVolumeClaim: - name: mail-accounts
claimName: mail-data mountPath: /tmp/docker-mailserver/postfix-accounts.cf
- name: mail-state subPath: postfix-accounts.cf
persistentVolumeClaim: readOnly: true
claimName: mail-state resources:
- name: mail-tls requests:
secret: memory: 512Mi
secretName: mail-tls cpu: 200m
--- limits:
# SMTP LoadBalancer Service (external) memory: 2Gi
apiVersion: v1 cpu: "1"
kind: Service securityContext:
metadata: capabilities:
name: mail-smtp add:
namespace: mail - NET_ADMIN
annotations: - SYS_PTRACE
metallb.universe.tf/loadBalancerIPs: 10.0.56.202 volumes:
spec: - name: mail-data
type: LoadBalancer persistentVolumeClaim:
selector: claimName: mail-data
app: mailserver - name: mail-state
ports: persistentVolumeClaim:
- port: 25 claimName: mail-state
targetPort: 25 - name: mail-tls
name: smtp secret:
protocol: TCP secretName: mail-tls
- port: 465 - name: mail-accounts
targetPort: 465 secret:
name: smtps secretName: mail-accounts
protocol: TCP ---
- port: 587 # SMTP LoadBalancer Service (external)
targetPort: 587 apiVersion: v1
name: submission kind: Service
protocol: TCP metadata:
--- name: mail-smtp
# IMAP ClusterIP Service (internal) namespace: mail
apiVersion: v1 annotations:
kind: Service metallb.universe.tf/loadBalancerIPs: 10.0.56.202
metadata: spec:
name: mail-imap type: LoadBalancer
namespace: mail selector:
spec: app: mailserver
selector: ports:
app: mailserver - port: 25
ports: targetPort: 25
- port: 143 name: smtp
targetPort: 143 protocol: TCP
name: imap - port: 465
- port: 993 targetPort: 465
targetPort: 993 name: smtps
name: imaps protocol: TCP
--- - port: 587
# TLS Certificate via cert-manager targetPort: 587
apiVersion: cert-manager.io/v1 name: submission
kind: Certificate protocol: TCP
metadata: ---
name: mail-tls # IMAP ClusterIP Service (internal)
namespace: mail apiVersion: v1
spec: kind: Service
secretName: mail-tls metadata:
issuerRef: name: mail-imap
name: step-ca-acme namespace: mail
kind: ClusterIssuer spec:
dnsNames: selector:
- mail.iamworkin.lan app: mailserver
--- ports:
# Traefik IngressRoute - Webmail placeholder - port: 143
# Snappymail will need a separate deployment; this routes to the targetPort: 143
# mail server's HTTP port if available, or to a future webmail deployment name: imap
apiVersion: traefik.io/v1alpha1 - port: 993
kind: IngressRoute targetPort: 993
metadata: name: imaps
name: mail-webmail ---
namespace: mail # TLS Certificate via cert-manager
spec: apiVersion: cert-manager.io/v1
entryPoints: kind: Certificate
- websecure metadata:
routes: name: mail-tls
- match: Host(`mail.iamworkin.lan`) namespace: mail
kind: Rule spec:
services: secretName: mail-tls
- name: mail-imap issuerRef:
port: 993 name: step-ca-acme
tls: kind: ClusterIssuer
secretName: mail-tls dnsNames:
- mail.iamworkin.lan
---
# Traefik IngressRoute - Webmail placeholder
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: mail-webmail
namespace: mail
spec:
entryPoints:
- websecure
routes:
- match: Host(`mail.iamworkin.lan`)
kind: Rule
services:
- name: mail-imap
port: 993
tls:
secretName: mail-tls

View File

@@ -1,354 +1,403 @@
# Matrix Synapse + Element Web # Matrix Synapse + Element Web
# PostgreSQL 16 + Synapse homeserver + Element Web client # PostgreSQL 16 + Synapse homeserver + Element Web client
# ArgoCD managed - BlueJay Lab # ArgoCD managed - BlueJay Lab
--- ---
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
name: matrix name: matrix
labels: labels:
app.kubernetes.io/part-of: bluejay-infra app.kubernetes.io/part-of: bluejay-infra
--- ---
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
name: matrix-db-secret name: matrix-db-secret
namespace: matrix namespace: matrix
type: Opaque type: Opaque
stringData: stringData:
POSTGRES_USER: synapse POSTGRES_USER: synapse
POSTGRES_PASSWORD: BlueJay-Matrix-DB-2026 POSTGRES_PASSWORD: BlueJay-Matrix-DB-2026
POSTGRES_DB: synapse POSTGRES_DB: synapse
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C" POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
--- ---
# PostgreSQL 16 StatefulSet # Synapse homeserver.yaml ConfigMap
apiVersion: apps/v1 apiVersion: v1
kind: StatefulSet kind: ConfigMap
metadata: metadata:
name: matrix-postgres name: synapse-config
namespace: matrix namespace: matrix
labels: data:
app: matrix-postgres homeserver.yaml: |
spec: server_name: "iamworkin.lan"
serviceName: matrix-postgres pid_file: /data/homeserver.pid
replicas: 1 public_baseurl: "https://matrix.iamworkin.lan/"
selector: listeners:
matchLabels: - port: 8008
app: matrix-postgres tls: false
template: type: http
metadata: x_forwarded: true
labels: bind_addresses: ["0.0.0.0"]
app: matrix-postgres resources:
spec: - names: [client, federation]
containers: compress: false
- name: postgres database:
image: postgres:16-alpine name: psycopg2
ports: args:
- containerPort: 5432 user: synapse
name: postgres password: BlueJay-Matrix-DB-2026
envFrom: database: synapse
- secretRef: host: matrix-postgres
name: matrix-db-secret port: 5432
volumeMounts: cp_min: 5
- name: matrix-postgres-data cp_max: 10
mountPath: /var/lib/postgresql/data log_config: "/data/log.config"
subPath: pgdata media_store_path: /data/media_store
resources: registration_shared_secret: "a208f2e4b260f6b7d6ff4566df49c56c8b73fa20b911ce4e617b791ee7868adc"
requests: report_stats: false
memory: 256Mi macaroon_secret_key: "9964f398e8b48a91469ad419d293c06db4562f49df8cc6e129fb3a801fd9052d"
cpu: 100m form_secret: "7b0a9dbaf9ee94450e0b3271c408dfc4d313a55843ce4eec2ac1bb0315ffeb76"
limits: signing_key_path: "/data/signing.key"
memory: 1Gi trusted_key_servers:
cpu: 500m - server_name: "matrix.org"
livenessProbe: enable_registration: false
exec: suppress_key_server_warning: true
command: log.config: |
- pg_isready version: 1
- -U formatters:
- synapse precise:
initialDelaySeconds: 30 format: "%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s"
periodSeconds: 10 handlers:
readinessProbe: console:
exec: class: logging.StreamHandler
command: formatter: precise
- pg_isready loggers:
- -U synapse.storage.SQL:
- synapse level: WARNING
initialDelaySeconds: 5 root:
periodSeconds: 5 level: WARNING
volumeClaimTemplates: handlers: [console]
- metadata: disable_existing_loggers: false
name: matrix-postgres-data ---
spec: # PostgreSQL 16 StatefulSet
accessModes: [ReadWriteOnce] apiVersion: apps/v1
resources: kind: StatefulSet
requests: metadata:
storage: 5Gi name: matrix-postgres
--- namespace: matrix
apiVersion: v1 labels:
kind: Service app: matrix-postgres
metadata: spec:
name: matrix-postgres serviceName: matrix-postgres
namespace: matrix replicas: 1
spec: selector:
selector: matchLabels:
app: matrix-postgres app: matrix-postgres
ports: template:
- port: 5432 metadata:
targetPort: 5432 labels:
name: postgres app: matrix-postgres
clusterIP: None spec:
--- containers:
# Synapse Data PVC - name: postgres
apiVersion: v1 image: postgres:16-alpine
kind: PersistentVolumeClaim ports:
metadata: - containerPort: 5432
name: synapse-data name: postgres
namespace: matrix envFrom:
spec: - secretRef:
accessModes: [ReadWriteOnce] name: matrix-db-secret
resources: volumeMounts:
requests: - name: matrix-postgres-data
storage: 2Gi mountPath: /var/lib/postgresql/data
--- subPath: pgdata
# Synapse Homeserver Deployment resources:
apiVersion: apps/v1 requests:
kind: Deployment memory: 256Mi
metadata: cpu: 100m
name: synapse limits:
namespace: matrix memory: 1Gi
labels: cpu: 500m
app: synapse livenessProbe:
spec: exec:
replicas: 1 command: ["pg_isready", "-U", "synapse"]
strategy: initialDelaySeconds: 30
type: Recreate periodSeconds: 10
selector: readinessProbe:
matchLabels: exec:
app: synapse command: ["pg_isready", "-U", "synapse"]
template: initialDelaySeconds: 5
metadata: periodSeconds: 5
labels: volumeClaimTemplates:
app: synapse - metadata:
spec: name: matrix-postgres-data
containers: spec:
- name: synapse accessModes: [ReadWriteOnce]
image: matrixdotorg/synapse:latest resources:
ports: requests:
- containerPort: 8008 storage: 5Gi
name: http ---
env: apiVersion: v1
- name: SYNAPSE_SERVER_NAME kind: Service
value: iamworkin.lan metadata:
- name: SYNAPSE_REPORT_STATS name: matrix-postgres
value: "no" namespace: matrix
- name: SYNAPSE_CONFIG_DIR spec:
value: /data selector:
- name: SYNAPSE_DATA_DIR app: matrix-postgres
value: /data ports:
- name: POSTGRES_HOST - port: 5432
value: matrix-postgres targetPort: 5432
- name: POSTGRES_PORT name: postgres
value: "5432" clusterIP: None
- name: POSTGRES_DB ---
valueFrom: # Synapse Data PVC
secretKeyRef: apiVersion: v1
name: matrix-db-secret kind: PersistentVolumeClaim
key: POSTGRES_DB metadata:
- name: POSTGRES_USER name: synapse-data
valueFrom: namespace: matrix
secretKeyRef: spec:
name: matrix-db-secret accessModes: [ReadWriteOnce]
key: POSTGRES_USER resources:
- name: POSTGRES_PASSWORD requests:
valueFrom: storage: 2Gi
secretKeyRef: ---
name: matrix-db-secret # Synapse init job: generate signing key if missing
key: POSTGRES_PASSWORD apiVersion: apps/v1
volumeMounts: kind: Deployment
- name: synapse-data metadata:
mountPath: /data name: synapse
resources: namespace: matrix
requests: labels:
memory: 512Mi app: synapse
cpu: 200m spec:
limits: replicas: 1
memory: 2Gi strategy:
cpu: "1" type: Recreate
livenessProbe: selector:
httpGet: matchLabels:
path: /health app: synapse
port: 8008 template:
initialDelaySeconds: 60 metadata:
periodSeconds: 10 labels:
readinessProbe: app: synapse
httpGet: spec:
path: /health initContainers:
port: 8008 - name: generate-signing-key
initialDelaySeconds: 30 image: matrixdotorg/synapse:latest
periodSeconds: 5 command: ["sh", "-c"]
volumes: args:
- name: synapse-data - |
persistentVolumeClaim: if [ \! -f /data/signing.key ]; then
claimName: synapse-data python -m synapse.app.homeserver --generate-keys --config-path /config/homeserver.yaml
--- fi
apiVersion: v1 volumeMounts:
kind: Service - name: synapse-data
metadata: mountPath: /data
name: synapse - name: synapse-config
namespace: matrix mountPath: /config
spec: containers:
selector: - name: synapse
app: synapse image: matrixdotorg/synapse:latest
ports: ports:
- port: 8008 - containerPort: 8008
targetPort: 8008 name: http
name: http env:
--- - name: SYNAPSE_CONFIG_DIR
# Element Web ConfigMap value: /config
apiVersion: v1 - name: SYNAPSE_CONFIG_PATH
kind: ConfigMap value: /config/homeserver.yaml
metadata: volumeMounts:
name: element-web-config - name: synapse-data
namespace: matrix mountPath: /data
data: - name: synapse-config
config.json: | mountPath: /config
{ resources:
"default_server_config": { requests:
"m.homeserver": { memory: 512Mi
"base_url": "https://matrix.iamworkin.lan", cpu: 200m
"server_name": "iamworkin.lan" limits:
} memory: 2Gi
}, cpu: "1"
"brand": "BlueJay Chat", livenessProbe:
"disable_guests": true, httpGet:
"disable_3pid_login": true path: /health
} port: 8008
--- initialDelaySeconds: 60
# Element Web Deployment periodSeconds: 10
apiVersion: apps/v1 readinessProbe:
kind: Deployment httpGet:
metadata: path: /health
name: element-web port: 8008
namespace: matrix initialDelaySeconds: 30
labels: periodSeconds: 5
app: element-web volumes:
spec: - name: synapse-data
replicas: 1 persistentVolumeClaim:
selector: claimName: synapse-data
matchLabels: - name: synapse-config
app: element-web configMap:
template: name: synapse-config
metadata: ---
labels: apiVersion: v1
app: element-web kind: Service
spec: metadata:
containers: name: synapse
- name: element-web namespace: matrix
image: vectorim/element-web:latest spec:
ports: selector:
- containerPort: 80 app: synapse
name: http ports:
volumeMounts: - port: 8008
- name: element-config targetPort: 8008
mountPath: /app/config.json name: http
subPath: config.json ---
resources: # Element Web ConfigMap
requests: apiVersion: v1
memory: 32Mi kind: ConfigMap
cpu: 10m metadata:
limits: name: element-web-config
memory: 128Mi namespace: matrix
cpu: 100m data:
livenessProbe: config.json: |
httpGet: {
path: / "default_server_config": {
port: 80 "m.homeserver": {
initialDelaySeconds: 10 "base_url": "https://matrix.iamworkin.lan",
periodSeconds: 10 "server_name": "iamworkin.lan"
readinessProbe: }
httpGet: },
path: / "brand": "BlueJay Chat",
port: 80 "disable_guests": true,
initialDelaySeconds: 5 "disable_3pid_login": true
periodSeconds: 5 }
volumes: ---
- name: element-config # Element Web Deployment
configMap: apiVersion: apps/v1
name: element-web-config kind: Deployment
--- metadata:
apiVersion: v1 name: element-web
kind: Service namespace: matrix
metadata: labels:
name: element-web app: element-web
namespace: matrix spec:
spec: replicas: 1
selector: selector:
app: element-web matchLabels:
ports: app: element-web
- port: 80 template:
targetPort: 80 metadata:
name: http labels:
--- app: element-web
# TLS Certificates via cert-manager spec:
apiVersion: cert-manager.io/v1 enableServiceLinks: false
kind: Certificate containers:
metadata: - name: element-web
name: matrix-tls image: vectorim/element-web:latest
namespace: matrix ports:
spec: - containerPort: 80
secretName: matrix-tls name: http
issuerRef: volumeMounts:
name: step-ca-acme - name: element-config
kind: ClusterIssuer mountPath: /app/config.json
dnsNames: subPath: config.json
- matrix.iamworkin.lan resources:
--- requests:
apiVersion: cert-manager.io/v1 memory: 32Mi
kind: Certificate cpu: 10m
metadata: limits:
name: element-tls memory: 128Mi
namespace: matrix cpu: 100m
spec: livenessProbe:
secretName: element-tls httpGet:
issuerRef: path: /
name: step-ca-acme port: 80
kind: ClusterIssuer initialDelaySeconds: 10
dnsNames: periodSeconds: 10
- element.iamworkin.lan readinessProbe:
--- httpGet:
# Traefik IngressRoute - Synapse path: /
apiVersion: traefik.io/v1alpha1 port: 80
kind: IngressRoute initialDelaySeconds: 5
metadata: periodSeconds: 5
name: synapse volumes:
namespace: matrix - name: element-config
spec: configMap:
entryPoints: name: element-web-config
- websecure ---
routes: apiVersion: v1
- match: Host(`matrix.iamworkin.lan`) kind: Service
kind: Rule metadata:
services: name: element-web
- name: synapse namespace: matrix
port: 8008 spec:
tls: selector:
secretName: matrix-tls app: element-web
--- ports:
# Traefik IngressRoute - Element Web - port: 80
apiVersion: traefik.io/v1alpha1 targetPort: 80
kind: IngressRoute name: http
metadata: ---
name: element-web # TLS Certificates via cert-manager
namespace: matrix apiVersion: cert-manager.io/v1
spec: kind: Certificate
entryPoints: metadata:
- websecure name: matrix-tls
routes: namespace: matrix
- match: Host(`element.iamworkin.lan`) spec:
kind: Rule secretName: matrix-tls
services: issuerRef:
- name: element-web name: step-ca-acme
port: 80 kind: ClusterIssuer
tls: dnsNames:
secretName: element-tls - matrix.iamworkin.lan
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: element-tls
namespace: matrix
spec:
secretName: element-tls
issuerRef:
name: step-ca-acme
kind: ClusterIssuer
dnsNames:
- element.iamworkin.lan
---
# Traefik IngressRoute - Synapse
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: synapse
namespace: matrix
spec:
entryPoints:
- websecure
routes:
- match: Host(`matrix.iamworkin.lan`)
kind: Rule
services:
- name: synapse
port: 8008
tls:
secretName: matrix-tls
---
# Traefik IngressRoute - Element Web
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: element-web
namespace: matrix
spec:
entryPoints:
- websecure
routes:
- match: Host(`element.iamworkin.lan`)
kind: Rule
services:
- name: element-web
port: 80
tls:
secretName: element-tls