3
0
mirror of https://github.com/spantaleev/matrix-docker-ansible-deploy.git synced 2026-02-28 18:03:10 +00:00

Merge Synapse reverse-proxy companion role into matrix-synapse

The companion role was tightly coupled to Synapse through shared tags, worker routing, and lifecycle ordering. Keeping them separate added coordination overhead without practical benefits, especially for parallelized execution.

This merges the role into matrix-synapse while keeping companion logic organized under dedicated reverse_proxy_companion task/template subdirectories.

Compatibility is preserved:
- matrix_synapse_reverse_proxy_companion_* variable names remain unchanged
- install/setup companion-specific tags remain available

Cross-role/global wiring is now in group_vars (matrix-synapse section), while role defaults provide sensible standalone defaults and self-wiring for Synapse-owned values.
This commit is contained in:
Slavi Pantaleev
2026-02-26 06:51:47 +02:00
parent 63b6bf4bc1
commit 28afbde971
23 changed files with 436 additions and 460 deletions

View File

@@ -0,0 +1,354 @@
#jinja2: lstrip_blocks: True
{% set room_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'room_worker') | list %}
{% set sync_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'sync_worker') | list %}
{% set client_reader_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'client_reader') | list %}
{% set federation_reader_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'federation_reader') | list %}
{% set generic_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'generic_worker') | list %}
{% set stream_writer_typing_stream_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'stream_writer') | selectattr('stream_writer_stream', 'equalto', 'typing') | list %}
{% set stream_writer_to_device_stream_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'stream_writer') | selectattr('stream_writer_stream', 'equalto', 'to_device') | list %}
{% set stream_writer_account_data_stream_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'stream_writer') | selectattr('stream_writer_stream', 'equalto', 'account_data') | list %}
{% set stream_writer_receipts_stream_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'stream_writer') | selectattr('stream_writer_stream', 'equalto', 'receipts') | list %}
{% set stream_writer_presence_stream_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'stream_writer') | selectattr('stream_writer_stream', 'equalto', 'presence') | list %}
{% set media_repository_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'media_repository') | list %}
{% set user_dir_workers = matrix_synapse_reverse_proxy_companion_synapse_workers_list | selectattr('type', 'equalto', 'user_dir') | list %}
{% macro render_worker_upstream(name, workers, load_balance) %}
upstream {{ name }} {
{#
We need to use a zone so that the upstream is stored in shared memory,
otherwise we can't use `resolve` below, as reported by nginx:
> resolving names at run time requires upstream ".." in ... to be in shared memory
#}
zone {{ name }} 64k;
{{ load_balance }}
keepalive {{ ((workers | length) * 2) | string }};
resolver {{ matrix_synapse_reverse_proxy_companion_http_level_resolver }} valid=5s;
{% for worker in workers %}
server "{{ worker.name }}:{{ worker.port }}" resolve;
{% endfor %}
}
{% endmacro %}
{% macro render_locations_to_upstream(locations, upstream_name) %}
{% for location in locations %}
location ~ {{ location }} {
proxy_pass http://{{ upstream_name }}$request_uri;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
{% endfor %}
{% endmacro %}
{% macro render_locations_to_upstream_with_whoami_sync_worker_router(locations, upstream_name) %}
{% for location in locations %}
location ~ {{ location }} {
# Use auth_request to call the whoami sync worker router.
# The handler resolves the access token to a user identifier and returns it
# in the X-User-Identifier header, which is then used for upstream hashing.
auth_request /_whoami_sync_worker_router;
auth_request_set $user_identifier $sent_http_x_user_identifier;
{% if matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_debug_headers_enabled %}
add_header X-Sync-Worker-Router-User-Identifier $user_identifier always;
add_header X-Sync-Worker-Router-Upstream $upstream_addr always;
{% endif %}
proxy_pass http://{{ upstream_name }}$request_uri;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
{% endfor %}
{% endmacro %}
{% if matrix_synapse_reverse_proxy_companion_synapse_workers_enabled %}
# Whether to upgrade HTTP connection
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
#Extract room name from URI
map $request_uri $room_name {
~^/_matrix/(client|federation)/.*?(?:%21|!)(?<room>[A-Za-z0-9._=\-\/]+)(?::|%3A)[A-Za-z0-9._=\-\/]+ $room;
}
# End maps
{% if matrix_synapse_reverse_proxy_companion_synapse_cache_enabled %}
proxy_cache_path {{ matrix_synapse_reverse_proxy_companion_synapse_cache_path }} levels=1:2 keys_zone={{ matrix_synapse_reverse_proxy_companion_synapse_cache_keys_zone_name }}:{{ matrix_synapse_reverse_proxy_companion_synapse_cache_keys_zone_size }} inactive={{ matrix_synapse_reverse_proxy_companion_synapse_cache_inactive_time }} max_size={{ matrix_synapse_reverse_proxy_companion_synapse_cache_max_size_mb }}m;
{% endif %}
# Round Robin "upstream" pools for workers
{% if room_workers | length > 0 %}
{{- render_worker_upstream('room_workers_upstream', room_workers, 'hash $room_name consistent;') }}
{% endif %}
{% if sync_workers | length > 0 %}
{{- render_worker_upstream('sync_workers_upstream', sync_workers, 'hash $user_identifier consistent;') }}
{% endif %}
{% if client_reader_workers | length > 0 %}
{{- render_worker_upstream('client_reader_workers_upstream', client_reader_workers, 'least_conn;') }}
{% endif %}
{% if federation_reader_workers | length > 0 %}
{{- render_worker_upstream('federation_reader_workers_upstream', federation_reader_workers, 'hash $http_x_forwarded_for;') }}
{% endif %}
{% if generic_workers | length > 0 %}
{{- render_worker_upstream('generic_workers_upstream', generic_workers, 'hash $http_x_forwarded_for;') }}
{% endif %}
{% if stream_writer_typing_stream_workers | length > 0 %}
{{- render_worker_upstream('stream_writer_typing_stream_workers_upstream', stream_writer_typing_stream_workers, '') }}
{% endif %}
{% if stream_writer_to_device_stream_workers | length > 0 %}
{{- render_worker_upstream('stream_writer_to_device_stream_workers_upstream', stream_writer_to_device_stream_workers, '') }}
{% endif %}
{% if stream_writer_account_data_stream_workers | length > 0 %}
{{- render_worker_upstream('stream_writer_account_data_stream_workers_upstream', stream_writer_account_data_stream_workers, '') }}
{% endif %}
{% if stream_writer_receipts_stream_workers | length > 0 %}
{{- render_worker_upstream('stream_writer_receipts_stream_workers_upstream', stream_writer_receipts_stream_workers, '') }}
{% endif %}
{% if stream_writer_presence_stream_workers | length > 0 %}
{{- render_worker_upstream('stream_writer_presence_stream_workers_upstream', stream_writer_presence_stream_workers, '') }}
{% endif %}
{% if media_repository_workers | length > 0 %}
{{- render_worker_upstream('media_repository_workers_upstream', media_repository_workers, 'least_conn;') }}
{% endif %}
{% if user_dir_workers | length > 0 %}
{{- render_worker_upstream('user_dir_workers_upstream', user_dir_workers, '') }}
{% endif %}
{% endif %}
server {
listen 8008;
server_name {{ matrix_synapse_reverse_proxy_companion_hostname }};
server_tokens off;
root /dev/null;
client_max_body_size {{ matrix_synapse_reverse_proxy_companion_client_api_client_max_body_size_mb }}M;
client_body_buffer_size {{ matrix_synapse_reverse_proxy_companion_client_api_client_body_buffer_size_mb }}M;
proxy_buffering on;
proxy_max_temp_file_size 0;
proxy_set_header Host $host;
{% if matrix_synapse_reverse_proxy_companion_whoami_sync_worker_router_enabled %}
# Internal location for whoami-based sync worker routing.
# This is called via auth_request from sync worker locations.
# The njs handler calls the whoami endpoint to resolve access tokens to usernames,
# then returns the username in the X-User-Identifier header for upstream hashing.
location = /_whoami_sync_worker_router {
internal;
js_content whoami_sync_worker_router.handleAuthRequest;
}
{% endif %}
{% if matrix_synapse_reverse_proxy_companion_synapse_workers_enabled %}
# Client-server overrides — These locations must go to the main Synapse process
location ~ {{ matrix_synapse_reverse_proxy_companion_client_server_main_override_locations_regex }} {
{# FIXME: This block was copied from the main Synapse fallback below. It would be better to have it in one place and avoid duplication. #}
{# Use the embedded DNS resolver in Docker containers to discover the service #}
resolver {{ matrix_synapse_reverse_proxy_companion_http_level_resolver }} valid=5s;
set $backend "{{ matrix_synapse_reverse_proxy_companion_client_api_addr }}";
proxy_pass http://$backend;
}
# Client-server SSO overrides — These locations must go to the main Synapse process
location ~ {{ matrix_synapse_reverse_proxy_companion_client_server_sso_override_locations_regex }} {
{# FIXME: This block was copied from the main Synapse fallback below. It would be better to have it in one place and avoid duplication. #}
{# Use the embedded DNS resolver in Docker containers to discover the service #}
resolver {{ matrix_synapse_reverse_proxy_companion_http_level_resolver }} valid=5s;
set $backend "{{ matrix_synapse_reverse_proxy_companion_client_api_addr }}";
proxy_pass http://$backend;
}
# QR code login (`rendezvous`) locations need to go to the same Synapse process.
# It doesn't necessarily need to be the main process, but it needs to be consistent.
# For simplicity, we'll send them to the main process though.
location ~ {{ matrix_synapse_reverse_proxy_companion_client_server_qr_code_login_locations_regex }} {
{# FIXME: This block was copied from the main Synapse fallback below. It would be better to have it in one place and avoid duplication. #}
{# Use the embedded DNS resolver in Docker containers to discover the service #}
resolver {{ matrix_synapse_reverse_proxy_companion_http_level_resolver }} valid=5s;
set $backend "{{ matrix_synapse_reverse_proxy_companion_client_api_addr }}";
proxy_pass http://$backend;
}
{# Workers redirects BEGIN #}
{% if generic_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#synapseappgeneric_worker
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_generic_worker_client_server_locations, 'generic_workers_upstream') }}
{% endif %}
{% if stream_writer_typing_stream_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#the-typing-stream
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_stream_writer_typing_stream_worker_client_server_locations, 'stream_writer_typing_stream_workers_upstream') }}
{% endif %}
{% if stream_writer_to_device_stream_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#the-to_device-stream
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_stream_writer_to_device_stream_worker_client_server_locations, 'stream_writer_to_device_stream_workers_upstream') }}
{% endif %}
{% if stream_writer_account_data_stream_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#the-account_data-stream
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_stream_writer_account_data_stream_worker_client_server_locations, 'stream_writer_account_data_stream_workers_upstream') }}
{% endif %}
{% if stream_writer_receipts_stream_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#the-receipts-stream
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_stream_writer_receipts_stream_worker_client_server_locations, 'stream_writer_receipts_stream_workers_upstream') }}
{% endif %}
{% if stream_writer_presence_stream_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#the-presence-stream
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_stream_writer_presence_stream_worker_client_server_locations, 'stream_writer_presence_stream_workers_upstream') }}
{% endif %}
{% if room_workers | length > 0 %}
# room workers
# https://tcpipuk.github.io/synapse/deployment/workers.html
# https://tcpipuk.github.io/synapse/deployment/nginx.html#locationsconf
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_room_worker_client_server_locations, 'room_workers_upstream') }}
{% endif %}
{% if sync_workers | length > 0 %}
# sync workers
# https://tcpipuk.github.io/synapse/deployment/workers.html
# https://tcpipuk.github.io/synapse/deployment/nginx.html#locationsconf
{{ render_locations_to_upstream_with_whoami_sync_worker_router(matrix_synapse_reverse_proxy_companion_synapse_sync_worker_client_server_locations, 'sync_workers_upstream') }}
{% endif %}
{% if client_reader_workers | length > 0 %}
# client_reader workers
# https://tcpipuk.github.io/synapse/deployment/workers.html
# https://tcpipuk.github.io/synapse/deployment/nginx.html#locationsconf
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_client_reader_client_server_locations, 'client_reader_workers_upstream') }}
{% endif %}
{% if media_repository_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#synapseappmedia_repository
{% for location in matrix_synapse_reverse_proxy_companion_synapse_media_repository_locations %}
location ~ {{ location }} {
proxy_pass http://media_repository_workers_upstream$request_uri;
{% if matrix_synapse_reverse_proxy_companion_synapse_cache_enabled %}
proxy_cache {{ matrix_synapse_reverse_proxy_companion_synapse_cache_keys_zone_name }};
proxy_cache_valid any {{ matrix_synapse_reverse_proxy_companion_synapse_cache_proxy_cache_valid_time }};
proxy_force_ranges on;
add_header X-Cache-Status $upstream_cache_status;
{% endif %}
}
{% endfor %}
{% endif %}
{% if user_dir_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#updating-the-user-directory
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_user_dir_locations, 'user_dir_workers_upstream') }}
{% endif %}
{# Workers redirects END #}
{% endif %}
{% for configuration_block in matrix_synapse_reverse_proxy_companion_synapse_client_api_additional_server_configuration_blocks %}
{{- configuration_block }}
{% endfor %}
{# Everything else just goes to the API server ##}
location / {
{# Use the embedded DNS resolver in Docker containers to discover the service #}
resolver {{ matrix_synapse_reverse_proxy_companion_http_level_resolver }} valid=5s;
set $backend "{{ matrix_synapse_reverse_proxy_companion_client_api_addr }}";
proxy_pass http://$backend;
}
}
{% if matrix_synapse_reverse_proxy_companion_federation_api_enabled %}
server {
listen 8048;
server_name {{ matrix_synapse_reverse_proxy_companion_hostname }};
server_tokens off;
root /dev/null;
client_max_body_size {{ matrix_synapse_reverse_proxy_companion_federation_api_client_max_body_size_mb }}M;
client_body_buffer_size {{ matrix_synapse_reverse_proxy_companion_federation_api_client_body_buffer_size_mb }}M;
proxy_buffering on;
proxy_max_temp_file_size 0;
proxy_set_header Host $host;
{% if matrix_synapse_reverse_proxy_companion_synapse_workers_enabled %}
# Federation overrides — These locations must go to the main Synapse process
location ~ {{ matrix_synapse_reverse_proxy_companion_federation_override_locations_regex }} {
{# FIXME: This block was copied from the fallback location below. It would be better to have it in one place and avoid duplication. #}
{# Use the embedded DNS resolver in Docker containers to discover the service #}
resolver {{ matrix_synapse_reverse_proxy_companion_http_level_resolver }} valid=5s;
set $backend "{{ matrix_synapse_reverse_proxy_companion_federation_api_addr }}";
proxy_pass http://$backend;
}
{% if room_workers | length > 0 %}
# https://tcpipuk.github.io/synapse/deployment/workers.html
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_room_worker_federation_locations, 'room_workers_upstream') }}
{% endif %}
{% if generic_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#synapseappgeneric_worker
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_generic_worker_federation_locations, 'generic_workers_upstream') }}
{% endif %}
{% if media_repository_workers | length > 0 %}
# https://matrix-org.github.io/synapse/latest/workers.html#synapseappmedia_repository
{% for location in matrix_synapse_reverse_proxy_companion_synapse_media_repository_locations %}
location ~ {{ location }} {
proxy_pass http://media_repository_workers_upstream$request_uri;
{% if matrix_synapse_reverse_proxy_companion_synapse_cache_enabled %}
proxy_buffering on;
proxy_cache {{ matrix_synapse_reverse_proxy_companion_synapse_cache_keys_zone_name }};
proxy_cache_valid any {{ matrix_synapse_reverse_proxy_companion_synapse_cache_proxy_cache_valid_time }};
proxy_force_ranges on;
add_header X-Cache-Status $upstream_cache_status;
{% endif %}
}
{% endfor %}
{% endif %}
{#
This is last, because we'd like more-specific requests (e.g. `/_matrix/federation/v1/media/` that may be handled by a media repository worker, if enabled)
to be routed to more specialized workers via their respective `locations` defined earlier (above).
As https://nginx.org/en/docs/http/ngx_http_core_module.html#location says about location matching:
> .. Then regular expressions are checked, in the order of their appearance in the configuration file.
See: https://github.com/spantaleev/matrix-docker-ansible-deploy/issues/3918
#}
{% if federation_reader_workers | length > 0 %}
# https://tcpipuk.github.io/synapse/deployment/workers.html
{{ render_locations_to_upstream(matrix_synapse_reverse_proxy_companion_synapse_federation_reader_federation_locations, 'federation_reader_workers_upstream') }}
{% endif %}
{% endif %}
{% for configuration_block in matrix_synapse_reverse_proxy_companion_synapse_federation_api_additional_server_configuration_blocks %}
{{- configuration_block }}
{% endfor %}
location / {
{# Use the embedded DNS resolver in Docker containers to discover the service #}
resolver {{ matrix_synapse_reverse_proxy_companion_http_level_resolver }} valid=5s;
set $backend "{{ matrix_synapse_reverse_proxy_companion_federation_api_addr }}";
proxy_pass http://$backend;
}
}
{% endif %}

View File

@@ -0,0 +1,5 @@
SPDX-FileCopyrightText: 2022 - 2025 Slavi Pantaleev
SPDX-FileCopyrightText: 2024 Charles Wright
SPDX-FileCopyrightText: 2024 Suguru Hirahara
SPDX-License-Identifier: AGPL-3.0-or-later

View File

@@ -0,0 +1,13 @@
#jinja2: lstrip_blocks: True
# The default is aligned to the CPU's cache size,
# which can sometimes be too low.
# Thus, we ensure a larger bucket size value is used.
server_names_hash_bucket_size 64;
{% if matrix_synapse_reverse_proxy_companion_http_level_resolver %}
resolver {{ matrix_synapse_reverse_proxy_companion_http_level_resolver }};
{% endif %}
{% for configuration_block in matrix_synapse_reverse_proxy_companion_http_additional_server_configuration_blocks %}
{{- configuration_block }}
{% endfor %}

View File

@@ -0,0 +1,3 @@
SPDX-FileCopyrightText: 2022 Slavi Pantaleev
SPDX-License-Identifier: AGPL-3.0-or-later