├── template ├── synapse │ ├── synapse.signing.key │ ├── synapse.log.config │ └── homeserver.yaml ├── postgresql │ └── env ├── containers │ ├── matrix.network │ ├── synapse.container │ ├── element-web.container │ ├── postgresql.container │ └── caddy.container ├── element-web │ ├── nginx.conf │ └── config.json └── caddy │ └── Caddyfile ├── .gitignore ├── secrets.example ├── config.bu ├── Makefile ├── LICENSE └── README.md /template/synapse/synapse.signing.key: -------------------------------------------------------------------------------- 1 | %%SYNAPSE_SIGNING_KEY%% 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /secrets 2 | 3 | /config.tmp 4 | /config*.ign 5 | 6 | /generated 7 | -------------------------------------------------------------------------------- /template/postgresql/env: -------------------------------------------------------------------------------- 1 | POSTGRES_PASSWORD=%%POSTGRES_PASSWORD%% 2 | POSTGRES_USER=synapse 3 | POSTGRES_DB=synapse 4 | POSTGRES_INITDB_ARGS=--encoding='UTF8' --lc-collate='C' --lc-ctype='C' 5 | -------------------------------------------------------------------------------- /template/containers/matrix.network: -------------------------------------------------------------------------------- 1 | [Network] 2 | DisableDNS=false 3 | Internal=false 4 | 5 | # Manual subnet to avoid issues with DNS resolution 6 | Subnet=10.89.1.0/24 7 | Gateway=10.89.1.1 8 | -------------------------------------------------------------------------------- /secrets.example: -------------------------------------------------------------------------------- 1 | SSH_PUBKEY="ssh-rsa AAAA..." 2 | POSTGRES_PASSWORD=a_passpharse_for_my_database 3 | DOMAIN_NAME=my.matrix.domain 4 | EMAIL=root@example.com 5 | SYNAPSE_REGISTRATION_SHARED_SECRET=a_very_long_string_generated_by_synapse 6 | SYNAPSE_MACAROON_SECRET_KEY=a_very_long_string_generated_by_synapse 7 | SYNAPSE_FORM_SECRET=a_very_long_string_generated_by_synapse 8 | SYNAPSE_SIGNING_KEY=a_key_generated_by_synapse 9 | SYNAPSE_REGISTRATION=false 10 | -------------------------------------------------------------------------------- /template/containers/synapse.container: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Synapse (Matrix homeserver) 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Container] 7 | ContainerName=synapse 8 | NoNewPrivileges=true 9 | Image=docker.io/matrixdotorg/synapse:latest 10 | ReadOnly=true 11 | Volume=/var/srv/matrix/synapse:/data:z 12 | AutoUpdate=registry 13 | Network=matrix.network 14 | IP=10.89.1.20 15 | 16 | [Service] 17 | Restart=on-failure 18 | TimeoutStartSec=900 19 | 20 | [Install] 21 | WantedBy=default.target 22 | -------------------------------------------------------------------------------- /template/synapse/synapse.log.config: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | formatters: 4 | precise: 5 | format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' 6 | 7 | handlers: 8 | console: 9 | class: logging.StreamHandler 10 | formatter: precise 11 | 12 | loggers: 13 | synapse.storage.SQL: 14 | # beware: increasing this to DEBUG will make synapse log sensitive 15 | # information such as access tokens. 16 | level: INFO 17 | 18 | root: 19 | level: INFO 20 | handlers: [console] 21 | 22 | disable_existing_loggers: false -------------------------------------------------------------------------------- /template/containers/element-web.container: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Element (Matrix web client) 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Container] 7 | ContainerName=element-web 8 | NoNewPrivileges=true 9 | Image=docker.io/vectorim/element-web:latest 10 | Volume=/var/srv/matrix/element-web/nginx.conf:/etc/nginx/nginx.conf:ro,z 11 | Volume=/var/srv/matrix/element-web/config.json:/app/config.json:ro,z 12 | AutoUpdate=registry 13 | Network=matrix.network 14 | IP=10.89.1.40 15 | 16 | [Service] 17 | Restart=on-failure 18 | TimeoutStartSec=900 19 | 20 | [Install] 21 | WantedBy=default.target 22 | -------------------------------------------------------------------------------- /template/containers/postgresql.container: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PostgreSQL 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Container] 7 | ContainerName=postgresql 8 | NoNewPrivileges=true 9 | Image=docker.io/library/postgres:15 10 | ReadOnly=true 11 | Tmpfs=/run 12 | Volume=/var/srv/matrix/postgresql/data:/var/lib/postgresql/data:z 13 | EnvironmentFile=/var/srv/matrix/postgresql/env 14 | Exec=-c listen_addresses='*' 15 | AutoUpdate=registry 16 | Network=matrix.network 17 | IP=10.89.1.30 18 | 19 | [Service] 20 | Restart=on-failure 21 | TimeoutStartSec=900 22 | 23 | [Install] 24 | WantedBy=default.target 25 | -------------------------------------------------------------------------------- /template/element-web/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | 3 | worker_processes auto; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | include /etc/nginx/mime.types; 11 | default_type application/octet-stream; 12 | 13 | server_tokens off; 14 | sendfile on; 15 | tcp_nopush on; 16 | reset_timedout_connection on; 17 | charset utf-8; 18 | types_hash_max_size 4096; 19 | 20 | server { 21 | listen 8080 default_server; 22 | listen [::]:8080 default_server; 23 | 24 | location / { 25 | root /usr/share/nginx/html; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /template/containers/caddy.container: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Caddy web server 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Container] 7 | ContainerName=caddy 8 | NoNewPrivileges=true 9 | Image=docker.io/library/caddy:2 10 | PublishPort=80:80/tcp 11 | PublishPort=443:443/tcp 12 | PublishPort=443:443/udp 13 | PublishPort=8448:8448/tcp 14 | ReadOnly=true 15 | Volume=/var/srv/matrix/caddy/config:/config:z 16 | Volume=/var/srv/matrix/caddy/data:/data:z 17 | Volume=/var/srv/matrix/caddy/Caddyfile:/etc/caddy/Caddyfile:ro,z 18 | AutoUpdate=registry 19 | Network=matrix.network 20 | IP=10.89.1.10 21 | 22 | [Service] 23 | Restart=on-failure 24 | TimeoutStartSec=900 25 | 26 | [Install] 27 | WantedBy=default.target 28 | -------------------------------------------------------------------------------- /config.bu: -------------------------------------------------------------------------------- 1 | variant: fcos 2 | version: 1.5.0 3 | passwd: 4 | users: 5 | - name: core 6 | ssh_authorized_keys: 7 | - %%SSH_PUBKEY%% 8 | storage: 9 | directories: 10 | - path: /var/srv/matrix 11 | mode: 0700 12 | - path: /var/srv/matrix/synapse/media_store 13 | mode: 0777 14 | - path: /var/srv/matrix/caddy/config 15 | - path: /var/srv/matrix/caddy/data 16 | - path: /var/srv/matrix/postgresql/data 17 | trees: 18 | - local: containers 19 | path: /etc/containers/systemd 20 | - local: caddy 21 | path: /var/srv/matrix/caddy 22 | - local: synapse 23 | path: /var/srv/matrix/synapse 24 | - local: element-web 25 | path: /var/srv/matrix/element-web 26 | - local: postgresql 27 | path: /var/srv/matrix/postgresql 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | include secrets 4 | 5 | all: 6 | rm -rf ./config.tmp 7 | cp -a template config.tmp 8 | find config.tmp/ -type f -print0 | xargs -0 sed -i 's/%%DOMAIN_NAME%%/${DOMAIN_NAME}/g' 9 | find config.tmp/ -type f -print0 | xargs -0 sed -i 's/%%EMAIL%%/${EMAIL}/' 10 | find config.tmp/ -type f -print0 | xargs -0 sed -i 's/%%POSTGRES_PASSWORD%%/${POSTGRES_PASSWORD}/' 11 | find config.tmp/ -type f -print0 | xargs -0 sed -i 's/%%SYNAPSE_REGISTRATION_SHARED_SECRET%%/${SYNAPSE_REGISTRATION_SHARED_SECRET}/' 12 | find config.tmp/ -type f -print0 | xargs -0 sed -i 's/%%SYNAPSE_MACAROON_SECRET_KEY%%/${SYNAPSE_MACAROON_SECRET_KEY}/' 13 | find config.tmp/ -type f -print0 | xargs -0 sed -i 's/%%SYNAPSE_FORM_SECRET%%/${SYNAPSE_FORM_SECRET}/' 14 | find config.tmp/ -type f -print0 | xargs -0 sed -i 's/%%SYNAPSE_REGISTRATION%%/${SYNAPSE_REGISTRATION}/' 15 | echo ${SYNAPSE_SIGNING_KEY} > config.tmp/synapse/synapse.signing.key 16 | sed 's|%%SSH_PUBKEY%%|${SSH_PUBKEY}|' config.bu | butane --files-dir config.tmp --strict --output config.ign 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice (including the next 11 | paragraph) shall be included in all copies or substantial portions of the 12 | Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 17 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /template/element-web/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "default_server_config": { 3 | "m.homeserver": { 4 | "base_url": "https://matrix.%%DOMAIN_NAME%%", 5 | "server_name": "%%DOMAIN_NAME%%" 6 | }, 7 | "m.identity_server": { 8 | "base_url": "https://vector.im" 9 | } 10 | }, 11 | "disable_guests": true, 12 | "brand": "Element", 13 | "integrations_ui_url": "https://scalar.vector.im/", 14 | "integrations_rest_url": "https://scalar.vector.im/api", 15 | "integrations_widgets_urls": [ 16 | "https://scalar.vector.im/_matrix/integrations/v1", 17 | "https://scalar.vector.im/api", 18 | "https://scalar-staging.vector.im/_matrix/integrations/v1", 19 | "https://scalar-staging.vector.im/api", 20 | "https://scalar-staging.riot.im/scalar/api" 21 | ], 22 | "showLabsSettings": true, 23 | "default_federate": true, 24 | "default_theme": "light", 25 | "roomDirectory": { 26 | "servers": [ 27 | "matrix.org", 28 | "gitter.im" 29 | ] 30 | }, 31 | "enable_presence_by_hs_url": { 32 | "https://matrix.org": false, 33 | "https://matrix-client.matrix.org": false 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /template/caddy/Caddyfile: -------------------------------------------------------------------------------- 1 | { 2 | # Only renew certificates once a week 3 | renew_interval 7d 4 | # Disable admin interface 5 | admin off 6 | # Only use the explicitly provided config 7 | persist_config off 8 | # Set email for Let's Encrypt 9 | email %%EMAIL%% 10 | # Use the staging endpoint to get certificates by default 11 | # Remove once ready to go to production 12 | acme_ca "https://acme-staging-v02.api.letsencrypt.org/directory" 13 | # Only use certificates from Let's Encrypt by default 14 | cert_issuer acme 15 | } 16 | 17 | %%DOMAIN_NAME%% { 18 | respond /.well-known/matrix/server <