job "matrix" { datacenters = ["dc1"] region = "global" group "matrix" { network { mode = "bridge" } volume "data" { source = "matrix-synapse-data" type = "csi" access_mode = "single-node-writer" attachment_mode = "file-system" } service { name = "matrix" port = 8008 meta { alloc = "${NOMAD_ALLOC_INDEX}" datacenter = "${NOMAD_DC}" group = "${NOMAD_GROUP_NAME}" job = "${NOMAD_JOB_NAME}" namespace = "${NOMAD_NAMESPACE}" node = "${node.unique.name}" region = "${NOMAD_REGION}" } check { type = "http" path = "/health" expose = true interval = "10s" timeout = "5s" task = "synapse" check_restart { limit = 12 grace = "10m" } } tags = [ "traefik.http.routers.matrix-admin.rule=Host(`matrix.consul`) && (PathPrefix(`/_admin/`) || PathPrefix(`/_synapse/admin`))", "traefik.enable=true", "traefik.http.routers.matrix-admin.entrypoints=https", "traefik.http.middlewares.csp-matrix-admin.headers.contentsecuritypolicy=connect-src 'self' https://scalar.vector.im https://api.maptiler.com;default-src 'self';font-src 'self' data:;frame-src 'self' blob: https://scalar.vector.im/ https://meet.element.io;img-src 'self' data: blob: https://img.youtube.com https://*.ytimg.com;script-src 'self' https://usercontent.riot.im https://scalar.vector.im;style-src 'self' 'unsafe-inline';worker-src 'self' blob:;", "traefik.http.routers.matrix-admin.middlewares=security-headers@file,rate-limit-std@file,forward-proto@file,inflight-std@file,hsts@file,compression@file,csp-matrix-admin", "traefik.http.routers.matrix.rule=Host(`matrix.consul`) || (Host(`matrix.consul`) && PathRegexp(`^/(_(synapse|matrix)|\\.well-known/matrix)/.*`))", "traefik.enable=true", "traefik.http.routers.matrix.entrypoints=https", "traefik.http.middlewares.csp-matrix.headers.contentsecuritypolicy=connect-src 'self' https://scalar.vector.im https://api.maptiler.com;default-src 'self';font-src 'self' data:;frame-src 'self' blob: https://scalar.vector.im/ https://meet.element.io;img-src 'self' data: blob: https://img.youtube.com https://*.ytimg.com;script-src 'self' https://usercontent.riot.im https://scalar.vector.im;style-src 'self' 'unsafe-inline';worker-src 'self' blob:;", "traefik.http.routers.matrix.middlewares=security-headers@file,rate-limit-std@file,forward-proto@file,inflight-std@file,hsts@file,compression@file,csp-matrix", ] connect { sidecar_service { proxy { upstreams { destination_name = "postgres" local_bind_port = 5432 # Work arround, see https://github.com/hashicorp/nomad/issues/18538 destination_type = "service" } } } sidecar_task { config { args = [ "-c", "${NOMAD_SECRETS_DIR}/envoy_bootstrap.json", "-l", "${meta.connect.log_level}", "--concurrency", "${meta.connect.proxy_concurrency}", "--disable-hot-restart" ] } resources { cpu = 50 memory = 64 } } } } # wait for required services tp be ready before starting the main task task "wait-for" { driver = "docker" user = 1053 config { image = "danielberteaud/wait-for:24.3-1" readonly_rootfs = true pids_limit = 20 } lifecycle { hook = "prestart" } env { SERVICE_0 = "postgres.service.consul" } resources { cpu = 10 memory = 10 memory_max = 30 } } task "synapse" { driver = "docker" leader = true config { image = "danielberteaud/matrix-synapse:1.103.0-1" pids_limit = 200 readonly_rootfs = true } vault { policies = ["matrix-synapse"] env = false disable_file = true change_mode = "noop" } env { SYNAPSE_CONFIG = "/secrets/homeserver.yml" TMPDIR = "/data/tmp" } # Use a template block instead of env {} so we can fetch values from vault template { data = <<_EOT LANG=fr_FR.utf8 TZ=Europe/Paris _EOT destination = "secrets/.env" perms = 400 env = true } template { data = <<_EOT alias_creation_rules: - action: allow alias: '*' user_id: '*' allow_guest_access: false allowed_avatar_mimetypes: - image/png - image/jpeg - image/gif database: args: database: matrix-synapse host: 127.0.0.1 password: '{{ with secret "database/creds/matrix-synapse" }}{{ .Data.password }}{{ end }}' port: "5432" user: '{{ with secret "database/creds/matrix-synapse" }}{{ .Data.username }}{{ end }}' name: psycopg2 default_identity_server: https://matrix.org delete_stale_devices_after: 180d email: client_base_url: https://matrix.consul enable_notifs: true notif_for_new_users: true notif_from: '%(app)s ' require_transport_security: false smtp_host: 127.0.0.1 smtp_port: 25 forgotten_room_retention_period: 15d form_secret: '{{- with secret "kv/service/matrix" }}{{ .Data.data.form_secret }}{{ end }}' listeners: - path: /alloc/tmp/synapse.sock resources: - compress: false names: - client - federation type: http log_config: /secrets/logging.conf macaroon_secret_key: '{{- with secret "kv/service/matrix" }}{{ .Data.data.macaroon_secret_key }}{{ end }}' max_avatar_size: 4M media_retention: local_media_lifetime: 730d remote_media_lifetime: 14d media_store_path: /data/media_store password_config: enabled: false public_baseurl: https://matrix.consul push: include_content: event_id_only report_stats: false request_token_inhibit_3pid_errors: true server_name: matrix.consul server_notices: system_mxid_display_name: Notification bot system_mxid_localpart: server signing_key_path: /data/conf/matrix.consul.signing.key sso: client_whitelist: - https://matrix.consul update_profile_information: true suppress_key_server_warning: true trusted_key_servers: - server_name: matrix.org url_preview_enabled: true url_preview_ip_range_blacklist: - 127.0.0.0/8 - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16 - 100.64.0.0/10 - 192.0.0.0/24 - 169.254.0.0/16 - 192.88.99.0/24 - 198.18.0.0/15 - 192.0.2.0/24 - 198.51.100.0/24 - 203.0.113.0/24 - 224.0.0.0/4 url_preview_url_blacklist: - username: '*' - netloc: ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ web_client: false _EOT destination = "secrets/homeserver.yml" uid = 100000 gid = 108008 perms = 0640 } template { data = <<_EOT version: 1 formatters: precise: format: '%(name)s - %(lineno)d - %(levelname)s - %(request)s- %(message)s' filters: context: (): synapse.util.logcontext.LoggingContextFilter request: "" handlers: console: class: logging.StreamHandler formatter: precise filters: [context] loggers: synapse.storage.SQL: # beware: increasing this to DEBUG will make synapse log sensitive # information such as access tokens. level: INFO root: level: DEBUG handlers: [console] _EOT destination = "secrets/logging.conf" } volume_mount { volume = "data" destination = "/data" } resources { cpu = 500 memory = 384 } } task "nginx" { driver = "docker" config { image = "danielberteaud/matrix-element:1.11.63-1" readonly_rootfs = true pids_limit = 100 volumes = [ "local/nginx.conf:/etc/nginx/conf.d/default.conf:ro", ] mount { type = "tmpfs" target = "/tmp" tmpfs_options { size = 5000000 } } } template { data = <<_EOT upstream synapse { server unix:/alloc/tmp/synapse.sock; } map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 127.0.0.1:8008 default_server; server_tokens off; root /opt/element; index index.html; include /etc/nginx/mime.types; default_type application/octet-stream; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_socket_keepalive on; proxy_buffering off; proxy_request_buffering off; client_max_body_size 100m; set_real_ip_from 127.0.0.1; real_ip_header X-Forwarded-For; real_ip_recursive on; location /_matrix { proxy_pass http://synapse; proxy_read_timeout 600; } location /_synapse { proxy_pass http://synapse; proxy_read_timeout 600; } location = /health { access_log off; proxy_pass http://synapse; } location /index.html { add_header Cache-Control "no-cache"; } location /version { add_header Cache-Control "no-cache"; } location /config.json { add_header Cache-Control "no-cache"; alias /local/element.json; } location /.well-known/matrix/server { add_header Access-Control-Allow-Origin *; default_type application/json; expires 6h; return 200 '{"m.server": "matrix.consul:443"}'; } location /.well-known/matrix/client { add_header Access-Control-Allow-Origin *; default_type application/json; expires 6h; return 200 '{"m.homeserver": {"base_url": "https://matrix.consul"}}'; } # Expose synapse admin location /_admin/ { alias /opt/synapse-admin/; expires 30d; } # default files location / { expires 30d; } } _EOT destination = "local/nginx.conf" } template { data = <<_EOT { "default_server_config": { "m.homeserver": { "base_url": "https://matrix.consul", "server_name": "matrix.consul" }, "m.identity_server": { "base_url": "https://vector.im" } }, "brand": "Element", "integrations_ui_url": "https://scalar.vector.im/", "integrations_rest_url": "https://scalar.vector.im/api", "integrations_widgets_urls": [ "https://scalar.vector.im/_matrix/integrations/v1", "https://scalar.vector.im/api", "https://scalar-staging.vector.im/_matrix/integrations/v1", "https://scalar-staging.vector.im/api", "https://scalar-staging.element.im/scalar/api" ], "show_labs_settings": true, "roomDirectory": { "servers": [ "matrix.org" ] }, "disable_guests": true, "settingDefaults": { "UIFeature.feedback": false, "UIFeature.thirdPartyId": false }, "map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx", "happyJson": true } _EOT destination = "local/element.json" } resources { cpu = 20 memory = 16 } } } }