mirror of
https://github.com/spantaleev/matrix-docker-ansible-deploy.git
synced 2026-06-30 15:31:18 +00:00
Add Synology DSM support (#5315)
Adds optional support for running the playbook on Synology DSM 7+, detected automatically via /etc/synoinfo.conf so that non-Synology hosts are unaffected. Includes DSM-native user/group management (synouser/synogroup), a requests version constraint for Docker SDK compatibility, and a boot-fix service that re-shares the volume mount and starts matrix services skipped by DSM's boot ordering. The shared-mount volume path is configurable via matrix_base_synology_volume_path, and the make-shared step only runs when the volume is not already shared. Co-authored-by: CKSit <sitchiuki@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,8 @@ If your server and services experience issues, feel free to come to [our support
|
|||||||
|
|
||||||
- [Alternative architectures](alternative-architectures.md)
|
- [Alternative architectures](alternative-architectures.md)
|
||||||
|
|
||||||
|
- [Configuring Synology DSM](configuring-playbook-synology.md)
|
||||||
|
|
||||||
- [Container images used by the playbook](container-images.md)
|
- [Container images used by the playbook](container-images.md)
|
||||||
|
|
||||||
- [Obtaining an Access Token](obtaining-access-tokens.md)
|
- [Obtaining an Access Token](obtaining-access-tokens.md)
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Configuring Synology DSM
|
||||||
|
|
||||||
|
This document is a guide for preparing Synology DSM for the installation of the [Matrix Docker Ansible Deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy) project.
|
||||||
|
|
||||||
|
> **Note:** Synology DSM is a community-supported platform. It is not officially tested or maintained by the project maintainers. Use at your own discretion.
|
||||||
|
|
||||||
|
**Intended audience:** Users already familiar with DSM, SSH, and this Ansible project.
|
||||||
|
|
||||||
|
## Assumptions
|
||||||
|
|
||||||
|
- DSM version 7 or higher
|
||||||
|
- `Volume1` is used as the default Docker storage location
|
||||||
|
- You are using DSM's built-in reverse proxy for handling HTTPS
|
||||||
|
|
||||||
|
## How Synology Support Works
|
||||||
|
|
||||||
|
The playbook automatically detects Synology DSM by checking for `/etc/synoinfo.conf`. When detected, it:
|
||||||
|
|
||||||
|
- Uses `synouser` and `synogroup` (DSM-native tools) instead of standard Linux user management
|
||||||
|
- Constrains the Python `requests` package to a version compatible with the Docker SDK
|
||||||
|
- Ensures `/volume1` has shared mount propagation so container bind mounts work correctly
|
||||||
|
- Deploys a `matrix-synology-boot-fix` service that runs on every boot after Docker is ready
|
||||||
|
|
||||||
|
You can override auto-detection by setting `matrix_base_host_is_synology: true` or `false` in your `vars.yml`.
|
||||||
|
|
||||||
|
### Matrix Service Account
|
||||||
|
|
||||||
|
The playbook creates a `matrix` system account using Synology's `synouser` tool. The account is secured as follows:
|
||||||
|
|
||||||
|
- **Expired** (`expired=1`) — the account cannot be used to log in to DSM or any application
|
||||||
|
|
||||||
|
You must set a password for this account via `matrix_synology_user_password` in your `vars.yml` (see [vars.yml Configuration](#varsyml-configuration)). The password cannot be used to log in because the account is expired, but a non-empty password is required as an additional security layer.
|
||||||
|
|
||||||
|
> If you pre-create the `matrix` user manually before running the playbook, the playbook will not modify the existing account's settings — you are responsible for securing it.
|
||||||
|
|
||||||
|
### Boot-fix Service
|
||||||
|
|
||||||
|
Synology DSM has two boot-time quirks that the boot-fix service addresses automatically:
|
||||||
|
|
||||||
|
1. **`/volume1` shared mount propagation**
|
||||||
|
|
||||||
|
Docker requires `/volume1` to be mounted as shared (`mount --make-shared /volume1`) for container bind mounts with `bind-propagation=slave` to work correctly (used by matrix-synapse for its media store). On Synology, this cannot be inserted into the systemd chain before Container Manager starts — doing so causes Container Manager to detect a broken dependency and prompt for repair on every boot. The playbook applies this during setup, and the boot-fix service re-applies it on every subsequent reboot, safely outside Container Manager's dependency chain.
|
||||||
|
|
||||||
|
2. **Skipped services at boot**
|
||||||
|
|
||||||
|
Synology's systemd drops services with multi-level dependency chains from the boot activation queue (e.g. `matrix-traefik → matrix-container-socket-proxy → docker`). These services show as `inactive` or `failed` after reboot even though they are enabled. The boot-fix service scans for any enabled `matrix-*.service` in either state and starts them automatically.
|
||||||
|
|
||||||
|
> **If you previously configured a Task Scheduler entry** (`Control Panel > Task Scheduler`) to run `mount --make-shared /volume1` at boot-up, you can remove it — the boot-fix service now handles this.
|
||||||
|
|
||||||
|
## Synology GUI Preparation
|
||||||
|
|
||||||
|
1. **Enable SSH**
|
||||||
|
- `Control Panel` > `Terminal & SNMP` > `Enable SSH service`
|
||||||
|
|
||||||
|
2. **Enable SFTP**
|
||||||
|
- `Control Panel` > `File Service` > `FTP` > `Enable SFTP service` with default port
|
||||||
|
|
||||||
|
3. **Enable User Home Directory**
|
||||||
|
- `Control Panel` > `User & Group` > `Advanced` > `Enable user home service`
|
||||||
|
|
||||||
|
4. **Install Container Manager**
|
||||||
|
- Install from `Package Center`
|
||||||
|
|
||||||
|
5. **Configure Reverse Proxy**
|
||||||
|
- `Control Panel` > `Login Portal` > `Advanced` > `Reverse Proxy`
|
||||||
|
- Create entries for each service you enable (e.g. Matrix, Element, admin page)
|
||||||
|
- Example entry:
|
||||||
|
- Source: `HTTPS` / `matrix.example.com` / port `443`
|
||||||
|
- Destination: `HTTP` / `localhost` / port `81`
|
||||||
|
|
||||||
|
## SSH Preparation
|
||||||
|
|
||||||
|
### (Optional but Recommended) Enable SSH Key Authentication
|
||||||
|
|
||||||
|
Configure key-based SSH login to avoid password prompts during Ansible runs.
|
||||||
|
|
||||||
|
### Set Up the Ansible Environment
|
||||||
|
|
||||||
|
Create a project folder and Python virtual environment on the DSM host:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mkdir ~/path/to/your/project/folder
|
||||||
|
cd ~/path/to/your/project/folder
|
||||||
|
|
||||||
|
python3 -m venv ./myenv
|
||||||
|
# (optional) activate python virtual environment
|
||||||
|
# source ./myenv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inventory Configuration
|
||||||
|
|
||||||
|
In your `inventory/hosts` file, set the Python interpreter to your virtual environment:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# SSH key authentication with empty passphrase example
|
||||||
|
matrix.example.com ansible_host=<your-dsm-ip> ansible_ssh_user=<dsm-ssh-user> become=true become_user=root ansible_python_interpreter=/volume1/homes/path/to/your/project/folder/myenv/bin/python ansible_sudo_pass='your-password'
|
||||||
|
```
|
||||||
|
|
||||||
|
## vars.yml Configuration
|
||||||
|
|
||||||
|
Add the following Synology-specific variables to your `vars.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Synology-specific settings
|
||||||
|
|
||||||
|
# Controls Synology DSM-specific handling. `null` means autodetect (via /etc/synoinfo.conf).
|
||||||
|
# Set to `true`/`false` to force.
|
||||||
|
# matrix_base_host_is_synology: true
|
||||||
|
|
||||||
|
# Password for the Matrix service account created by the playbook.
|
||||||
|
# The account is created as expired so this password cannot be used to log in.
|
||||||
|
matrix_synology_user_password: "your-strong-password"
|
||||||
|
|
||||||
|
# User and group that will be created automatically by the playbook
|
||||||
|
matrix_user_name: "matrix"
|
||||||
|
matrix_group_name: "matrix"
|
||||||
|
|
||||||
|
# Data path on your Synology volume
|
||||||
|
matrix_base_data_path: "/volume1/docker/matrix"
|
||||||
|
|
||||||
|
# Use Synology Container Manager's Docker daemon instead of installing Docker
|
||||||
|
matrix_playbook_docker_installation_enabled: false
|
||||||
|
devture_systemd_docker_base_host_command_docker: "/var/packages/ContainerManager/target/usr/bin/docker"
|
||||||
|
devture_systemd_docker_base_docker_service_name: "pkg-ContainerManager-dockerd.service"
|
||||||
|
|
||||||
|
# Use Synology's NTP service
|
||||||
|
devture_timesync_ntpd_service: "chronyd"
|
||||||
|
|
||||||
|
# Reverse proxy settings — use HTTPS at the DSM reverse proxy level
|
||||||
|
matrix_playbook_ssl_enabled: true
|
||||||
|
traefik_config_entrypoint_web_secure_enabled: false
|
||||||
|
|
||||||
|
# Bind to localhost only — DSM reverse proxy handles public traffic
|
||||||
|
traefik_container_web_host_bind_port: '127.0.0.1:81'
|
||||||
|
matrix_playbook_public_matrix_federation_api_traefik_entrypoint_host_bind_port: '127.0.0.1:8449'
|
||||||
|
|
||||||
|
# Trust X-Forwarded-* headers from the local reverse proxy
|
||||||
|
traefik_config_entrypoint_web_forwardedHeaders_insecure: true
|
||||||
|
|
||||||
|
matrix_playbook_public_matrix_federation_api_traefik_entrypoint_config_custom:
|
||||||
|
forwardedHeaders:
|
||||||
|
insecure: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Playbook
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Full setup
|
||||||
|
ansible-playbook -i inventory/hosts setup.yml --tags=setup-all
|
||||||
|
|
||||||
|
# start
|
||||||
|
ansible-playbook -i inventory/hosts setup.yml --tags=install-all,start
|
||||||
|
|
||||||
|
# Stop all services
|
||||||
|
ansible-playbook -i inventory/hosts setup.yml --tags=stop
|
||||||
|
|
||||||
|
# Apply config changes (always include start to restart running containers)
|
||||||
|
ansible-playbook -i inventory/hosts setup.yml --tags=stop,setup-all,start
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Important:** Always include `stop` before `setup-all,start` when changing configuration. Running `setup-all` alone does not restart already-running containers.
|
||||||
|
|
||||||
|
## Creating Matrix Users
|
||||||
|
|
||||||
|
After the services are running, create your first Matrix user:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# option 1:
|
||||||
|
sudo docker exec -it matrix-synapse register_new_matrix_user http://localhost:8008 -c /data/homeserver.yaml -u your_username -p your_password
|
||||||
|
|
||||||
|
# option 2:
|
||||||
|
ansible-playbook -i inventory/hosts setup.yml --extra-vars='username=your_username password=your_password admin=yes|no' --tags=register-user
|
||||||
|
```
|
||||||
@@ -204,6 +204,26 @@ matrix_group_system: true
|
|||||||
matrix_user_uid: ~
|
matrix_user_uid: ~
|
||||||
matrix_user_gid: ~
|
matrix_user_gid: ~
|
||||||
|
|
||||||
|
# Controls Synology DSM-specific handling. `null` means autodetect (via /etc/synoinfo.conf).
|
||||||
|
# Set to `true`/`false` to force.
|
||||||
|
matrix_base_host_is_synology: ~
|
||||||
|
|
||||||
|
# Password for the Matrix service account on Synology DSM.
|
||||||
|
# Must be set to a non-empty value in your vars.yml when running on Synology.
|
||||||
|
# The account is created as expired so the password cannot be used to log in.
|
||||||
|
matrix_synology_user_password: ""
|
||||||
|
|
||||||
|
# Version constraint for the requests Python package installed on Synology hosts.
|
||||||
|
# requests >= 2.32 dropped the http+docker URL scheme used by the Docker SDK,
|
||||||
|
# causing "Not supported URL scheme http+docker" errors. Installed into the
|
||||||
|
# system Python interpreter (ansible_python_interpreter) on the remote host.
|
||||||
|
matrix_base_synology_requests_version_constraint: "requests<2.32"
|
||||||
|
|
||||||
|
# Synology volume that needs shared mount propagation so that Docker
|
||||||
|
# bind-propagation=slave mounts (used by matrix-synapse for its media store)
|
||||||
|
# work correctly. Defaults to /volume1 (DSM's default Docker storage volume).
|
||||||
|
matrix_base_synology_volume_path: "/volume1"
|
||||||
|
|
||||||
matrix_base_data_path: "/matrix"
|
matrix_base_data_path: "/matrix"
|
||||||
matrix_base_data_path_mode: "750"
|
matrix_base_data_path_mode: "750"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Detect Synology DSM
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /etc/synoinfo.conf
|
||||||
|
register: matrix_base_synoinfo_conf_stat
|
||||||
|
when: matrix_base_host_is_synology is none
|
||||||
|
|
||||||
|
- name: Set matrix_base_host_is_synology from detection
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
matrix_base_host_is_synology: "{{ matrix_base_synoinfo_conf_stat.stat.exists }}"
|
||||||
|
when: matrix_base_host_is_synology is none
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
# SPDX-FileCopyrightText: 2020 Marcel Partap
|
# SPDX-FileCopyrightText: 2020 Marcel Partap
|
||||||
# SPDX-FileCopyrightText: 2022 Marko Weltzer
|
# SPDX-FileCopyrightText: 2022 Marko Weltzer
|
||||||
# SPDX-FileCopyrightText: 2022 Warren Bailey
|
# SPDX-FileCopyrightText: 2022 Warren Bailey
|
||||||
|
# SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
@@ -15,6 +16,11 @@
|
|||||||
block:
|
block:
|
||||||
- ansible.builtin.include_tasks: "{{ role_path }}/tasks/validate_config.yml"
|
- ansible.builtin.include_tasks: "{{ role_path }}/tasks/validate_config.yml"
|
||||||
|
|
||||||
|
- tags:
|
||||||
|
- always
|
||||||
|
block:
|
||||||
|
- ansible.builtin.include_tasks: "{{ role_path }}/tasks/detect_platform.yml"
|
||||||
|
|
||||||
# This needs to always run, because it populates `matrix_user_uid` and `matrix_user_gid`,
|
# This needs to always run, because it populates `matrix_user_uid` and `matrix_user_gid`,
|
||||||
# which are required by many other roles.
|
# which are required by many other roles.
|
||||||
- tags:
|
- tags:
|
||||||
@@ -24,6 +30,13 @@
|
|||||||
block:
|
block:
|
||||||
- ansible.builtin.include_tasks: "{{ role_path }}/tasks/setup_matrix_user.yml"
|
- ansible.builtin.include_tasks: "{{ role_path }}/tasks/setup_matrix_user.yml"
|
||||||
|
|
||||||
|
- tags:
|
||||||
|
- setup-all
|
||||||
|
- install-all
|
||||||
|
block:
|
||||||
|
- ansible.builtin.include_tasks: "{{ role_path }}/tasks/setup_synology_prerequisites.yml"
|
||||||
|
when: matrix_base_host_is_synology
|
||||||
|
|
||||||
- tags:
|
- tags:
|
||||||
- setup-all
|
- setup-all
|
||||||
- install-all
|
- install-all
|
||||||
|
|||||||
@@ -7,11 +7,20 @@
|
|||||||
# SPDX-FileCopyrightText: 2022 Sebastian Gumprich
|
# SPDX-FileCopyrightText: 2022 Sebastian Gumprich
|
||||||
# SPDX-FileCopyrightText: 2024 - 2025 Suguru Hirahara
|
# SPDX-FileCopyrightText: 2024 - 2025 Suguru Hirahara
|
||||||
# SPDX-FileCopyrightText: 2024 László Várady
|
# SPDX-FileCopyrightText: 2024 László Várady
|
||||||
|
# SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# Snapshot ownership before any changes so we can decide whether a recursive
|
||||||
|
# chown is needed (only when uid/gid actually differs from expected).
|
||||||
|
- name: Check current ownership of Matrix base path (Synology)
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ matrix_base_data_path }}"
|
||||||
|
register: matrix_base_data_path_stat
|
||||||
|
when: matrix_base_host_is_synology
|
||||||
|
|
||||||
- name: Ensure Matrix base paths exists
|
- name: Ensure Matrix base paths exists
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ item }}"
|
path: "{{ item }}"
|
||||||
@@ -28,3 +37,18 @@
|
|||||||
src: "{{ role_path }}/templates/bin/remove-all.j2"
|
src: "{{ role_path }}/templates/bin/remove-all.j2"
|
||||||
dest: "{{ matrix_bin_path }}/remove-all"
|
dest: "{{ matrix_bin_path }}/remove-all"
|
||||||
mode: '0750'
|
mode: '0750'
|
||||||
|
|
||||||
|
# On Synology, name-based chown works for directly-touched paths but leaves
|
||||||
|
# existing sub-paths with stale numeric ownership when uid/gid changes between
|
||||||
|
# runs. We recurse only when the pre-task uid/gid didn't match, so normal runs
|
||||||
|
# skip the expensive tree walk entirely. chown -R is used instead of the file
|
||||||
|
# module's recurse option to avoid Ansible iterating every entry in Python.
|
||||||
|
- name: Ensure Matrix base path ownership is correct using numeric UID/GID (Synology)
|
||||||
|
ansible.builtin.command: chown -R {{ matrix_user_uid }}:{{ matrix_user_gid }} {{ matrix_base_data_path }}
|
||||||
|
changed_when: true
|
||||||
|
when: >-
|
||||||
|
matrix_base_host_is_synology and (
|
||||||
|
not matrix_base_data_path_stat.stat.exists or
|
||||||
|
matrix_base_data_path_stat.stat.uid | int != matrix_user_uid | int or
|
||||||
|
matrix_base_data_path_stat.stat.gid | int != matrix_user_gid | int
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,31 +1,13 @@
|
|||||||
# SPDX-FileCopyrightText: 2020 - 2022 Slavi Pantaleev
|
# SPDX-FileCopyrightText: 2020 - 2022 Slavi Pantaleev
|
||||||
# SPDX-FileCopyrightText: 2022 Marko Weltzer
|
# SPDX-FileCopyrightText: 2022 Marko Weltzer
|
||||||
|
# SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
- name: Ensure Matrix group is created
|
- ansible.builtin.include_tasks: "{{ role_path }}/tasks/setup_matrix_user_synology.yml"
|
||||||
ansible.builtin.group:
|
when: matrix_base_host_is_synology
|
||||||
name: "{{ matrix_group_name }}"
|
|
||||||
gid: "{{ omit if matrix_user_gid is none else matrix_user_gid }}"
|
|
||||||
state: present
|
|
||||||
system: "{{ matrix_group_system }}"
|
|
||||||
register: matrix_group
|
|
||||||
|
|
||||||
- name: Ensure Matrix user is created
|
- ansible.builtin.include_tasks: "{{ role_path }}/tasks/setup_matrix_user_linux.yml"
|
||||||
ansible.builtin.user:
|
when: not matrix_base_host_is_synology
|
||||||
name: "{{ matrix_user_name }}"
|
|
||||||
uid: "{{ omit if matrix_user_uid is none else matrix_user_uid }}"
|
|
||||||
state: present
|
|
||||||
group: "{{ matrix_group_name }}"
|
|
||||||
home: "{{ matrix_base_data_path }}"
|
|
||||||
create_home: false
|
|
||||||
system: "{{ matrix_user_system }}"
|
|
||||||
shell: "{{ matrix_user_shell }}"
|
|
||||||
register: matrix_user
|
|
||||||
|
|
||||||
- name: Initialize matrix_user_uid and matrix_user_gid
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
matrix_user_uid: "{{ matrix_user.uid }}"
|
|
||||||
matrix_user_gid: "{{ matrix_group.gid }}"
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2020 - 2022 Slavi Pantaleev
|
||||||
|
# SPDX-FileCopyrightText: 2022 Marko Weltzer
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Ensure Matrix group is created
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: "{{ matrix_group_name }}"
|
||||||
|
gid: "{{ omit if matrix_user_gid is none else matrix_user_gid }}"
|
||||||
|
state: present
|
||||||
|
system: "{{ matrix_group_system }}"
|
||||||
|
register: matrix_group
|
||||||
|
|
||||||
|
- name: Ensure Matrix user is created
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ matrix_user_name }}"
|
||||||
|
uid: "{{ omit if matrix_user_uid is none else matrix_user_uid }}"
|
||||||
|
state: present
|
||||||
|
group: "{{ matrix_group_name }}"
|
||||||
|
home: "{{ matrix_base_data_path }}"
|
||||||
|
create_home: false
|
||||||
|
system: "{{ matrix_user_system }}"
|
||||||
|
shell: "{{ matrix_user_shell }}"
|
||||||
|
register: matrix_user
|
||||||
|
|
||||||
|
- name: Initialize matrix_user_uid and matrix_user_gid
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
matrix_user_uid: "{{ matrix_user.uid }}"
|
||||||
|
matrix_user_gid: "{{ matrix_group.gid }}"
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Fail if matrix_synology_user_password is not set
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
You must set `matrix_synology_user_password` to a non-empty value in your vars.yml.
|
||||||
|
This password secures the Matrix service account on Synology DSM.
|
||||||
|
The account is created as expired so the password cannot be used to log in.
|
||||||
|
when: matrix_synology_user_password == '' or matrix_synology_user_password is none
|
||||||
|
|
||||||
|
- name: Check if Matrix user exists (Synology)
|
||||||
|
ansible.builtin.command: id {{ matrix_user_name }}
|
||||||
|
register: matrix_user_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
# Created with expired=1 (cannot log in)
|
||||||
|
# as this is a service account. If you pre-create the user, you are responsible
|
||||||
|
# for securing it; the playbook will not modify an existing account's settings.
|
||||||
|
- name: Ensure Matrix user is created (Synology)
|
||||||
|
ansible.builtin.command: >
|
||||||
|
/usr/syno/sbin/synouser --add {{ matrix_user_name }}
|
||||||
|
"{{ matrix_synology_user_password }}" "{{ matrix_user_name }}" 1 "" 0
|
||||||
|
when: matrix_user_check.rc != 0
|
||||||
|
changed_when: true
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Ensure Matrix user password is up to date (Synology)
|
||||||
|
ansible.builtin.command: /usr/syno/sbin/synouser --setpw {{ matrix_user_name }} "{{ matrix_synology_user_password }}"
|
||||||
|
when: matrix_user_check.rc == 0
|
||||||
|
changed_when: false
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Check if Matrix group exists (Synology)
|
||||||
|
ansible.builtin.command: /usr/syno/sbin/synogroup --get {{ matrix_group_name }}
|
||||||
|
register: matrix_group_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Ensure Matrix group is created (Synology)
|
||||||
|
ansible.builtin.command: /usr/syno/sbin/synogroup --add {{ matrix_group_name }} {{ matrix_user_name }}
|
||||||
|
when: matrix_group_check.rc != 0
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- name: Get Matrix user UID (Synology)
|
||||||
|
ansible.builtin.command: id -u {{ matrix_user_name }}
|
||||||
|
register: matrix_user_uid_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Get Matrix group info (Synology)
|
||||||
|
ansible.builtin.command: /usr/syno/sbin/synogroup --get {{ matrix_group_name }}
|
||||||
|
register: matrix_synogroup_result
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Initialize matrix_user_uid and matrix_user_gid
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
matrix_user_uid: "{{ matrix_user_uid_result.stdout }}"
|
||||||
|
matrix_user_gid: >-
|
||||||
|
{{
|
||||||
|
matrix_synogroup_result.stdout_lines
|
||||||
|
| select('match', '^Group ID:')
|
||||||
|
| first
|
||||||
|
| regex_search('\[(\d+)\]', '\1')
|
||||||
|
| first
|
||||||
|
}}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Deploy Matrix boot recovery script (Synology)
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ role_path }}/templates/bin/matrix-synology-boot-fix.j2"
|
||||||
|
dest: "{{ matrix_bin_path }}/matrix-synology-boot-fix"
|
||||||
|
mode: "0750"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: Deploy Matrix boot recovery service (Synology)
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "{{ role_path }}/templates/systemd/matrix-synology-boot-fix.service.j2"
|
||||||
|
dest: /etc/systemd/system/matrix-synology-boot-fix.service
|
||||||
|
mode: "0644"
|
||||||
|
register: matrix_synology_boot_fix_service
|
||||||
|
|
||||||
|
- name: Reload systemd and enable Matrix boot recovery service (Synology)
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: matrix-synology-boot-fix.service
|
||||||
|
daemon_reload: true
|
||||||
|
enabled: true
|
||||||
|
when: matrix_synology_boot_fix_service.changed
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- name: Ensure requests Python package is constrained for Docker SDK compatibility (Synology)
|
||||||
|
ansible.builtin.pip:
|
||||||
|
name: "{{ matrix_base_synology_requests_version_constraint }}"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
# Determine whether the volume is already a shared mount, so that the
|
||||||
|
# make-shared command below only runs (and only reports `changed`) when it
|
||||||
|
# actually needs to. We read /proc/self/mountinfo (always present on Linux)
|
||||||
|
# and look for the ` shared:` optional tag on the volume's mount point line.
|
||||||
|
# grep exits non-zero on no-match or any error, so the make-shared command is
|
||||||
|
# skipped only when shared propagation is positively confirmed; every other
|
||||||
|
# case falls through to running it (which is idempotent).
|
||||||
|
- name: Determine current mount propagation of the Synology volume
|
||||||
|
ansible.builtin.command: grep -E ' {{ matrix_base_synology_volume_path }} .* shared:' /proc/self/mountinfo
|
||||||
|
register: matrix_base_synology_volume_propagation
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
# Run immediately during setup so matrix services can start without a manual
|
||||||
|
# step. The boot-fix service handles this on every subsequent reboot.
|
||||||
|
# noqa command-instead-of-module: ansible.builtin.mount does not support
|
||||||
|
# changing mount propagation (--make-shared); command is the only option here.
|
||||||
|
- name: Ensure the Synology volume has shared mount propagation
|
||||||
|
ansible.builtin.command: mount --make-shared {{ matrix_base_synology_volume_path }} # noqa command-instead-of-module
|
||||||
|
when: matrix_base_synology_volume_propagation.rc != 0
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
|
- ansible.builtin.include_tasks: "{{ role_path }}/tasks/setup_synology_boot_fix.yml"
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
#
|
||||||
|
# Boot recovery for Matrix services on Synology DSM.
|
||||||
|
#
|
||||||
|
# This script runs after multi-user.target (outside Container Manager's dependency
|
||||||
|
# chain) and does two things:
|
||||||
|
#
|
||||||
|
# 1. Makes {{ matrix_base_synology_volume_path }} mount-shared so Docker bind-propagation=slave mounts work.
|
||||||
|
# Inserting this into the systemd chain Before=pkg-ContainerManager-dockerd.service
|
||||||
|
# causes Container Manager to detect a broken dependency and prompt for repair,
|
||||||
|
# so it must run here instead, after Docker is already up.
|
||||||
|
#
|
||||||
|
# 2. Starts any enabled matrix-*.service that systemd skipped at boot.
|
||||||
|
# Synology's systemd drops services with multi-level dependency chains
|
||||||
|
# (e.g. traefik -> socket-proxy -> docker) from the boot activation queue.
|
||||||
|
# Services that need bind-propagation=slave (e.g. matrix-synapse) are
|
||||||
|
# created after step 1, so the propagation is already in effect.
|
||||||
|
|
||||||
|
# Wait up to 120s for Docker to be ready
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt 60 ]; do
|
||||||
|
{{ devture_systemd_docker_base_host_command_docker }} info >/dev/null 2>&1 && break
|
||||||
|
i=$((i + 1))
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! {{ devture_systemd_docker_base_host_command_docker }} info >/dev/null 2>&1; then
|
||||||
|
echo "matrix-synology-boot-fix: Docker not ready after 120s, aborting" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make {{ matrix_base_synology_volume_path }} shared so Docker bind-propagation=slave mounts work correctly.
|
||||||
|
# Must run after Docker is up to avoid interfering with Container Manager's
|
||||||
|
# integrity checks, but before matrix-synapse (and any other service using
|
||||||
|
# bind-propagation=slave) creates its containers.
|
||||||
|
/bin/mount --make-shared {{ matrix_base_synology_volume_path }}
|
||||||
|
echo "matrix-synology-boot-fix: {{ matrix_base_synology_volume_path }} set to shared mount propagation"
|
||||||
|
|
||||||
|
# Start any enabled matrix-*.service that is inactive or failed.
|
||||||
|
# Both states indicate the service did not come up at boot — either skipped by
|
||||||
|
# Synology's boot ordering or failed due to Docker/mount-propagation not being
|
||||||
|
# ready yet (the conditions above now satisfy those prerequisites).
|
||||||
|
{{ devture_systemd_docker_base_host_command_systemctl }} list-unit-files 'matrix-*.service' --state=enabled --no-legend 2>/dev/null | \
|
||||||
|
while read -r unit _state; do
|
||||||
|
[ "$unit" = "matrix-synology-boot-fix.service" ] && continue
|
||||||
|
status="$({{ devture_systemd_docker_base_host_command_systemctl }} is-active "$unit" 2>/dev/null)"
|
||||||
|
if [ "$status" = "inactive" ] || [ "$status" = "failed" ]; then
|
||||||
|
echo "matrix-synology-boot-fix: starting $unit (was $status)"
|
||||||
|
{{ devture_systemd_docker_base_host_command_systemctl }} start "$unit"
|
||||||
|
fi
|
||||||
|
done
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2026 Chiu Ki Sit
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Matrix Services Boot Recovery (Synology)
|
||||||
|
# Run after multi-user.target so all matrix services have been attempted first.
|
||||||
|
After=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart={{ matrix_bin_path }}/matrix-synology-boot-fix
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user