Skip to content

Helm chart for deploying a Matrix stack, including Synapse, Postgresql, Sliding Sync, MAS, Element, coturn, and bridges.

License

Notifications You must be signed in to change notification settings

small-hack/matrix-chart

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Matrix Chart

A Helm chart for deploying a Matrix homeserver stack on Kubernetes.

TLDR

See the README.md for docs auto-generated from the values.yaml.

Read through the parameters and modify them locally before installing the chart:

# add the helm repo locally
helm repo add matrix https://small-hack.github.io/matrix-chart

# downloads the values.yaml locally
helm show values matrix/matrix > values.yaml

# You should then edit the values.yaml to your liking.

## NOTE: The most important helm parameter is matrix.hostname
## without it, this chart may not work!

# install the chart
helm install my-release-name matrix/matrix --values values.yaml

NOTE: The most important helm parameter is matrix.hostname. Without it, this chart may not work!

Current Features ✨

  • Latest version of Synapse (the official matrix homeserver)
  • Ingress definitions for federated Synapse (Matrix homeserver) and Element (client for matrix)

Optional Features

⚠️ Untested Features

These features still need to be tested, but are technically baked into the chart from the fork:

Notes

Databases

You must select one of the following options:

  • Use the Bitnami PostgreSQL subchart (set postgresql.enabled to true)
  • Use your own external database, which can also be PostgreSQL. (set externalDatabase.enabled to true)

Note

You cannot enable both externalDatabase and postgresql. You must select one.

Ingress

A previous version of this chart supported using the synapse.ingress.host parameter. This option has been removed. You must now set a synapse.ingress.hosts. Because of this, you must now also set matrix.hostname or certain functionality will not work. Example of how to setup ingress and hostname:

matrix:
  # used for setting up config files that require your homeserver hostname
  # such as bridging between your matrix homeserver (synapse) and other services
  # such as discord or WhatsApp
  hostname: my-synapse-hostname.com

synapse:
  ingress:
    className: "nginx"
    annotations:
      # required for TLS certs issued by cert-manager
      cert-manager.io/cluster-issuer: letsencrypt-staging

      # -- This annotation is required for the Nginx ingress provider. You can
      # remove it if you use a different ingress provider
      nginx.ingress.kubernetes.io/configuration-snippet: |
        proxy_intercept_errors off;

    hosts:
      - host: "my-synapse-hostname.com"
        paths:
          - path: /
            pathType: ImplementationSpecific
            # if mas.enabled is set to true, you want pathType for / to be Prefix
            # pathType: Prefix

          # if mas.enabled is set to true, you want to uncomment the following:
          # - path: "/_matrix/client/(r0|v3)/(refresh|login|logout).*"
          #   pathType: ImplementationSpecific
          #   backend:
          #     service:
          #       value: release-name-mas
          #       port:
          #         name: http
    # -- enable tls for synapse ingress
    tls:
      - secretName: "matrix-tls"
        hosts:
          - my-synapse-hostname

Federation

Federation not Working

This can be broken for a number of reasons, and some of them are listed in the official synapse docs, but one that was persistent for the devs here was constantly getting a 401 when testing.

I managed to finally get past that by adding the following to my values.yaml:

matrix:
  hostname: my-synapse-hostname.com
  federation:
    enabled: true

synapse:
  ingress:
    # replace matrix.mydomain.com with your actual matrix domain
    nginx.ingress.kubernetes.io/configuration-snippet: |
      location /.well-known/matrix/server {
        return 200 '{"m.server": "matrix.mydomain.com:443"}';
        add_header Content-Type application/json;
      }

Note

By the way, you can test by going to https://federationtester.matrix.org/api/report?server_name=matrix.mydomain.com where matrix.mydomain.com is replaced by your synapse server.

Later on, I realized I could also use serve_server_wellknown in the synapse config, so I've added it to the Chart's parameters and you can use it like this in your values.yaml:

matrix:
  hostname: my-synapse-hostname.com
  federation:
    enabled: true
  serve_server_wellknown: true

Addiing Trusted Key Servers from an existing Secret

If you'd like to get your trusted_key_servers from an existing Kubernetes Secret, you can do so with an in-line yaml block. Here's an example values.yaml:

matrix:
  hostname: my-synapse-hostname.com
  federation:
    enabled: true
  security:
    trustedKeyServersExistingSecret: "trusted-key-servers"
    trustedKeyServersSecretKey: "trustedKeyServers"

Here's an example Kubernetes Secret using in-line YAML (NOTE the trusted_key_servers):

apiVersion: v1
kind: Secret
metadata:
  name: trusted-key-servers
  namespace: matrix
type: Opaque
stringData:
  # friend.com is the matrix server you'd like to federate with :)
  trustedKeyServers: |-
    trusted_key_servers:
      - server_name: friend.com
        verify_keys:
          ed25519:auto: abcdefghijklmnopqrstuvwxyz1234567890

Notes on using Matrix Sliding Sync

To use sliding sync, which is required for element-x, you'll need to ensure that requests to .well-known/matrix/client return the correct json. To do that, you'll want update your matrix.extra_well_known_client_content values and set syncv3.enabled to true. Example below:

matrix:
  hostname: my-synapse-hostname.com
  extra_well_known_client_content:
     "org.matrix.msc3575.proxy":
       "url": "https://your-sliding-sync-hostname.com"


syncv3:
  # this enables this subchart: https://github.com/small-hack/matrix-sliding-sync-chart
  # which deploys this: https://github.com/matrix-org/sliding-sync
  enabled: true
  server: "https://my-synapse-hostname.com"
  secret: "this.is.a.test.secret"
  bindaddr: "127.0.0.1:8008"
  # note: you'll still have to actually fill out parameters
  # under slidingSync.postgresql, but it is truncated here for brevity
  # check out values.yaml for all possible slidingSync.postgresql values
  postgresql:
    enabled: true

After synapse is up, you should be able to verify it's returning correctly by doing:

$ curl https://matrix.example.com/.well-known/matrix/client | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   136  100   136    0     0   1818      0 --:--:-- --:--:-- --:--:--  1837
{
  "m.homeserver": {
    "base_url": "https://matrix.example.com"
  },
  "org.matrix.msc3575.proxy": {
    "url": "https://matrix.example.com"
  }
}

Notes on using MAS (Matrix Authentication Service)

MAS is currently the only way to use OIDC with element-x. If you're using MAS (Matrix Authentication Service), you'll need to set mas.enabled to true. You'll also need to setup proper routes for synapse to redirect to MAS. See example below:

matrix:
  hostname: my-synapse-hostname.com
  experimental_features:
    msc3861:
      # Likely needed if using OIDC on synapse and you want to allow usage of Element-X (the beta of element)
      enabled: false
      # -- Synapse will call `{issuer}/.well-known/openid-configuration` to get the OIDC configuration
      issuer: http://my-mas-domain.com/
      # -- Matches the `mas.mas.client_id` in the auth service config
      client_id: 0000000000000000000SYNAPSE
      # -- Matches the `mas.mas.client_auth_method` in the auth service config
      client_auth_method: client_secret_basic
      # -- Matches the `mas.mas.clients.client_secret` in the auth service config
      client_secret: "SomeRandomSecret"
      # -- Matches the `mas.mas.matrix.secret` in the auth service config
      admin_token: "special-secret-for-msc3861"
      # -- URL to advertise to clients where users can self-manage their account
      account_management_url: "https://my-mas-domain.com/account"

synapse:
  enabled: true
  ingress:
    enabled: true
    className: "nginx"
    annotations:
      # you need for the routing to work properly
      nginx.ingress.kubernetes.io/use-regex: "true"
      # -- This annotation is required for the Nginx ingress provider. You can
      # remove it if you use a different ingress provider
      nginx.ingress.kubernetes.io/configuration-snippet: |
        proxy_intercept_errors off;
      # -- required for TLS certs issued by cert-manager
      cert-manager.io/cluster-issuer: letsencrypt-staging
    hosts:
      - host: 'my-synapse-hostname.com'
        paths:
          - path: "/_matrix/client/(r0|v3)/(refresh|login|logout).*"
            pathType: ImplementationSpecific
            backend:
              service:
                # this assumes you passed in mas.fullnameOverride="mas"
                name: mas
                port:
                  name: http

          - path: /
            pathType: Prefix
    tls:
      - secretName: matrix-tls
        hosts:
          - 'my-synapse-hostname.com'

mas:
  enabled: true
  # sets all MAS resources to be called mas
  fullnameOverride: "mas"
  postgresql:
    enabled: true

  ingress:
    enabled: true
    className: "nginx"
    annotations:
      cert-manager.io/cluster-issuer: 'letsencrypt-prod'
    hosts:
      - host: 'my-mas-domain.com'
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: matrix-authentication-service-tls
        hosts:
          - 'my-mas-domain.com'

  # templates out the Matrix Authentication Service config file
  mas:
    database:
      # if blank, this can be autogenerated from mas.postgres or mas.externalDatabase
      # settings, or you set this to a valid postgres URI
      # https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING-URIS
      uri: ""

    http:
      # -- Public URL base used when building absolute public URLs
      public_base: "https://my-mas-domain.com/"
      # List of HTTP listeners, see below
      listeners:
        # The name of the listener, used in logs and metrics
        - name: web
          # List of resources to serve
          resources:
            - name: discovery
            - name: human
            - name: oauth
            - name: compat
            - name: graphql
            - name: assets
          binds:
            - host: 0.0.0.0
              port: 8080

    policy:
      client_registration:
        # don't require URIs to be on the same host. default: false
        allow_host_mismatch: true
        # allow non-SSL and localhost URIs. default: false
        allow_insecure_uris: true

    # this is mostly ignored in favor of the above masClientSecret variable
    clients:
      - client_id: "0000000000000000000SYNAPSE"
        client_auth_method: client_secret_basic
        client_secret: "SomeRandomSecret"

    matrix:
      homeserver: "my-synapse-hostname.com"
      endpoint: "https://my-synapse-hostname.com"
      secret: "special-secret-for-msc3861"

    upstream_oauth2:
      existingSecret: "synapse-oidc"
      secretKeys:
        # -- key in secret with the issuer
        issuer: "issuer"
        # -- key in secret with the client_id
        client_id: "client_id"
        # -- key in secret with the client_secret
        client_secret: "client_secret"

      # this below example is compatible with zitadel
      providers:
        # -- A unique identifier (ULID) for the provider: https://www.ulidtools.com
        # in the valid redirect uris, you want to use this id
        - id: "01HYZ2G7QS9P2BHSDS94F3GR80"
          issuer: https://example-zitadel-domain.com/
          client_id: "idgenreatedbyyourupstreamoidcprovider"
          client_secret: "secretgenreatedbyyourupstreamoidcprovider"

          token_endpoint_auth_method: client_secret_basic
          claims_imports:
            subject:
              template: "{{ user.sub }}"

            localpart:
              action: require
              template: "{{ user.preferred_username }}"

            displayname:
              action: suggest
              template: "{{ user.name }}"

            email:
              action: suggest
              template: "{{ user.email }}"
              set_email_verification: always

Bridges

We've only recently started adding/testing bridges to this stack, so there may be some bugs, but so far, we've got the discord bridge upgraded. The rest of the bridges are in a beta/alpha state and although we want to support them, we haven't had the time to test them out since the major fork. If you find something wrong with them, please feel free to submit an Issue or Pull Request.

So far we've tested and gotten working two bots/bridges: Alertmanager and Discord. We wanted to get hookshot working, but try as we might, we could never get the bot to respond to queries in a matrix chat.

Alertmanager

Check out the upstream repo for more info (especially .env.default), but here's the gist for configuring it via this chart.

bridges:
  alertmanager:
    enabled: false

    existingSecret:
      # -- optional secret to replace the entire registration.yaml
      registration: ""

    # this section is for registering the application service with matrix
    # read more about application services here:
    # https://spec.matrix.org/v1.11/application-service-api/
    registration:
      # -- url of the alertmanager service. if not provided, we will template it
      # for you like http://matrix-alertmanager-service:3000
      url: ""
      # A secret token that the application service will use to authenticate
      # requests to the homeserver.
      as_token: ""
      # -- Use an existing Kubernetes Secret to store your own generated appservice
      # and homeserver tokens. If this is not set, we'll generate them for you.
      # Setting this won't override the ENTIRE registration.yaml we generate for
      # the synapse pod to authenticate mautrix/discord. It will only replaces the tokens.
      # To replaces the ENTIRE registration.yaml, use
      # bridges.alertmanager.existingSecret.registration
      existingSecret: ""
      existingSecretKeys:
        # -- key in existingSecret for as_token (application service token). If
        # provided and existingSecret is set, ignores bridges.alertmanager.registration.as_token
        as_token: "as_token"
        # -- key in existingSecret for hs_token (home server token)
        hs_token: "hs_token"

    encryption: false

    config:
      # -- secret key for the alertmanager webhook config URL
      app_alertmanager_secret: ""
      # -- your homeserver url, e.g. https://homeserver.tld
      homeserver_url: ""

      bot:
        # -- optional: display name to set for the bot user
        display_name: ""
        # -- optional: mxc:// avatar to set for the bot user
        avatar_url: ""
        # -- rooms to send alerts to, separated by a |
        # Each entry contains the receiver name (from alertmanager) and the
        # internal id (not the public alias) of the Matrix channel to forward to.
        # example: reciever1/!789fhdsauoh48:mymatrix.hostname.com
        rooms: ""
        # -- Set this to true to make firing alerts do a `@room` mention.
        # NOTE! Bot should also have enough power in the room for this to be useful.
        mention_room: false

      # -- set to enable Grafana links, e.g. https://grafana.example.com
      grafana_url: ""
      # -- grafana data source, e.g. default
      grafana_datasource: ""
      # -- set to enable silence link, e.g. https://alertmanager.example.com
      alertmanager_url: ""

Discord

We previously had the halfshot/discord bridge as a part of this stack, but as of July 2024 the image was no longer being updated and hadn't been updated in 3 years, see: #589 for more info. Instead we now offer the mautrix/discord bridge. You can read their docs here.

Here's how we got it mostly working on our end via the values.yaml:

matrix:
  hostname: my-synapse-hostname.com

bridges:
  discord_mautrix:
    enabled: true
    # this just keeps the replicasets from getting
    # out of control, feel free to set to 10 to
    # keep more history for rollbacks
    revisionHistoryLimit: 1

    # -- extra volumes for the mautrix/discord deployment
    # we created this separately from the chart
    extraVolumes:
      - name: sqllite
        persistentVolumeClaim:
          claimName: mautrix-discord-bridge-sqlite

    extraVolumeMounts:
      - name: sqllite
        mountPath: /sql

    admin_users:
      - friend
      - admin

    config:
      # Homeserver details
      homeserver:
        address: "https://my-synapse-hostname.com"
        domain: "my-synapse-hostname.com"

      appservice:
        # Database config - we used sqllite because it's easy
        database:
          type: sqlite3-fk-wal
          uri: file:/sql/mautrixdiscord.db?_txlock=immediate

      bridge:
        encryption:
          # -- Allow encryption, work in group chat rooms with e2ee enabled
          allow: true
          # -- Default to encryption, force-enable encryption in all portals the bridge creates
          # This will cause the bridge bot to be in private chats for the encryption to work properly.
          default: true

Example PVC for the sqllite file to persist:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mautrix-discord-bridge-sqlite
  namespace: matrix
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi
  storageClassName: local-path

After you set this up, you'll still need to authenticate the matrix bot (mautrix/discord) with your Discord bot. For that, you'll need to follow the instructions in the mautrix discord docs.

About and Status

This is a fork of Arkaniad/matrix-chart, which is a fork of typokign/matrix-chart. We recently transferred this chart from @jessebot to the small-hack org to help with maintanence longterm :) Working on full stability, but always happy to receive GitHub Issues or PRs! Please star the repo if you like our work 💙

Our goal is to provide regular updates using renovatebot and provide some level of basic security from a k8s perspective. We're also trying to standardize the chart more by following predictable values.yaml patterns.