vault backup: 2026-01-05 13:03:55
This commit is contained in:
@@ -0,0 +1,424 @@
|
||||
|
||||
|
||||
# Matrix ESS (Community) — Single‑Node Install on Debian 13 (K3s + Traefik + cert‑manager)
|
||||
_Last updated: 2025-09-25 08:45 UTC_
|
||||
|
||||
This guide installs **Element Server Suite (ESS) Community** (Synapse + MAS + Element Web + Matrix RTC) on a **single Debian 13** node using **K3s**, **Traefik** (default in K3s), and **cert‑manager** with **Let’s Encrypt**. It is tailored to your domain choices:
|
||||
|
||||
- **serverName**: `chans.xyz`
|
||||
- **Hosts**: `synapse.chans.xyz`, `account.chans.xyz`, `chat.chans.xyz`, `mrtc.chans.xyz`
|
||||
|
||||
> Tip: if you already have K3s and cert‑manager installed and working, you can jump to **5. Values files** and **6. Install ESS**.
|
||||
|
||||
---
|
||||
|
||||
## 0) Requirements & Ports
|
||||
|
||||
- Debian 13 (root/sudo), public IPv4 (and optional IPv6).
|
||||
- DNS control for `chans.xyz`.
|
||||
- Open/forward these ports to this node:
|
||||
- **80/tcp**, **443/tcp** (ACME + HTTPS + federation)
|
||||
- **30881/tcp**, **30882/udp** (Matrix RTC SFU)
|
||||
- Time in sync (`systemd-timesyncd` or equivalent).
|
||||
|
||||
---
|
||||
|
||||
## 1) DNS Setup
|
||||
|
||||
Create A/AAAA records that point to your node’s public IP(s):
|
||||
|
||||
```
|
||||
chans.xyz A / AAAA -> <YOUR_IP>
|
||||
synapse.chans.xyz A / AAAA -> <YOUR_IP>
|
||||
account.chans.xyz A / AAAA -> <YOUR_IP>
|
||||
chat.chans.xyz A / AAAA -> <YOUR_IP>
|
||||
mrtc.chans.xyz A / AAAA -> <YOUR_IP>
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- **Do not** use a `CNAME` at the **apex** (`chans.xyz`)—use `A/AAAA`. Subdomains can be `CNAME`s if you prefer.
|
||||
- Federation relies on `https://chans.xyz/.well-known/matrix/server` which the chart serves for you.
|
||||
|
||||
---
|
||||
|
||||
## 2) (Optional) Cloud‑Init (without firewalld)
|
||||
|
||||
If you build the node via cloud‑init, this minimal config installs K3s & Helm and disables swap:
|
||||
|
||||
```yaml
|
||||
#cloud-config
|
||||
package_update: true
|
||||
package_upgrade: true
|
||||
packages: [curl, ca-certificates, gnupg, lsb-release]
|
||||
|
||||
runcmd:
|
||||
- swapoff -a
|
||||
- sed -ri 's/^[^#].*\sswap\s/## &/g' /etc/fstab
|
||||
- curl -sfL https://get.k3s.io | sh -s - server
|
||||
- mkdir -p /home/windy/.kube
|
||||
- cp /etc/rancher/k3s/k3s.yaml /home/windy/.kube/config
|
||||
- chown windy:windy /home/windy/.kube/config && chmod 600 /home/windy/.kube/config
|
||||
- bash -lc 'echo export KUBECONFIG=$HOME/.kube/config >> /home/windy/.bashrc'
|
||||
- su - windy -c "curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash"
|
||||
```
|
||||
|
||||
You can manage ports at the cloud firewall or your router (no `firewalld` required).
|
||||
|
||||
---
|
||||
|
||||
## 3) Manual K3s + Helm (if not using cloud‑init)
|
||||
|
||||
```bash
|
||||
# Install latest K3s
|
||||
curl -sfL https://get.k3s.io | sh -s - server
|
||||
|
||||
# kubeconfig for your user (replace 'windy' if needed)
|
||||
mkdir -p ~windy/.kube
|
||||
sudo cp /etc/rancher/k3s/k3s.yaml ~windy/.kube/config
|
||||
sudo chown windy:windy ~windy/.kube/config
|
||||
chmod 600 ~windy/.kube/config
|
||||
echo 'export KUBECONFIG=$HOME/.kube/config' | sudo tee -a ~windy/.bashrc
|
||||
|
||||
# Helm
|
||||
sudo -iu windy bash -lc 'curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash'
|
||||
```
|
||||
|
||||
Verify:
|
||||
```bash
|
||||
kubectl get nodes -o wide
|
||||
kubectl get pods -A
|
||||
```
|
||||
|
||||
You should see the node `Ready` and `traefik` running in `kube-system`.
|
||||
|
||||
---
|
||||
|
||||
## 4) cert‑manager + Let’s Encrypt (ClusterIssuer)
|
||||
|
||||
If you haven’t installed cert‑manager yet:
|
||||
|
||||
```bash
|
||||
helm repo add jetstack https://charts.jetstack.io --force-update
|
||||
kubectl create namespace cert-manager 2>/dev/null || true
|
||||
helm install cert-manager jetstack/cert-manager -n cert-manager --set crds.enabled=true
|
||||
```
|
||||
|
||||
Create a production ClusterIssuer (`letsencrypt-prod`):
|
||||
|
||||
```yaml
|
||||
# clusterissuer.yaml
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: letsencrypt-prod
|
||||
spec:
|
||||
acme:
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-prod-private-key
|
||||
solvers:
|
||||
- http01:
|
||||
ingress:
|
||||
class: traefik
|
||||
```
|
||||
|
||||
Apply:
|
||||
```bash
|
||||
kubectl apply -f clusterissuer.yaml
|
||||
kubectl get clusterissuer
|
||||
```
|
||||
|
||||
You should see `letsencrypt-prod READY=True`.
|
||||
|
||||
---
|
||||
|
||||
## 5) Values files (hosts + TLS)
|
||||
|
||||
Create the directory and values files:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/ess-config-values
|
||||
```
|
||||
|
||||
**`~/ess-config-values/hostnames.yaml`**
|
||||
```yaml
|
||||
serverName: chans.xyz
|
||||
|
||||
elementWeb:
|
||||
ingress:
|
||||
host: chat.chans.xyz
|
||||
|
||||
synapse:
|
||||
ingress:
|
||||
host: synapse.chans.xyz
|
||||
|
||||
matrixAuthenticationService:
|
||||
ingress:
|
||||
host: account.chans.xyz
|
||||
|
||||
matrixRTC:
|
||||
ingress:
|
||||
host: mrtc.chans.xyz
|
||||
```
|
||||
|
||||
**`~/ess-config-values/tls.yaml`**
|
||||
```yaml
|
||||
global:
|
||||
ingress:
|
||||
className: traefik
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
tls:
|
||||
enabled: true
|
||||
issuer: letsencrypt-prod
|
||||
```
|
||||
|
||||
> The TLS values ensure your Ingresses are annotated for cert‑manager and include TLS host entries so Certificates are created automatically.
|
||||
|
||||
---
|
||||
|
||||
## 6) Install ESS (matrix‑stack chart)
|
||||
|
||||
```bash
|
||||
kubectl create namespace ess 2>/dev/null || true
|
||||
|
||||
helm upgrade --install ess oci://ghcr.io/element-hq/ess-helm/matrix-stack -n ess -f ~/ess-config-values/hostnames.yaml -f ~/ess-config-values/tls.yaml --wait
|
||||
```
|
||||
|
||||
Check status:
|
||||
```bash
|
||||
kubectl get pods -n ess
|
||||
kubectl get ingress -n ess
|
||||
```
|
||||
|
||||
You should see ingresses for `synapse`, `account`, `chat`, `mrtc`, and `well-known` with `CLASS=traefik`.
|
||||
|
||||
---
|
||||
|
||||
## 7) Certificates issuance
|
||||
|
||||
Confirm the ingresses have TLS + issuer:
|
||||
```bash
|
||||
kubectl -n ess get ingress -o jsonpath='{range .items[*]}{.metadata.name}{" issuer="}{.metadata.annotations.cert-manager\.io/cluster-issuer}{" tlsHosts="}{range .spec.tls[*].hosts}{.}{" "}{end}{"\n"}{end}'
|
||||
```
|
||||
|
||||
Then watch certs:
|
||||
```bash
|
||||
kubectl get certificate -n ess
|
||||
kubectl get order,challenge -n ess
|
||||
```
|
||||
|
||||
When ready, confirm live certs:
|
||||
```bash
|
||||
for h in synapse.chans.xyz account.chans.xyz chat.chans.xyz mrtc.chans.xyz chans.xyz; do
|
||||
echo "=== $h ==="
|
||||
openssl s_client -connect "$h:443" -servername "$h" </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject -dates
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8) Well‑Known verification (federation & clients)
|
||||
|
||||
```bash
|
||||
curl -s https://chans.xyz/.well-known/matrix/server | jq .
|
||||
curl -s https://chans.xyz/.well-known/matrix/client | jq .
|
||||
```
|
||||
|
||||
Expected:
|
||||
- `server` → `{ "m.server": "synapse.chans.xyz:443" }`
|
||||
- `client` → `{ "m.homeserver": { "base_url": "https://synapse.chans.xyz" }, ... }`
|
||||
|
||||
Optional federation tester: <https://federationtester.matrix.org/#chans.xyz>
|
||||
|
||||
---
|
||||
|
||||
## 9) Create the first admin account
|
||||
|
||||
Interactive:
|
||||
```bash
|
||||
kubectl exec -n ess -it deploy/ess-matrix-authentication-service -- mas-cli manage register-user --admin
|
||||
```
|
||||
|
||||
Non‑interactive example:
|
||||
```bash
|
||||
kubectl exec -n ess deploy/ess-matrix-authentication-service -- mas-cli manage register-user --yes --admin --username admin --password 'CHANGE_ME_strong_password'
|
||||
```
|
||||
|
||||
Login at **https://chat.chans.xyz**.
|
||||
|
||||
---
|
||||
|
||||
## 10) Enable self‑registration (optional)
|
||||
|
||||
```yaml
|
||||
# ~/ess-config-values/mas-registration.yaml
|
||||
matrixAuthenticationService:
|
||||
additional:
|
||||
registration.yaml:
|
||||
config: |
|
||||
account:
|
||||
password_registration_enabled: true
|
||||
password_recovery_enabled: true
|
||||
login_with_email_allowed: true
|
||||
```
|
||||
|
||||
Apply (include this file):
|
||||
```bash
|
||||
helm upgrade --install ess oci://ghcr.io/element-hq/ess-helm/matrix-stack -n ess -f ~/ess-config-values/hostnames.yaml -f ~/ess-config-values/tls.yaml -f ~/ess-config-values/mas-registration.yaml --wait
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11) Outbound email (MAS required, Synapse optional)
|
||||
|
||||
### 11.1 MAS SMTP (required for signup/reset)
|
||||
|
||||
**Option A — inline values (simple):**
|
||||
```yaml
|
||||
# ~/ess-config-values/mas-email.yaml
|
||||
matrixAuthenticationService:
|
||||
additional:
|
||||
user-config.yaml:
|
||||
config: |
|
||||
email:
|
||||
from: '"Matrix @ chans.xyz" <noreply@chans.xyz>'
|
||||
reply_to: '"Support" <support@chans.xyz>'
|
||||
transport: smtp
|
||||
mode: starttls
|
||||
hostname: smtp.windy.me
|
||||
port: 587
|
||||
username: noreply@chans.xyz # authenticate as the sender
|
||||
password: "MAILBOX_PASSWORD"
|
||||
account:
|
||||
password_registration_enabled: true
|
||||
password_recovery_enabled: true
|
||||
login_with_email_allowed: true
|
||||
```
|
||||
|
||||
**Option B — secret ref (keeps password out of Git):**
|
||||
```bash
|
||||
cat > /tmp/mas-user-config.yaml <<'YAML'
|
||||
email:
|
||||
from: '"Matrix @ chans.xyz" <noreply@chans.xyz>'
|
||||
reply_to: '"Support" <support@chans.xyz>'
|
||||
transport: smtp
|
||||
mode: starttls
|
||||
hostname: smtp.windy.me
|
||||
port: 587
|
||||
username: noreply@chans.xyz
|
||||
password: "MAILBOX_PASSWORD"
|
||||
account:
|
||||
password_registration_enabled: true
|
||||
password_recovery_enabled: true
|
||||
login_with_email_allowed: true
|
||||
YAML
|
||||
|
||||
kubectl -n ess create secret generic mas-extra-config --from-file=user-config.yaml=/tmp/mas-user-config.yaml
|
||||
```
|
||||
|
||||
Then reference it:
|
||||
```yaml
|
||||
# ~/ess-config-values/mas-email-secretref.yaml
|
||||
matrixAuthenticationService:
|
||||
additional:
|
||||
user-config.yaml:
|
||||
configSecret: mas-extra-config
|
||||
configSecretKey: user-config.yaml
|
||||
```
|
||||
|
||||
Apply (include one of the two files above):
|
||||
```bash
|
||||
helm upgrade --install ess oci://ghcr.io/element-hq/ess-helm/matrix-stack -n ess -f ~/ess-config-values/hostnames.yaml -f ~/ess-config-values/tls.yaml -f ~/ess-config-values/mas-email.yaml --wait
|
||||
# or replace mas-email.yaml with mas-email-secretref.yaml if you used a Secret
|
||||
```
|
||||
|
||||
> **Mailcow 553 fix**: If authenticating as `zhiqiang@windy.me` and sending as `noreply@chans.xyz`, Mailcow rejects with `553 5.7.1 Sender address rejected`. Either (a) **authenticate as** `noreply@chans.xyz` by creating that mailbox in Mailcow and publishing SPF/DKIM/DMARC for `chans.xyz`; or (b) allow “send as” in Mailcow’s **Sender ACL** for `zhiqiang@windy.me`. Hosting the `chans.xyz` mailbox gives best deliverability (DKIM/DMARC alignment).
|
||||
|
||||
Monitor while testing:
|
||||
```bash
|
||||
kubectl -n ess logs deploy/ess-matrix-authentication-service -f | grep -iE 'smtp|email|send'
|
||||
```
|
||||
|
||||
### 11.2 Synapse email notifications (optional)
|
||||
```yaml
|
||||
# ~/ess-config-values/synapse-email.yaml
|
||||
synapse:
|
||||
additional:
|
||||
email.yaml:
|
||||
config: |
|
||||
email:
|
||||
smtp_host: "smtp.windy.me"
|
||||
smtp_port: 587
|
||||
smtp_user: "noreply@chans.xyz"
|
||||
smtp_pass: "MAILBOX_PASSWORD"
|
||||
require_transport_security: true
|
||||
notif_from: "Matrix on chans.xyz <noreply@chans.xyz>"
|
||||
enable_notifs: true
|
||||
```
|
||||
|
||||
Include this file in your next Helm upgrade.
|
||||
|
||||
---
|
||||
|
||||
## 12) Health checks & troubleshooting
|
||||
|
||||
**Basic:**
|
||||
```bash
|
||||
kubectl get pods,svc,ingress,certificate -n ess -o wide
|
||||
```
|
||||
|
||||
**Certs flow:**
|
||||
```bash
|
||||
kubectl get certificate,order,challenge -n ess
|
||||
kubectl describe challenge -n ess <name>
|
||||
kubectl logs -n kube-system deploy/traefik --tail=200
|
||||
```
|
||||
|
||||
**Well‑known + federation:**
|
||||
```bash
|
||||
curl -s https://chans.xyz/.well-known/matrix/server | jq .
|
||||
curl -s https://chans.xyz/.well-known/matrix/client | jq .
|
||||
```
|
||||
|
||||
**Common pitfalls:**
|
||||
- Ingresses lack TLS + `cert-manager.io/cluster-issuer` → fix `tls.yaml`.
|
||||
- `553 Sender address rejected` from Mailcow → align SMTP auth user with sender or allow “send as”, and set SPF/DKIM/DMARC for `chans.xyz`.
|
||||
- Port 80 blocked → Let’s Encrypt HTTP‑01 fails (check challenges).
|
||||
- Apex `chans.xyz` not pointing at the node → `.well-known` fails → federation fails.
|
||||
|
||||
---
|
||||
|
||||
## 13) Upgrades / Uninstall
|
||||
|
||||
Upgrade to latest chart:
|
||||
```bash
|
||||
helm repo update # if using repos
|
||||
helm upgrade --install ess oci://ghcr.io/element-hq/ess-helm/matrix-stack -n ess -f ~/ess-config-values/hostnames.yaml -f ~/ess-config-values/tls.yaml --wait
|
||||
```
|
||||
|
||||
Uninstall ESS (keeps PVCs unless you delete them):
|
||||
```bash
|
||||
helm uninstall ess -n ess
|
||||
kubectl delete namespace ess
|
||||
```
|
||||
|
||||
Reset K3s (if ever needed):
|
||||
```bash
|
||||
sudo /usr/local/bin/k3s-uninstall.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14) Quick copy‑paste checklist
|
||||
|
||||
1. DNS A/AAAA for: `chans.xyz`, `synapse.`, `account.`, `chat.`, `mrtc.` → your IP.
|
||||
2. K3s running with Traefik; cert‑manager installed; `ClusterIssuer letsencrypt-prod` **Ready**.
|
||||
3. `hostnames.yaml` with `*.ingress.host` set to your subdomains.
|
||||
4. `tls.yaml` with `global.ingress.annotations.cert-manager.io/cluster-issuer=letsencrypt-prod` and TLS enabled.
|
||||
5. `helm upgrade --install ess …` with both files.
|
||||
6. `kubectl get certificate -n ess` → `READY=True`.
|
||||
7. `/.well-known` returns correct JSON; federation tester OK.
|
||||
8. Create admin via MAS CLI; log in at `https://chat.chans.xyz`.
|
||||
9. Configure SMTP for MAS (and optionally Synapse), fix Mailcow sender policy if needed.
|
||||
Reference in New Issue
Block a user