From 476660fd65a494d984381b80fdd4d37b0cd748ed Mon Sep 17 00:00:00 2001 From: Saibotk Date: Sat, 18 Jan 2025 17:10:42 +0100 Subject: [PATCH] refactor!(mastodon): Migrate to podman quadlet --- playbooks/mastodon.yml | 36 +- roles/mastodon/README.md | 37 -- roles/mastodon/defaults/main.yml | 108 ++-- roles/mastodon/handlers/main.yml | 92 +++- roles/mastodon/meta/main.yml | 44 +- roles/mastodon/tasks/main.yml | 397 +++++++++----- roles/mastodon/templates/.env.production | 266 --------- .../{default.conf => default.conf.j2} | 28 +- roles/mastodon/templates/docker-compose.yml | 179 ------ .../templates/mastodon-backend.network.j2 | 8 + .../mastodon-elasticsearch.container.j2 | 43 ++ .../templates/mastodon-elasticsearch.env.j2 | 13 + .../templates/mastodon-frontend.network.j2 | 6 + .../templates/mastodon-nginx.container.j2 | 45 ++ .../templates/mastodon-postgres-socket.volume | 5 + .../templates/mastodon-postgres.container.j2 | 48 ++ .../templates/mastodon-postgres.env.j2 | 3 + .../templates/mastodon-redis-socket.volume | 5 + .../templates/mastodon-redis.container.j2 | 46 ++ .../templates/mastodon-sidekiq.container.j2 | 45 ++ .../templates/mastodon-streaming.container.j2 | 44 ++ .../templates/mastodon-web.container.j2 | 45 ++ roles/mastodon/templates/mastodon.caddy.j2 | 31 ++ roles/mastodon/templates/mastodon.env.j2 | 113 ++++ roles/mastodon/templates/redis.conf.j2 | 513 ++++++++++++++++++ roles/mastodon/vars/main.yml | 5 + 26 files changed, 1473 insertions(+), 732 deletions(-) delete mode 100644 roles/mastodon/README.md delete mode 100644 roles/mastodon/templates/.env.production rename roles/mastodon/templates/{default.conf => default.conf.j2} (79%) delete mode 100644 roles/mastodon/templates/docker-compose.yml create mode 100644 roles/mastodon/templates/mastodon-backend.network.j2 create mode 100644 roles/mastodon/templates/mastodon-elasticsearch.container.j2 create mode 100644 roles/mastodon/templates/mastodon-elasticsearch.env.j2 create mode 100644 roles/mastodon/templates/mastodon-frontend.network.j2 create mode 100644 roles/mastodon/templates/mastodon-nginx.container.j2 create mode 100644 roles/mastodon/templates/mastodon-postgres-socket.volume create mode 100644 roles/mastodon/templates/mastodon-postgres.container.j2 create mode 100644 roles/mastodon/templates/mastodon-postgres.env.j2 create mode 100644 roles/mastodon/templates/mastodon-redis-socket.volume create mode 100644 roles/mastodon/templates/mastodon-redis.container.j2 create mode 100644 roles/mastodon/templates/mastodon-sidekiq.container.j2 create mode 100644 roles/mastodon/templates/mastodon-streaming.container.j2 create mode 100644 roles/mastodon/templates/mastodon-web.container.j2 create mode 100644 roles/mastodon/templates/mastodon.caddy.j2 create mode 100644 roles/mastodon/templates/mastodon.env.j2 create mode 100644 roles/mastodon/templates/redis.conf.j2 create mode 100644 roles/mastodon/vars/main.yml diff --git a/playbooks/mastodon.yml b/playbooks/mastodon.yml index b83b935..34fb948 100644 --- a/playbooks/mastodon.yml +++ b/playbooks/mastodon.yml @@ -1,25 +1,17 @@ ---- -# Infrastructure -# Ansible instructions to deploy the infrastructure -# Copyright (C) 2019-2020 Christoph (Sheogorath) Kern -# Copyright (C) 2020 Saibotk -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +- name: Install Mastodon. -- name: Install & configure Mastodon hosts: mastodon + roles: - - docker - - docker_cleanup - - traefik - - mastodon + - role: podman + become: true + tags: + - always + - podman + - role: caddy + become: true + tags: + - always + - caddy + - role: mastodon + become: true diff --git a/roles/mastodon/README.md b/roles/mastodon/README.md deleted file mode 100644 index e9562d3..0000000 --- a/roles/mastodon/README.md +++ /dev/null @@ -1,37 +0,0 @@ -Mastodon -========= - -This will setup a Mastodon instance using their official docker container and traefik as a reverse proxy. - -**Note: This role will also care about migrations etc, when upgrading to a new version!** - -Requirements ------------- - -You will need to have docker, docker-compose and traefik installed or declared as dependencies with their respective roles. - -**This role assumes that you have setup traefik with an endpoint called `websecure`.** - -Role Variables --------------- - -**Please look at the [defaults/main.yml](defaults/main.yml) for all available variables and their description.** - -**Note: Lines that are commented out via `#` are usually still valid/used variables, but they are not defined by default, so they might enable a feature, when uncommenting/defining them!** - -### Global variables, that are used: - -- `proxy_network`: Defined by the local traefik installation, this is the shared proxy network used by traefik to reach the containers. (optional) -- `proxy_hiddenservice`: Defined by the local traefik installation, this is used to generate the alt-svc header for the alternative Tor domain. (optional) - -Dependencies ------------- - -- docker -- docker-compose -- traefik - -License -------- - -GPL-3.0-only diff --git a/roles/mastodon/defaults/main.yml b/roles/mastodon/defaults/main.yml index 842fc72..fbdb22b 100644 --- a/roles/mastodon/defaults/main.yml +++ b/roles/mastodon/defaults/main.yml @@ -1,39 +1,10 @@ ---- -# Default variables for the mastodon role - -# Infrastructure -# Ansible instructions to deploy the infrastructure -# Copyright (C) 2019-2020 Christoph (Sheogorath) Kern -# Copyright (C) 2020 Saibotk -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# The install location mastodon_install_location: /srv/mastodon -# The container data volume mount locations -mastodon_database_location: "{{ mastodon_install_location }}/postgres" -mastodon_public_location: "{{ mastodon_install_location }}/public" -mastodon_redis_location: "{{ mastodon_install_location }}/redis" -mastodon_elastic_location: "{{ mastodon_install_location }}/elasticsearch" -mastodon_nginx_location: "{{ mastodon_install_location }}/nginx" -# The certresolver that is used by traefik for this domain -mastodon_traefik_certresolver: letsencrypt_http - -# The domain that traefik will server mastodon under mastodon_domain: mastodon.example.com +mastodon_postgres_password: "{{ lookup('passwordstore', 'mastodon.example.com/database create=true') }}" + # The mastodon configuration (see mastodon documentation for a reference / the `templates/.env.production` file) mastodon_config: local_domain: "{{ mastodon_domain }}" @@ -48,23 +19,66 @@ mastodon_config: smtp_login: undef smtp_password: undef smtp_from_address: mastodon@example.com - enable_elasticsearch: false -# Should we automatically adjust the needed sysctl setting for vm.max_map_count? -mastodon_elasticsearch_adjust_sysctl: true -# Container versions +mastodon_streaming_containerimage: ghcr.io/mastodon/mastodon-streaming +mastodon_containerimage: ghcr.io/mastodon/mastodon # renovate: depName=ghcr.io/mastodon/mastodon -mastodon_version: 4.3.2 -# renovate: depName=docker.io/library/postgres -mastodon_database_version: 15.8 -# renovate: depName=docker.io/library/redis -mastodon_redis_version: 7.4 -# renovate: depName=docker.elastic.co/elasticsearch/elasticsearch -mastodon_elasticsearch_version: 7.17.25 +mastodon_image_tag: "v4.3.3" -# Container tag definitions -mastodon_image_version: "v{{ mastodon_version }}" -mastodon_database_image_version: "{{ mastodon_database_version }}-alpine" -mastodon_redis_image_version: "{{ mastodon_redis_version }}-alpine" -mastodon_elasticsearch_image_version: "{{ mastodon_elasticsearch_version }}" +mastodon_nginx_containerimage: docker.io/nginxinc/nginx-unprivileged +# renovate: depName=docker.io/nginxinc/nginx-unprivileged +mastodon_nginx_image_tag: "1.27.3-alpine" + +mastodon_postgres_containerimage: docker.io/library/postgres +# renovate: depName=docker.io/library/postgres +mastodon_postgres_image_tag: "17.2-alpine" + +mastodon_redis_containerimage: docker.io/library/redis +# renovate: depName=docker.io/library/redis +mastodon_redis_image_tag: "7.4-alpine" + +mastodon_elasticsearch_containerimage: docker.io/library/elasticsearch +# renovate: depName=docker.io/library/elasticsearch +mastodon_elasticsearch_image_tag: "7.17.26" + + +mastodon_selinux_level: "{{ omit }}" +mastodon_nginx_selinux_level: "{{ omit }}" +mastodon_postgres_selinux_level: "{{ omit }}" +mastodon_redis_selinux_level: "{{ omit }}" +mastodon_elasticsearch_selinux_level: "{{ omit }}" +## This will be used for the `mastodon_public_location` which will need to +## be accessed by the main mastodon containers and by nginx. +## Make sure it is accessible/compatible with `mastodon_selinux_level` +## and `mastodon_nginx_selinux_level`. +mastodon_public_selinux_level: "{{ omit }}" + + +mastodon_memory_low: 512m +mastodon_memory_high: 0 +mastodon_swap_max: -1 + +mastodon_sidekiq_memory_low: 512m +mastodon_sidekiq_memory_high: 0 +mastodon_sidekiq_swap_max: -1 + +mastodon_streaming_memory_low: 64m +mastodon_streaming_memory_high: 256m +mastodon_streaming_swap_max: -1 + +mastodon_nginx_memory_low: 32m +mastodon_nginx_memory_high: 64m +mastodon_nginx_swap_max: -1 + +mastodon_redis_memory_low: 128m +mastodon_redis_memory_high: 512m +mastodon_redis_swap_max: -1 + +mastodon_elasticsearch_low: 1024m +mastodon_elasticsearch_memory_high: 1536m +mastodon_elasticsearch_swap_max: -1 + +mastodon_postgres_memory_low: 512m +mastodon_postgres_memory_high: 0 +mastodon_postgres_swap_max: -1 diff --git a/roles/mastodon/handlers/main.yml b/roles/mastodon/handlers/main.yml index 101e012..ccc3830 100644 --- a/roles/mastodon/handlers/main.yml +++ b/roles/mastodon/handlers/main.yml @@ -1,14 +1,84 @@ ---- -# Handlers file for the mastodon role - -- name: Pull mastodon image - community.docker.docker_image: - name: "docker.io/tootsuite/mastodon:{{ mastodon_image_version }}" - source: pull +- name: Apply new SELinux file context to filesystem. + ansible.builtin.command: "restorecon -irF {{ mastodon_install_location }}" become: true + listen: "mastodon selinux context changed" + changed_when: false -- name: Stop mastodon for upgrade - community.docker.docker_compose_v2: - state: stopped - project_src: "{{ mastodon_install_location }}" +- name: Restart mastodon web service. + ansible.builtin.systemd: + state: restarted + name: mastodon-web.service + daemon_reload: true become: true + listen: + - "mastodon web service changed" + - "mastodon selinux context changed" + - "mastodon postgres socket changed" + - "mastodon redis socket changed" + - "mastodon env changed" + +- name: Restart mastodon streaming service. + ansible.builtin.systemd: + state: restarted + name: mastodon-streaming.service + daemon_reload: true + become: true + listen: + - "mastodon streaming service changed" + - "mastodon selinux context changed" + - "mastodon postgres socket changed" + - "mastodon redis socket changed" + - "mastodon env changed" + +- name: Restart mastodon sidekiq service. + ansible.builtin.systemd: + state: restarted + name: mastodon-sidekiq.service + daemon_reload: true + become: true + listen: + - "mastodon sidekiq service changed" + - "mastodon selinux context changed" + - "mastodon postgres socket changed" + - "mastodon redis socket changed" + - "mastodon env changed" + +- name: Restart mastodon postgres service. + ansible.builtin.systemd: + state: restarted + name: mastodon-postgres.service + daemon_reload: true + become: true + listen: + - "mastodon postgres service changed" + - "mastodon selinux context changed" + +- name: Restart mastodon redis service. + ansible.builtin.systemd: + state: restarted + name: mastodon-redis.service + daemon_reload: true + become: true + listen: + - "mastodon redis service changed" + - "mastodon selinux context changed" + +- name: Restart mastodon nginx service. + ansible.builtin.systemd: + state: restarted + name: mastodon-nginx.service + daemon_reload: true + become: true + listen: + - "mastodon nginx service changed" + - "mastodon selinux context changed" + +- name: Restart mastodon elasticsearch service. + ansible.builtin.systemd: + state: restarted + name: mastodon-elasticsearch.service + daemon_reload: true + become: true + listen: + - "mastodon elasticsearch service changed" + - "mastodon selinux context changed" diff --git a/roles/mastodon/meta/main.yml b/roles/mastodon/meta/main.yml index 8dd1ae8..c43d2cd 100644 --- a/roles/mastodon/meta/main.yml +++ b/roles/mastodon/meta/main.yml @@ -1,44 +1,20 @@ galaxy_info: author: saibotk - description: "Deploys a mastodon server via docker using traefik." + description: Deploy mastodon with podman and systemd. + + issue_tracker_url: https://git.saibotk.de/saibotk.de/infrastructure/-/issues + license: GPL-3.0-only - min_ansible_version: "2.9" - standalone: true + + min_ansible_version: "2.10" platforms: - - name: EL - versions: - - all - - name: GenericUNIX - versions: - - all - name: Fedora versions: - - all - - name: opensuse - versions: - - all - - name: GenericBSD - versions: - - all - - name: FreeBSD - versions: - - all - - name: Ubuntu - versions: - - all - - name: SLES - versions: - - all - - name: GenericLinux - versions: - - all - - name: Debian - versions: - - all + - "41" + + standalone: true galaxy_tags: [] -dependencies: - - role: docker - - role: traefik +dependencies: [] diff --git a/roles/mastodon/tasks/main.yml b/roles/mastodon/tasks/main.yml index 03eaee6..04a3d34 100644 --- a/roles/mastodon/tasks/main.yml +++ b/roles/mastodon/tasks/main.yml @@ -1,172 +1,309 @@ ---- -# Tasks file for the mastodon role - -# Infrastructure -# Ansible instructions to deploy the infrastructure -# Copyright (C) 2019-2020 Christoph (Sheogorath) Kern -# Copyright (C) 2020 Alexander Wellbrock -# Copyright (C) 2020 Saibotk -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - name: Update default SELinux contexts community.general.sefcontext: - target: "{{ item }}(/.*)?" + target: "{{ item.target }}" setype: "container_file_t" + selevel: "{{ item.selevel }}" state: present - with_items: - - "{{ mastodon_database_location }}" - - "{{ mastodon_public_location }}" - - "{{ mastodon_redis_location }}" - - "{{ mastodon_elastic_location }}" - - "{{ mastodon_nginx_location }}" + loop: + - target: "{{ mastodon_public_location }}(/.*)?" + selevel: "{{ mastodon_public_selinux_level }}" + - target: "{{ mastodon_postgres_location }}(/.*)?" + selevel: "{{ mastodon_postgres_selinux_level }}" + - target: "{{ mastodon_redis_location }}(/.*)?" + selevel: "{{ mastodon_redis_selinux_level }}" + - target: "{{ mastodon_install_location }}/redis.conf" + selevel: "{{ mastodon_redis_selinux_level }}" + - target: "{{ mastodon_elasticsearch_location }}(/.*)?" + selevel: "{{ mastodon_elasticsearch_selinux_level }}" + - target: "{{ mastodon_install_location }}/default.conf" + selevel: "{{ mastodon_nginx_selinux_level }}" become: true + notify: "mastodon selinux context changed" -- name: Create install directory +- name: Create mastodon directories. ansible.builtin.file: - path: "{{ item }}" - state: directory - mode: "0700" + path: "{{ mastodon_install_location }}" owner: "root" group: "root" - with_items: - - "{{ mastodon_install_location }}" + mode: "0700" + state: directory become: true -- name: Create data directories - ansible.builtin.file: # noqa risky-file-permissions # Container manages permissions on its own - path: "{{ item }}" - state: directory - setype: "container_file_t" - with_items: - - "{{ mastodon_database_location }}" - - "{{ mastodon_public_location }}" - - "{{ mastodon_redis_location }}" - - "{{ mastodon_elastic_location }}" - - "{{ mastodon_nginx_location }}" - become: true +- name: Ensure mastodon data directories exist. + block: + - name: Stat mastodon public dir. + ansible.builtin.stat: + path: "{{ mastodon_public_location }}" + become: true + register: mastodon_stat_public_dir -- name: Adjust elasticsearch directory permissions - ansible.builtin.file: - path: "{{ mastodon_elastic_location }}" - state: directory - setype: "container_file_t" - mode: "0750" - owner: 1000 - group: "root" - become: true + - name: Stat mastodon postgres dir. + ansible.builtin.stat: + path: "{{ mastodon_postgres_location }}" + become: true + register: mastodon_stat_postgres_dir + + - name: Stat mastodon redis dir. + ansible.builtin.stat: + path: "{{ mastodon_redis_location }}" + become: true + register: mastodon_stat_redis_dir + + - name: Stat mastodon elasticsearch dir. + ansible.builtin.stat: + path: "{{ mastodon_elasticsearch_location }}" + become: true + register: mastodon_stat_elasticsearch_dir + + - name: Create mastodon data directories. + ansible.builtin.file: + path: "{{ item.path }}" + state: directory + mode: "0700" + owner: "{{ item.owner }}" + group: "{{ item.group }}" + loop: + - path: "{{ mastodon_public_location }}" + owner: "{{ mastodon_stat_public_dir.stat.uid | default('root') }}" + group: "{{ mastodon_stat_public_dir.stat.gid | default('root') }}" + - path: "{{ mastodon_postgres_location }}" + owner: "{{ mastodon_stat_postgres_dir.stat.uid | default('root') }}" + group: "{{ mastodon_stat_postgres_dir.stat.gid | default('root') }}" + - path: "{{ mastodon_redis_location }}" + owner: "{{ mastodon_stat_redis_dir.stat.uid | default('root') }}" + group: "{{ mastodon_stat_redis_dir.stat.gid | default('root') }}" + - path: "{{ mastodon_elasticsearch_location }}" + owner: "{{ mastodon_stat_elasticsearch_dir.stat.uid | default('root') }}" + group: "{{ mastodon_stat_elasticsearch_dir.stat.gid | default('root') }}" + become: true - name: Adjust sysctl settings for elasticsearch ansible.posix.sysctl: name: vm.max_map_count value: "262144" state: present - when: - - mastodon_config.enable_elasticsearch is defined and mastodon_config.enable_elasticsearch - - mastodon_elasticsearch_adjust_sysctl become: true -- name: Create public data directory - ansible.builtin.file: - path: "{{ mastodon_public_location }}/system" - mode: "0755" - owner: "991" - group: "991" - state: directory - setype: "container_file_t" - become: true - -- name: Deploy nginx proxy config file +- name: Deploy redis config file. ansible.builtin.template: - src: "default.conf" - dest: "{{ mastodon_nginx_location }}/default.conf" + src: redis.conf.j2 + dest: "{{ mastodon_install_location }}/redis.conf" + owner: "{{ mastodon_stat_redis_dir.stat.uid | default('root') }}" + group: "{{ mastodon_stat_redis_dir.stat.gid | default('root') }}" + mode: "0600" + become: true + notify: "mastodon redis service changed" + +- name: Deploy mastodon environment file. + ansible.builtin.template: + src: mastodon.env.j2 + dest: "{{ mastodon_install_location }}/mastodon.env" + mode: "0600" + owner: "root" + group: "root" + become: true + notify: "mastodon env changed" + +- name: Deploy postgres environment file. + ansible.builtin.template: + src: mastodon-postgres.env.j2 + dest: "{{ mastodon_install_location }}/mastodon-postgres.env" mode: "0600" owner: "root" group: "root" become: true -- name: Check if migration is needed - ansible.builtin.command: "grep -q 'mastodon/mastodon:{{ mastodon_image_version }}' '{{ mastodon_install_location }}/docker-compose.yml'" - register: mastodon_version_fact - ignore_errors: true - changed_when: mastodon_version_fact.rc > 0 - failed_when: false - become: true - notify: ["Pull mastodon image", "Stop mastodon for upgrade"] - -- name: Immediately run / flush Ansible handlers - ansible.builtin.meta: "flush_handlers" - -- name: Deploy config +- name: Deploy elasticsearch environment file. ansible.builtin.template: - src: ".env.production" - dest: "{{ mastodon_install_location }}/.env.production" + src: mastodon-elasticsearch.env.j2 + dest: "{{ mastodon_install_location }}/mastodon-elasticsearch.env" mode: "0600" owner: "root" group: "root" - tags: - - mastodon become: true -- name: Deploy docker-compose.yml +- name: Add caddy config file. + block: + - name: Check caddy config dir. + ansible.builtin.stat: + path: "{{ caddy_install_dir }}/config" + become: true + register: caddy_stat_config_dir + + - name: Template caddy config for mastodon. + ansible.builtin.template: + src: mastodon.caddy.j2 + dest: "{{ caddy_install_dir }}/config/mastodon.caddy" + mode: "0600" + setype: "container_file_t" + selevel: "{{ caddy_selinux_level }}" + owner: "{{ caddy_stat_config_dir.stat.uid }}" + group: "{{ caddy_stat_config_dir.stat.gid }}" + notify: "caddy config changed" + become: true + +- name: Ensure mastodon nginx config exists. + block: + - name: Stat mastodon nginx config. + ansible.builtin.stat: + path: "{{ mastodon_install_location }}/default.conf" + become: true + register: mastodon_stat_nginx_config + + - name: Deploy mastodon nginx config. + ansible.builtin.template: + src: default.conf.j2 + dest: "{{ mastodon_install_location }}/default.conf" + mode: "0600" + owner: "{{ mastodon_stat_nginx_config.stat.uid | default('root') }}" + group: "{{ mastodon_stat_nginx_config.stat.gid | default('root') }}" + setype: "container_file_t" + selevel: "{{ mastodon_nginx_selinux_level }}" + become: true + notify: "mastodon nginx service changed" + +- name: Create mastodon postgres socket volume. ansible.builtin.template: - src: "docker-compose.yml" - dest: "{{ mastodon_install_location }}/docker-compose.yml" - mode: "0600" + src: mastodon-postgres-socket.volume + dest: /etc/containers/systemd/mastodon-postgres-socket.volume owner: "root" group: "root" - validate: docker compose -f %s config -q - tags: - - mastodon + mode: "0644" + become: true + notify: + - "mastodon postgres socket changed" + +- name: Create mastodon redis socket volume. + ansible.builtin.template: + src: mastodon-redis-socket.volume + dest: /etc/containers/systemd/mastodon-redis-socket.volume + owner: "root" + group: "root" + mode: "0644" + become: true + notify: + - "mastodon redis socket changed" + +- name: Create mastodon web container file. + ansible.builtin.template: + src: mastodon-web.container.j2 + dest: /etc/containers/systemd/mastodon-web.container + owner: "root" + group: "root" + mode: "0644" + become: true + notify: "mastodon web service changed" + +- name: Create mastodon streaming container file. + ansible.builtin.template: + src: mastodon-streaming.container.j2 + dest: /etc/containers/systemd/mastodon-streaming.container + owner: "root" + group: "root" + mode: "0644" + become: true + notify: "mastodon streaming service changed" + +- name: Create mastodon sidekiq container file. + ansible.builtin.template: + src: mastodon-sidekiq.container.j2 + dest: /etc/containers/systemd/mastodon-sidekiq.container + owner: "root" + group: "root" + mode: "0644" + become: true + notify: "mastodon sidekiq service changed" + +- name: Create mastodon postgres container file. + ansible.builtin.template: + src: mastodon-postgres.container.j2 + dest: /etc/containers/systemd/mastodon-postgres.container + owner: "root" + group: "root" + mode: "0644" + become: true + notify: "mastodon postgres service changed" + +- name: Create mastodon redis container file. + ansible.builtin.template: + src: mastodon-redis.container.j2 + dest: /etc/containers/systemd/mastodon-redis.container + owner: "root" + group: "root" + mode: "0644" + become: true + notify: "mastodon redis service changed" + +- name: Create mastodon elasticsearch container file. + ansible.builtin.template: + src: mastodon-elasticsearch.container.j2 + dest: /etc/containers/systemd/mastodon-elasticsearch.container + owner: "root" + group: "root" + mode: "0644" + become: true + notify: "mastodon elasticsearch service changed" + +- name: Create mastodon nginx container file. + ansible.builtin.template: + src: mastodon-nginx.container.j2 + dest: /etc/containers/systemd/mastodon-nginx.container + owner: "root" + group: "root" + mode: "0644" + become: true + notify: "mastodon nginx service changed" + +# TODO: Quadlet does not change networks when their definition changes +# We need to find a solution to recreate the network +- name: Create mastodon frontend network definition file. + ansible.builtin.template: + src: mastodon-frontend.network.j2 + dest: "/etc/containers/systemd/mastodon-frontend.network" + owner: "root" + group: "root" + mode: "0644" become: true -- name: Migrate database - ansible.builtin.command: "docker-compose run --rm web rails db:migrate" - args: - chdir: "{{ mastodon_install_location }}" - when: - # noqa no-handler - - mastodon_version_fact is changed - tags: - - docker - - mastodon +# TODO: Quadlet does not change networks when their definition changes +# We need to find a solution to recreate the network +- name: Create mastodon backend network definition file. + ansible.builtin.template: + src: mastodon-backend.network.j2 + dest: "/etc/containers/systemd/mastodon-backend.network" + owner: "root" + group: "root" + mode: "0644" become: true - changed_when: true - environment: - PYTHONPATH: "" -- name: Clear cache - ansible.builtin.command: docker-compose run --rm web bin/tootctl cache clear - args: - chdir: "{{ mastodon_install_location }}" - when: - # noqa no-handler - - mastodon_version_fact is changed - tags: - - docker - - mastodon +- name: Ensure mastodon services are enabled. + ansible.builtin.systemd: + enabled: true + name: "{{ item }}" + daemon_reload: true + loop: + - mastodon-postgres.service + - mastodon-redis.service + - mastodon-elasticsearch.service + - mastodon-web.service + - mastodon-streaming.service + - mastodon-sidekiq.service + - mastodon-nginx.service become: true - changed_when: true - environment: - PYTHONPATH: "" -- name: Compose mastodon - community.docker.docker_compose_v2: - state: present - project_src: "{{ mastodon_install_location }}" - pull: always - remove_orphans: true - tags: - - docker - - mastodon +- name: Flush handlers + ansible.builtin.meta: flush_handlers + +- name: Ensure mastodon services are started. + ansible.builtin.systemd: + state: started + name: "{{ item }}" + loop: + - mastodon-postgres.service + - mastodon-redis.service + - mastodon-elasticsearch.service + - mastodon-web.service + - mastodon-streaming.service + - mastodon-sidekiq.service + - mastodon-nginx.service become: true diff --git a/roles/mastodon/templates/.env.production b/roles/mastodon/templates/.env.production deleted file mode 100644 index 9661187..0000000 --- a/roles/mastodon/templates/.env.production +++ /dev/null @@ -1,266 +0,0 @@ -{{ ansible_managed | comment }} -# Service dependencies -# You may set REDIS_URL instead for more advanced options -# You may also set REDIS_NAMESPACE to share Redis between multiple Mastodon servers -REDIS_HOST=redis -REDIS_PORT=6379 -# You may set DATABASE_URL instead for more advanced options -DB_HOST=db -DB_USER=postgres -DB_NAME=postgres -DB_PASS= -DB_PORT=5432 - -{% if mastodon_config.enable_elasticsearch is defined %} -# Optional ElasticSearch configuration -# You may also set ES_PREFIX to share the same cluster between multiple Mastodon servers (falls back to REDIS_NAMESPACE if not set) -ES_ENABLED={{ mastodon_config.enable_elasticsearch | bool | lower }} -ES_HOST=es -ES_PORT=9200 -{% endif %} - -# Federation -# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation. -# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com. -LOCAL_DOMAIN={{ mastodon_config.local_domain }} - -# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links) - -{% if mastodon_config.web_domain is defined %} -# Use this only if you need to run mastodon on a different domain than the one used for federation. -# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md -# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING. -WEB_DOMAIN={{ mastodon_config.web_domain }} - -{% endif %} -# Use this if you want to have several aliases handler@example1.com -# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not -# be added. Comma separated values -# ALTERNATE_DOMAINS=example1.com,example2.com - -# Application secrets -# Generate each with the `RAILS_ENV=production bundle exec rails secret` task (`docker-compose run --rm web rails secret` if you use docker compose) -SECRET_KEY_BASE={{ mastodon_config.secret_key_base }} -OTP_SECRET={{ mastodon_config.otp_secret }} -# DB encryption secrets -# Generate them yourself, each 32 alphanumeric or -# Generate them with `RAILS_ENV=production bundle exec rails db:encryption:init` task (`docker-compose run --rm web rails db:encryption:init` if you use docker compose) -ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY={{ mastodon_config.ar_enc_deterministic_key }} -ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT={{ mastodon_config.ar_enc_derivation_salt }} -ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY={{ mastodon_config.ar_enc_primary_key }} - -# VAPID keys (used for push notifications -# You can generate the keys using the following command (first is the private key, second is the public one) -# You should only generate this once per instance. If you later decide to change it, all push subscription will -# be invalidated, requiring the users to access the website again to resubscribe. -# -# Generate with `RAILS_ENV=production bundle exec rails mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rails mastodon:webpush:generate_vapid_key` if you use docker compose) -# -# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html -VAPID_PRIVATE_KEY={{ mastodon_config.vapid_private_key }} -VAPID_PUBLIC_KEY={{ mastodon_config.vapid_public_key }} - -# Registrations -{% if mastodon_config.single_user_mode is defined %} -# Single user mode will disable registrations and redirect frontpage to the first profile -SINGLE_USER_MODE={{ mastodon_config.single_user_mode | bool | lower }} -{% endif %} -# Prevent registrations with following e-mail domains -# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc -# Only allow registrations with the following e-mail domains -# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc - -# Optionally change default language -# DEFAULT_LOCALE=de - -# E-mail configuration -# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers -# If you want to use an SMTP server without authentication (e.g local Postfix relay) -# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and -# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough). -SMTP_SERVER={{ mastodon_config.smtp_server }} -SMTP_PORT={{ mastodon_config.smtp_port | default(587) }} -SMTP_LOGIN={{ mastodon_config.smtp_login }} -SMTP_PASSWORD={{ mastodon_config.smtp_password }} -SMTP_FROM_ADDRESS={{ mastodon_config.smtp_from_address }} -#SMTP_REPLY_TO= -#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN -#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail -#SMTP_AUTH_METHOD=plain -#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt -#SMTP_OPENSSL_VERIFY_MODE=peer -#SMTP_ENABLE_STARTTLS_AUTO=true -#SMTP_TLS=true - -# Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files. -# PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system -# PAPERCLIP_ROOT_URL=/system - -# Optional asset host for multi-server setups -# The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN -# if WEB_DOMAIN is not set. For example, the server may have the -# following header field: -# Access-Control-Allow-Origin: https://example.com/ -# CDN_HOST=https://assets.example.com - -# S3 (optional) -# The attachment host must allow cross origin request from WEB_DOMAIN or -# LOCAL_DOMAIN if WEB_DOMAIN is not set. For example, the server may have the -# following header field: -# Access-Control-Allow-Origin: https://192.168.1.123:9000/ -# S3_ENABLED=true -# S3_BUCKET= -# AWS_ACCESS_KEY_ID= -# AWS_SECRET_ACCESS_KEY= -# S3_REGION= -# S3_PROTOCOL=http -# S3_HOSTNAME=192.168.1.123:9000 - -# S3 (Minio Config (optional) Please check Minio instance for details) -# The attachment host must allow cross origin request - see the description -# above. -# S3_ENABLED=true -# S3_BUCKET= -# AWS_ACCESS_KEY_ID= -# AWS_SECRET_ACCESS_KEY= -# S3_REGION= -# S3_PROTOCOL=https -# S3_HOSTNAME= -# S3_ENDPOINT= -# S3_SIGNATURE_VERSION= - -# Google Cloud Storage (optional) -# Use S3 compatible API. Since GCS does not support Multipart Upload, -# increase the value of S3_MULTIPART_THRESHOLD to disable Multipart Upload. -# The attachment host must allow cross origin request - see the description -# above. -# S3_ENABLED=true -# AWS_ACCESS_KEY_ID= -# AWS_SECRET_ACCESS_KEY= -# S3_REGION= -# S3_PROTOCOL=https -# S3_HOSTNAME=storage.googleapis.com -# S3_ENDPOINT=https://storage.googleapis.com -# S3_MULTIPART_THRESHOLD=52428801 # 50.megabytes - -# Swift (optional) -# The attachment host must allow cross origin request - see the description -# above. -# SWIFT_ENABLED=true -# SWIFT_USERNAME= -# For Keystone V3, the value for SWIFT_TENANT should be the project name -# SWIFT_TENANT= -# SWIFT_PASSWORD= -# Some OpenStack V3 providers require PROJECT_ID (optional) -# SWIFT_PROJECT_ID= -# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid -# issues with token rate-limiting during high load. -# SWIFT_AUTH_URL= -# SWIFT_CONTAINER= -# SWIFT_OBJECT_URL= -# SWIFT_REGION= -# Defaults to 'default' -# SWIFT_DOMAIN_NAME= -# Defaults to 60 seconds. Set to 0 to disable -# SWIFT_CACHE_TTL= - -# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare) -# S3_ALIAS_HOST= - -# Streaming API integration -# STREAMING_API_BASE_URL= - -# Advanced settings -# If you need to use pgBouncer, you need to disable prepared statements: -# PREPARED_STATEMENTS=false - -# Cluster number setting for streaming API server. -# If you comment out following line, cluster number will be `numOfCpuCores - 1`. -STREAMING_CLUSTER_NUM=1 - -# Docker mastodon user -# If you use Docker, you may want to assign UID/GID manually. -{% if mastodon_config.docker_uid is defined %} -UID={{ mastodon_config.docker_uid }} -{% endif %} -{% if mastodon_config.docker_gid is defined %} -GID={{ mastodon_config.docker_gid }} -{% endif %} - -# LDAP authentication (optional) -# LDAP_ENABLED=true -# LDAP_HOST=localhost -# LDAP_PORT=389 -# LDAP_METHOD=simple_tls -# LDAP_BASE= -# LDAP_BIND_DN= -# LDAP_PASSWORD= -# LDAP_UID=cn -# LDAP_SEARCH_FILTER=%{uid}=%{email} - -# PAM authentication (optional) -# PAM authentication uses for the email generation the "email" pam variable -# and optional as fallback PAM_DEFAULT_SUFFIX -# The pam environment variable "email" is provided by: -# https://github.com/devkral/pam_email_extractor -# PAM_ENABLED=true -# Fallback email domain for email address generation (LOCAL_DOMAIN by default) -# PAM_EMAIL_DOMAIN=example.com -# Name of the pam service (pam "auth" section is evaluated) -# PAM_DEFAULT_SERVICE=rpam -# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default) -# PAM_CONTROLLED_SERVICE=rpam - -# Global OAuth settings (optional) : -# If you have only one strategy, you may want to enable this -# OAUTH_REDIRECT_AT_SIGN_IN=true - -# Optional CAS authentication (cf. omniauth-cas) : -# CAS_ENABLED=true -# CAS_URL=https://sso.myserver.com/ -# CAS_HOST=sso.myserver.com/ -# CAS_PORT=443 -# CAS_SSL=true -# CAS_VALIDATE_URL= -# CAS_CALLBACK_URL= -# CAS_LOGOUT_URL= -# CAS_LOGIN_URL= -# CAS_UID_FIELD='user' -# CAS_CA_PATH= -# CAS_DISABLE_SSL_VERIFICATION=false -# CAS_UID_KEY='user' -# CAS_NAME_KEY='name' -# CAS_EMAIL_KEY='email' -# CAS_NICKNAME_KEY='nickname' -# CAS_FIRST_NAME_KEY='firstname' -# CAS_LAST_NAME_KEY='lastname' -# CAS_LOCATION_KEY='location' -# CAS_IMAGE_KEY='image' -# CAS_PHONE_KEY='phone' - -# Optional SAML authentication (cf. omniauth-saml) -#SAML_ENABLED=true -#SAML_ACS_URL=https://flausch.social/auth/auth/saml/callback -#SAML_ISSUER=mastodon -#SAML_IDP_SSO_TARGET_URL=https://auth.the-rainbow-unicorns.de/auth/realms/unicorns/protocol/saml/clients/mastodon -#SAML_IDP_CERT= -#SAML_IDP_CERT_FINGERPRINT= -#SAML_NAME_IDENTIFIER_FORMAT=urn:oasis:names:tc:SAML:2.0:nameid-format:persistent -#SAML_CERT= -#SAML_PRIVATE_KEY= -#SAML_SECURITY_WANT_ASSERTION_SIGNED=true -#SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true -#SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true -#SAML_ATTRIBUTES_STATEMENTS_UID=name -#SAML_ATTRIBUTES_STATEMENTS_EMAIL=email -#SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.16.840.1.113730.3.1.241" -#SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME="urn:oid:2.5.4.42" -#SAML_ATTRIBUTES_STATEMENTS_LAST_NAME="urn:oid:2.5.4.4" -#SAML_UID_ATTRIBUTE=name -#SAML_ATTRIBUTES_STATEMENTS_VERIFIED= -#SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL= - -# Use HTTP proxy for outgoing request (optional) -# http_proxy=http://gateway.local:8118 -# Access control for hidden service. -# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true diff --git a/roles/mastodon/templates/default.conf b/roles/mastodon/templates/default.conf.j2 similarity index 79% rename from roles/mastodon/templates/default.conf rename to roles/mastodon/templates/default.conf.j2 index e527452..1c4421e 100644 --- a/roles/mastodon/templates/default.conf +++ b/roles/mastodon/templates/default.conf.j2 @@ -3,12 +3,29 @@ map $http_upgrade $connection_upgrade { '' close; } +upstream backend { + server backend-mastodon-web:3000 fail_timeout=0; +} + +upstream streaming { + # Instruct nginx to send connections to the server with the least number of connections + # to ensure load is distributed evenly. + least_conn; + + server backend-mastodon-streaming:4000 fail_timeout=0; + # Uncomment these lines for load-balancing multiple instances of streaming for scaling, + # this assumes your running the streaming server on ports 4000, 4001, and 4002: + # server 127.0.0.1:4001 fail_timeout=0; + # server 127.0.0.1:4002 fail_timeout=0; +} + proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g; server { - listen 80; - listen [::]:80; + listen 8080; + listen [::]:8080; server_name {{ mastodon_domain }}; + set_real_ip_from 10.0.0.0/8; set_real_ip_from 172.16.0.0/12; @@ -29,6 +46,7 @@ server { gzip_buffers 16 8k; gzip_http_version 1.1; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon; + gzip_static on; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; @@ -60,7 +78,7 @@ server { proxy_set_header X-Forwarded-Proto https; proxy_set_header Proxy ""; - proxy_pass http://streaming:4000; + proxy_pass http://streaming; proxy_buffering off; proxy_redirect off; proxy_http_version 1.1; @@ -78,7 +96,7 @@ server { proxy_set_header Proxy ""; proxy_pass_header Server; - proxy_pass http://web:3000; + proxy_pass http://backend; proxy_buffering on; proxy_redirect off; proxy_http_version 1.1; @@ -94,7 +112,5 @@ server { tcp_nodelay on; } - - error_page 404 500 501 502 503 504 /500.html; } diff --git a/roles/mastodon/templates/docker-compose.yml b/roles/mastodon/templates/docker-compose.yml deleted file mode 100644 index 4e362af..0000000 --- a/roles/mastodon/templates/docker-compose.yml +++ /dev/null @@ -1,179 +0,0 @@ -{{ ansible_managed | comment }} - -# Infrastructure -# Ansible instructions to deploy the infrastructure -# Copyright (C) 2019-2020 Christoph (Sheogorath) Kern -# Copyright (C) 2020 Saibotk -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -version: '2.1' -services: - nginx: - image: docker.io/library/nginx:alpine - mem_limit: 32mb - memswap_limit: 64mb - labels: - - "traefik.http.routers.mastodon.rule=Host(`{{ mastodon_domain }}`) && PathPrefix(`/`)" - - "traefik.http.routers.mastodon.entrypoints=websecure" - - "traefik.http.routers.mastodon.tls.certresolver={{ mastodon_traefik_certresolver }}" - - "traefik.http.routers.mastodon.middlewares=mastodon,compress" - - "traefik.http.middlewares.mastodon.headers.sslredirect=true" - - "traefik.http.middlewares.mastodon.headers.stsSeconds=63072000" - - "traefik.http.middlewares.mastodon.headers.referrerPolicy=same-origin" - - - "traefik.enable=true" -{% if proxy_network is defined %} - - "traefik.docker.network={{ proxy_network }}" -{% endif %} - networks: - frontend: -{% if proxy_network is defined %} - {{ proxy_network }}: -{% endif %} - volumes: - - "{{ mastodon_nginx_location }}/default.conf:/etc/nginx/conf.d/default.conf:ro" - - "{{ mastodon_public_location }}:/usr/share/nginx/html:ro" - depends_on: - - web - - streaming - restart: always - - db: - image: docker.io/library/postgres:{{ mastodon_database_image_version }} - mem_limit: 512mb - memswap_limit: 768mb - read_only: true - tmpfs: - - /run/postgresql:size=512K - - /tmp:size=128K - environment: - - "POSTGRES_HOST_AUTH_METHOD=trust" - stop_grace_period: 2m - stop_signal: SIGINT - networks: - backend: - healthcheck: - test: ['CMD', 'pg_isready', '-U', 'postgres'] - volumes: - - {{ mastodon_database_location }}:/var/lib/postgresql/data - restart: always - - redis: - image: docker.io/library/redis:{{ mastodon_redis_image_version }} - mem_limit: 512mb - memswap_limit: 768mb - networks: - backend: - healthcheck: - test: ['CMD', 'redis-cli', 'ping'] - volumes: - - {{ mastodon_redis_location }}:/data - restart: always - -{% if mastodon_config.enable_elasticsearch is defined and mastodon_config.enable_elasticsearch %} - es: - restart: always - image: docker.elastic.co/elasticsearch/elasticsearch:{{ mastodon_elasticsearch_image_version }} - mem_limit: 1124mb - memswap_limit: 1280mb - environment: - - "ES_JAVA_OPTS=-Xms512m -Xmx512m -Des.enforce.bootstrap.checks=true" - - "xpack.license.self_generated.type=basic" - - "xpack.security.enabled=false" - - "xpack.watcher.enabled=false" - - "xpack.graph.enabled=false" - - "xpack.ml.enabled=false" - - "bootstrap.memory_lock=true" - - "cluster.name=es-mastodon" - - "discovery.type=single-node" - - "thread_pool.write.queue_size=1000" - - "ingest.geoip.downloader.enabled=false" - networks: - backend: - healthcheck: - test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"] - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 - hard: 65536 - volumes: - - {{ mastodon_elastic_location }}:/usr/share/elasticsearch/data -{% endif %} - - web: - image: ghcr.io/mastodon/mastodon:{{ mastodon_image_version }} - mem_limit: 1024mb - memswap_limit: 1280mb - env_file: {{ mastodon_install_location }}/.env.production - depends_on: - - db - - redis -{% if mastodon_config.enable_elasticsearch is defined and mastodon_config.enable_elasticsearch %} - - es -{% endif %} - volumes: - - {{ mastodon_public_location }}/system:/mastodon/public/system - networks: - frontend: - backend: - healthcheck: - test: ['CMD-SHELL',"curl -s --noproxy localhost localhost:3000/health | grep -q 'OK' || exit 1"] - restart: always - command: bundle exec puma -C config/puma.rb - - streaming: - image: ghcr.io/mastodon/mastodon-streaming:{{ mastodon_image_version }} - mem_limit: 1024mb - memswap_limit: 1280mb - env_file: {{ mastodon_install_location }}/.env.production - networks: - frontend: - backend: - healthcheck: - test: ['CMD-SHELL', "curl -s --noproxy localhost localhost:4000/api/v1/streaming/health | grep -q 'OK' || exit 1"] - depends_on: - - db - - redis - restart: always - command: node ./streaming/index.js - - sidekiq: - image: ghcr.io/mastodon/mastodon:{{ mastodon_image_version }} - mem_limit: 1024mb - memswap_limit: 1280mb - env_file: {{ mastodon_install_location }}/.env.production - depends_on: - - db - - redis - volumes: - - {{ mastodon_public_location }}/system:/mastodon/public/system - networks: - frontend: - backend: - healthcheck: - test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"] - restart: always - command: bundle exec sidekiq - -networks: - frontend: - backend: - internal: true -{% if proxy_network is defined %} - {{ proxy_network }}: - external: true -{% endif %} diff --git a/roles/mastodon/templates/mastodon-backend.network.j2 b/roles/mastodon/templates/mastodon-backend.network.j2 new file mode 100644 index 0000000..49ae27d --- /dev/null +++ b/roles/mastodon/templates/mastodon-backend.network.j2 @@ -0,0 +1,8 @@ +{{ ansible_managed | comment }} + +[Network] +NetworkName = mastodon_backend +Driver = bridge +Internal = true +# Most containers expect ipv4 here +IPv6 = false diff --git a/roles/mastodon/templates/mastodon-elasticsearch.container.j2 b/roles/mastodon/templates/mastodon-elasticsearch.container.j2 new file mode 100644 index 0000000..a154b8b --- /dev/null +++ b/roles/mastodon/templates/mastodon-elasticsearch.container.j2 @@ -0,0 +1,43 @@ +{{ ansible_managed | comment }} + +[Unit] +Description = ElasticSearch for Mastodon + +[Service] +Restart = always +RestartSec = 5s + +[Container] +Image = {{ mastodon_elasticsearch_containerimage }}:{{ mastodon_elasticsearch_image_tag }} +ContainerName = mastodon-elasticsearch + +# To prevent it from using chroot +User = 1000 + +HealthCmd = CMD-SHELL curl --silent --fail localhost:9200/_cluster/health || exit 1 + +# AutoUpdate = registry +LogDriver = journald + +NoNewPrivileges = true +DropCapability = all +UserNS = auto:size=65535 +{% if mastodon_elasticsearch_selinux_level != omit %} +SecurityLabelLevel = {{ mastodon_elasticsearch_selinux_level }} +{% endif %} + +EnvironmentFile = {{ mastodon_install_location }}/mastodon-elasticsearch.env + +Network = mastodon-backend.network:alias=backend-mastodon-elasticsearch + +Volume = {{ mastodon_elasticsearch_location }}:/usr/share/elasticsearch/data:U + +Ulimit = nofile=65536:65536 +Ulimit = memlock=-1:-1 + +PodmanArgs = --memory={{ mastodon_memory_high }} +PodmanArgs = --memory-swap={{ mastodon_swap_max }} +PodmanArgs = --memory-reservation={{ mastodon_memory_low }} + +[Install] +WantedBy = default.target diff --git a/roles/mastodon/templates/mastodon-elasticsearch.env.j2 b/roles/mastodon/templates/mastodon-elasticsearch.env.j2 new file mode 100644 index 0000000..c4d0fe8 --- /dev/null +++ b/roles/mastodon/templates/mastodon-elasticsearch.env.j2 @@ -0,0 +1,13 @@ +{{ ansible_managed | comment }} + +ES_JAVA_OPTS=-Xms512m -Xmx512m -Des.enforce.bootstrap.checks=true +xpack.license.self_generated.type=basic +xpack.security.enabled=false +xpack.watcher.enabled=false +xpack.graph.enabled=false +xpack.ml.enabled=false +bootstrap.memory_lock=true +cluster.name=es-mastodon +discovery.type=single-node +thread_pool.write.queue_size=1000 +ingest.geoip.downloader.enabled=false diff --git a/roles/mastodon/templates/mastodon-frontend.network.j2 b/roles/mastodon/templates/mastodon-frontend.network.j2 new file mode 100644 index 0000000..ab9d6e5 --- /dev/null +++ b/roles/mastodon/templates/mastodon-frontend.network.j2 @@ -0,0 +1,6 @@ +{{ ansible_managed | comment }} + +[Network] +NetworkName = mastodon_frontend +Driver = bridge +IPv6 = true diff --git a/roles/mastodon/templates/mastodon-nginx.container.j2 b/roles/mastodon/templates/mastodon-nginx.container.j2 new file mode 100644 index 0000000..48c4507 --- /dev/null +++ b/roles/mastodon/templates/mastodon-nginx.container.j2 @@ -0,0 +1,45 @@ +{{ ansible_managed | comment }} + +[Unit] +Description = Nginx for Mastodon +Requires = mastodon-web.service mastodon-streaming.service +After = mastodon-web.service mastodon-streaming.service + +[Service] +Restart = always +RestartSec = 5s + +[Container] +Image = {{ mastodon_nginx_containerimage }}:{{ mastodon_nginx_image_tag }} +ContainerName = mastodon-nginx + +# AutoUpdate = registry +LogDriver = journald + +ReadOnly = true +NoNewPrivileges = true +DropCapability = all +UserNS = container:mastodon-web +{% if mastodon_nginx_selinux_level != omit %} +SecurityLabelLevel = {{ mastodon_nginx_selinux_level }} +{% endif %} + +Network = mastodon-backend.network +Network = caddy.network:alias=caddy-mastodon-nginx +ExposeHostPort = 8080 + +# Equal to mastodon user, so it can stat the public folder +User = 991 + +Volume = {{ mastodon_public_location }}:/usr/share/nginx/html/system:ro +Volume = {{ mastodon_install_location }}/default.conf:/etc/nginx/conf.d/default.conf:U,ro + +Tmpfs = /var/cache/nginx:rw,noexec,nosuid,nodev,size=1g +Tmpfs = /tmp:rw,noexec,nosuid,nodev,size=100m + +PodmanArgs = --memory={{ mastodon_nginx_memory_high }} +PodmanArgs = --memory-swap={{ mastodon_nginx_swap_max }} +PodmanArgs = --memory-reservation={{ mastodon_nginx_memory_low }} + +[Install] +WantedBy = default.target diff --git a/roles/mastodon/templates/mastodon-postgres-socket.volume b/roles/mastodon/templates/mastodon-postgres-socket.volume new file mode 100644 index 0000000..34ba6f7 --- /dev/null +++ b/roles/mastodon/templates/mastodon-postgres-socket.volume @@ -0,0 +1,5 @@ +{{ ansible_managed | comment }} + +[Volume] + +VolumeName = mastodon-postgres-socket diff --git a/roles/mastodon/templates/mastodon-postgres.container.j2 b/roles/mastodon/templates/mastodon-postgres.container.j2 new file mode 100644 index 0000000..7653c8a --- /dev/null +++ b/roles/mastodon/templates/mastodon-postgres.container.j2 @@ -0,0 +1,48 @@ +{{ ansible_managed | comment }} + +[Unit] +Description = Postgres for Mastodon + +[Service] +Restart = always +RestartSec = 5s + +[Container] +Image = {{ mastodon_postgres_containerimage }}:{{ mastodon_postgres_image_tag }} +ContainerName = mastodon-postgres + +HealthCmd = CMD pg_isready -U postgres + +# AutoUpdate = registry +LogDriver = journald + +# User = 70 +# Group = 70 + +# TODO: Investigate necessary capabilities +# NoNewPrivileges = true +ReadOnly = true +DropCapability = all +AddCapability = CHOWN DAC_OVERRIDE SETUID SETGID +UserNS = auto:size=65535 +{% if mastodon_postgres_selinux_level != omit %} +SecurityLabelLevel = {{ mastodon_postgres_selinux_level }} +{% endif %} + +EnvironmentFile = {{ mastodon_install_location }}/mastodon-postgres.env + +Volume = mastodon-postgres-socket:/var/run/postgresql:U,z +Volume = {{ mastodon_postgres_location }}:/var/lib/postgresql/data:U +Tmpfs = /run/postgresql:rw,noexec,nosuid,nodev,size=1m +Tmpfs = /tmp:rw,noexec,nosuid,nodev,size=50m + +ExposeHostPort = 5432 + +PodmanArgs = --memory={{ mastodon_postgres_memory_high }} +PodmanArgs = --memory-swap={{ mastodon_postgres_swap_max }} +PodmanArgs = --memory-reservation={{ mastodon_postgres_memory_low }} + +PodmanArgs = --stop-signal=SIGINT + +[Install] +WantedBy = default.target diff --git a/roles/mastodon/templates/mastodon-postgres.env.j2 b/roles/mastodon/templates/mastodon-postgres.env.j2 new file mode 100644 index 0000000..27cd002 --- /dev/null +++ b/roles/mastodon/templates/mastodon-postgres.env.j2 @@ -0,0 +1,3 @@ +{{ ansible_managed | comment }} + +POSTGRES_PASSWORD={{ mastodon_postgres_password }} diff --git a/roles/mastodon/templates/mastodon-redis-socket.volume b/roles/mastodon/templates/mastodon-redis-socket.volume new file mode 100644 index 0000000..bbb2ff5 --- /dev/null +++ b/roles/mastodon/templates/mastodon-redis-socket.volume @@ -0,0 +1,5 @@ +{{ ansible_managed | comment }} + +[Volume] + +VolumeName = mastodon-redis-socket diff --git a/roles/mastodon/templates/mastodon-redis.container.j2 b/roles/mastodon/templates/mastodon-redis.container.j2 new file mode 100644 index 0000000..69a3814 --- /dev/null +++ b/roles/mastodon/templates/mastodon-redis.container.j2 @@ -0,0 +1,46 @@ +{{ ansible_managed | comment }} + +[Unit] +Description = Redis for Mastodon + +[Service] +Restart = always +RestartSec = 5s + +[Container] +Image = {{ mastodon_redis_containerimage }}:{{ mastodon_redis_image_tag }} +ContainerName = mastodon-redis + +HealthCmd = CMD redis-cli -s /run/redis/redis.sock ping + +Exec = redis-server /usr/lib/redis/redis.conf + +# AutoUpdate = registry +LogDriver = journald + +NoNewPrivileges = true +ReadOnly = true +DropCapability = all +AddCapability = CHOWN DAC_OVERRIDE SETUID SETGID +UserNS = auto:size=65535 +{% if mastodon_redis_selinux_level != omit %} +SecurityLabelLevel = {{ mastodon_redis_selinux_level }} +{% endif %} + +User = redis +Group = redis + +# Sysctl = vm.overcommit_memory=1 + +Volume = mastodon-redis-socket:/run/redis:U,z +Volume = {{ mastodon_install_location }}/redis.conf:/usr/lib/redis/redis.conf:U,ro +Volume = {{ mastodon_redis_location }}:/data:U + +Tmpfs = /run:rw,noexec,nosuid,nodev,size=1m + +PodmanArgs = --memory={{ mastodon_redis_memory_high }} +PodmanArgs = --memory-swap={{ mastodon_redis_swap_max }} +PodmanArgs = --memory-reservation={{ mastodon_redis_memory_low }} + +[Install] +WantedBy = default.target diff --git a/roles/mastodon/templates/mastodon-sidekiq.container.j2 b/roles/mastodon/templates/mastodon-sidekiq.container.j2 new file mode 100644 index 0000000..2d438f1 --- /dev/null +++ b/roles/mastodon/templates/mastodon-sidekiq.container.j2 @@ -0,0 +1,45 @@ +{{ ansible_managed | comment }} + +[Unit] +Description = Mastodon Sidekiq Server +Requires = mastodon-postgres.service mastodon-redis.service +After = mastodon-postgres.service mastodon-redis.service + +[Service] +Restart = always +RestartSec = 5s + +[Container] +Image = {{ mastodon_containerimage }}:{{ mastodon_image_tag }} +ContainerName = mastodon-sidekiq + +HealthCmd = CMD-SHELL ps aux | grep '[s]idekiq\ 6' || false + +Exec = bundle exec sidekiq + +# AutoUpdate = registry +LogDriver = journald + +NoNewPrivileges = true +DropCapability = all +UserNS = container:mastodon-web +{% if mastodon_selinux_level != omit %} +SecurityLabelLevel = {{ mastodon_selinux_level }} +{% endif %} + +Network = mastodon-frontend.network +Network = mastodon-backend.network + +EnvironmentFile = {{ mastodon_install_location }}/mastodon.env + +Volume = {{ mastodon_public_location }}:/mastodon/public/system + +Volume = mastodon-postgres-socket:/var/run/postgresql:z +Volume = mastodon-redis-socket:/run/redis:z + +PodmanArgs = --memory={{ mastodon_memory_high }} +PodmanArgs = --memory-swap={{ mastodon_swap_max }} +PodmanArgs = --memory-reservation={{ mastodon_memory_low }} + +[Install] +WantedBy = default.target diff --git a/roles/mastodon/templates/mastodon-streaming.container.j2 b/roles/mastodon/templates/mastodon-streaming.container.j2 new file mode 100644 index 0000000..dbcc361 --- /dev/null +++ b/roles/mastodon/templates/mastodon-streaming.container.j2 @@ -0,0 +1,44 @@ +{{ ansible_managed | comment }} + +[Unit] +Description = Mastodon Streaming Server +Requires = mastodon-postgres.service mastodon-redis.service +After = mastodon-postgres.service mastodon-redis.service + +[Service] +Restart = always +RestartSec = 5s + +[Container] +Image = {{ mastodon_streaming_containerimage }}:{{ mastodon_image_tag }} +ContainerName = mastodon-streaming + +HealthCmd = CMD-SHELL curl -s --noproxy localhost localhost:4000/api/v1/streaming/health | grep -q 'OK' || exit 1 + +Exec = node ./streaming/index.js + +AutoUpdate = registry +LogDriver = journald + +NoNewPrivileges = true +DropCapability = all +UserNS = auto:size=65535 +{% if mastodon_selinux_level != omit %} +SecurityLabelLevel = {{ mastodon_selinux_level }} +{% endif %} + +Network = mastodon-backend.network:alias=backend-mastodon-streaming + +ExposeHostPort = 4000 + +EnvironmentFile = {{ mastodon_install_location }}/mastodon.env + +Volume = mastodon-postgres-socket:/var/run/postgresql:z +Volume = mastodon-redis-socket:/run/redis:z + +PodmanArgs = --memory={{ mastodon_memory_high }} +PodmanArgs = --memory-swap={{ mastodon_swap_max }} +PodmanArgs = --memory-reservation={{ mastodon_memory_low }} + +[Install] +WantedBy = default.target diff --git a/roles/mastodon/templates/mastodon-web.container.j2 b/roles/mastodon/templates/mastodon-web.container.j2 new file mode 100644 index 0000000..655270a --- /dev/null +++ b/roles/mastodon/templates/mastodon-web.container.j2 @@ -0,0 +1,45 @@ +{{ ansible_managed | comment }} + +[Unit] +Description = Mastodon Web Server +Requires = mastodon-postgres.service mastodon-redis.service mastodon-elasticsearch.service +After = mastodon-postgres.service mastodon-redis.service mastodon-elasticsearch.service + +[Service] +Restart = always +RestartSec = 5s + +[Container] +Image = {{ mastodon_containerimage }}:{{ mastodon_image_tag }} +ContainerName = mastodon-web + +HealthCmd = CMD-SHELL curl -s --noproxy localhost localhost:3000/health | grep -q 'OK' || exit 1 + +Exec = bundle exec puma -C config/puma.rb + +# AutoUpdate = registry +LogDriver = journald + +NoNewPrivileges = true +DropCapability = all +UserNS = auto:size=65535 +{% if mastodon_selinux_level != omit %} +SecurityLabelLevel = {{ mastodon_selinux_level }} +{% endif %} + +Network = mastodon-frontend.network +Network = mastodon-backend.network:alias=backend-mastodon-web + +EnvironmentFile = {{ mastodon_install_location }}/mastodon.env + +Volume = {{ mastodon_public_location }}:/mastodon/public/system:U + +Volume = mastodon-postgres-socket:/var/run/postgresql:z +Volume = mastodon-redis-socket:/run/redis:z + +PodmanArgs = --memory={{ mastodon_memory_high }} +PodmanArgs = --memory-swap={{ mastodon_swap_max }} +PodmanArgs = --memory-reservation={{ mastodon_memory_low }} + +[Install] +WantedBy = default.target diff --git a/roles/mastodon/templates/mastodon.caddy.j2 b/roles/mastodon/templates/mastodon.caddy.j2 new file mode 100644 index 0000000..70a1420 --- /dev/null +++ b/roles/mastodon/templates/mastodon.caddy.j2 @@ -0,0 +1,31 @@ +{{ ansible_managed | comment }} + +{{ mastodon_domain }} { + encode gzip + + header { + # enable HSTS + Strict-Transport-Security "max-age=31536000; preload;" + + # disable clients from sniffing the media type + X-Content-Type-Options nosniff + + # clickjacking protection + X-Frame-Options DENY + + # keep referrer data off of HTTP connections + Referrer-Policy no-referrer-when-downgrade + + # Server name removing + -Server + } + + # TODO: Move to own caddy file + route /.well-known/matrix* { + reverse_proxy https://matrix.flausch.social { + header_up -Host + } + } + + reverse_proxy caddy-mastodon-nginx:8080 +} diff --git a/roles/mastodon/templates/mastodon.env.j2 b/roles/mastodon/templates/mastodon.env.j2 new file mode 100644 index 0000000..86fc561 --- /dev/null +++ b/roles/mastodon/templates/mastodon.env.j2 @@ -0,0 +1,113 @@ +{{ ansible_managed | comment }} + +# Redis +# ----- +REDIS_URL=unix:///run/redis/redis.sock + +# PostgreSQL +# ---------- +DB_HOST=/var/run/postgresql +DB_USER=postgres +DB_NAME=postgres +DB_PASS={{ mastodon_postgres_password }} +DB_PORT=5432 + +# Elasticsearch +# ------------------------ +ES_ENABLED=true +ES_HOST=backend-mastodon-elasticsearch +ES_PORT=9200 + +# Federation +# ---------- +# This identifies your server and cannot be changed safely later +# ---------- +LOCAL_DOMAIN={{ mastodon_config.local_domain }} + +{% if mastodon_config.web_domain is defined %} +# Use this only if you need to run mastodon on a different domain than the one used for federation. +# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md +# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING. +WEB_DOMAIN={{ mastodon_config.web_domain }} + +{% endif %} +# Use this if you want to have several aliases handler@example1.com +# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not +# be added. Comma separated values +# ALTERNATE_DOMAINS=example1.com,example2.com + +# Secrets +# ------- +# Make sure to use `bundle exec rails secret` to generate secrets +# ------- +SECRET_KEY_BASE={{ mastodon_config.secret_key_base }} +OTP_SECRET={{ mastodon_config.otp_secret }} + +# Encryption secrets +# ------------------ +# Must be available (and set to same values) for all server processes +# These are private/secret values, do not share outside hosting environment +# Use `bin/rails db:encryption:init` to generate fresh secrets +# Do not change these secrets once in use, as this would cause data loss and other issues +# ------------------ +ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY={{ mastodon_config.ar_enc_deterministic_key }} +ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT={{ mastodon_config.ar_enc_derivation_salt }} +ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY={{ mastodon_config.ar_enc_primary_key }} + +# Web Push +# -------- +# Generate with `bundle exec rails mastodon:webpush:generate_vapid_key` +# -------- +VAPID_PRIVATE_KEY={{ mastodon_config.vapid_private_key }} +VAPID_PUBLIC_KEY={{ mastodon_config.vapid_public_key }} + + +# Registrations +# ------------ +{% if mastodon_config.single_user_mode is defined %} +# Single user mode will disable registrations and redirect frontpage to the first profile +SINGLE_USER_MODE={{ mastodon_config.single_user_mode | bool | lower }} +{% endif %} +# Prevent registrations with following e-mail domains +# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc +# Only allow registrations with the following e-mail domains +# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc + +# Optionally change default language +# DEFAULT_LOCALE=de + + +# Sending mail +# ------------ +SMTP_SERVER={{ mastodon_config.smtp_server }} +SMTP_PORT={{ mastodon_config.smtp_port | default(587) }} +SMTP_LOGIN={{ mastodon_config.smtp_login }} +SMTP_PASSWORD={{ mastodon_config.smtp_password }} +SMTP_FROM_ADDRESS={{ mastodon_config.smtp_from_address }} +#SMTP_REPLY_TO= +#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN +#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail +#SMTP_AUTH_METHOD=plain +#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt +#SMTP_OPENSSL_VERIFY_MODE=peer +#SMTP_ENABLE_STARTTLS_AUTO=true +#SMTP_TLS=true + + +# File storage (optional) +# ----------------------- +# S3 (Minio Config (optional) Please check Minio instance for details) +# The attachment host must allow cross origin request from WEB_DOMAIN or +# LOCAL_DOMAIN if WEB_DOMAIN is not set. For example, the server may have the +# following header field: +# ----------------------- +# S3_ENABLED=true +# S3_BUCKET= +# AWS_ACCESS_KEY_ID= +# AWS_SECRET_ACCESS_KEY= +# S3_REGION= +# S3_PROTOCOL=https +# S3_HOSTNAME= +# S3_ENDPOINT= +# S3_SIGNATURE_VERSION= + diff --git a/roles/mastodon/templates/redis.conf.j2 b/roles/mastodon/templates/redis.conf.j2 new file mode 100644 index 0000000..9264036 --- /dev/null +++ b/roles/mastodon/templates/redis.conf.j2 @@ -0,0 +1,513 @@ +{{ ansible_managed | comment }} + +# Only listen on localhost +bind 127.0.0.1 -::1 + +# When protected mode is on and the default user has no password, the server +# only accepts local connections from the IPv4 address (127.0.0.1), IPv6 address +# (::1) or Unix domain sockets. +protected-mode yes + +# Redis uses default hardened security configuration directives to reduce the +# attack surface on innocent users. Therefore, several sensitive configuration +# directives are immutable, and some potentially-dangerous commands are blocked. +# +# Configuration directives that control files that Redis writes to (e.g., 'dir' +# and 'dbfilename') and that aren't usually modified during runtime +# are protected by making them immutable. +# +# Commands that can increase the attack surface of Redis and that aren't usually +# called by users are blocked by default. +# +# These can be exposed to either all connections or just local ones by setting +# each of the configs listed below to either of these values: +# +# no - Block for any connection (remain immutable) +# yes - Allow for any connection (no protection) +# local - Allow only for local connections. Ones originating from the +# IPv4 address (127.0.0.1), IPv6 address (::1) or Unix domain sockets. +enable-protected-configs no +enable-debug-command no +enable-module-command no + +# Accept connections on the specified port, default is 6379 (IANA #815344). +# If port 0 is specified Redis will not listen on a TCP socket. +port 0 + +# Unix socket. +unixsocket /run/redis/redis.sock +unixsocketperm 777 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# Apply OS-specific mechanism to mark the listening socket with the specified +# ID, to support advanced routing and filtering capabilities. +# +# On Linux, the ID represents a connection mark. +# On FreeBSD, the ID represents a socket cookie ID. +# On OpenBSD, the ID represents a route table ID. +# +# The default value is 0, which implies no marking is required. +# socket-mark-id 0 + +################################# GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +# When Redis is supervised by upstart or systemd, this parameter has no impact. +daemonize no + +# If you run Redis from upstart or systemd, Redis can interact with your +# supervision tree. Options: +# supervised no - no supervision interaction +# supervised upstart - signal upstart by putting Redis into SIGSTOP mode +# requires "expect stop" in your upstart job config +# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET +# on startup, and updating Redis status on a regular +# basis. +# supervised auto - detect upstart or systemd method based on +# UPSTART_JOB or NOTIFY_SOCKET environment variables +# Note: these supervision methods only signal "process is ready." +# They do not enable continuous pings back to your supervisor. +# +# The default is "no". To run under upstart/systemd, you can simply uncomment +# the line below: +supervised auto + +pidfile /run/redis_6379.pid + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +# nothing (nothing is logged) +loglevel notice + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile "" + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +# Set the local environment which is used for string comparison operations, and +# also affect the performance of Lua scripts. Empty String indicates the locale +# is derived from the environment variables. +locale-collate "" + +################################ SNAPSHOTTING ################################ + +# Save the DB to disk. +# +# save [ ...] +# +# Redis will save the DB if the given number of seconds elapsed and it +# surpassed the given number of write operations against the DB. +# +# Snapshotting can be completely disabled with a single empty string argument +# as in following example: +# +# save "" +# +# Unless specified otherwise, by default Redis will save the DB: +# * After 3600 seconds (an hour) if at least 1 change was performed +# * After 300 seconds (5 minutes) if at least 100 changes were performed +# * After 60 seconds if at least 10000 changes were performed +# +# You can set these explicitly by uncommenting the following line. +# +save 3600 1 300 100 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# By default compression is enabled as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# Enables or disables full sanitization checks for ziplist and listpack etc when +# loading an RDB or RESTORE payload. This reduces the chances of a assertion or +# crash later on while processing commands. +# Options: +# no - Never perform full sanitization +# yes - Always perform full sanitization +# clients - Perform full sanitization only for user connections. +# Excludes: RDB files, RESTORE commands received from the master +# connection, and client connections which have the +# skip-sanitize-payload ACL flag. +# The default should be 'clients' but since it currently affects cluster +# resharding via MIGRATE, it is temporarily set to 'no' by default. +# +sanitize-dump-payload clients + +# The filename where to dump the DB +dbfilename dump.rdb + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +dir /data + +################################## SECURITY ################################### + +# Warning: since Redis is pretty fast, an outside user can try up to +# 1 million passwords per second against a modern box. This means that you +# should use very strong passwords, otherwise they will be very easy to break. +# Note that because the password is really a shared secret between the client +# and the server, and should not be memorized by any human, the password +# can be easily a long string from /dev/urandom or whatever, so by using a +# long and unguessable password no brute force attack will be possible. + +# Redis ACL users are defined in the following format: +# +# user ... acl rules ... +# +# For example: +# +# user worker +@list +@connection ~jobs:* on >ffa9203c493aa99 +# +# The special username "default" is used for new connections. If this user +# has the "nopass" rule, then new connections will be immediately authenticated +# as the "default" user without the need of any password provided via the +# AUTH command. Otherwise if the "default" user is not flagged with "nopass" +# the connections will start in not authenticated state, and will require +# AUTH (or the HELLO command AUTH option) in order to be authenticated and +# start to work. +# +# The ACL rules that describe what a user can do are the following: +# +# on Enable the user: it is possible to authenticate as this user. +# off Disable the user: it's no longer possible to authenticate +# with this user, however the already authenticated connections +# will still work. +# skip-sanitize-payload RESTORE dump-payload sanitization is skipped. +# sanitize-payload RESTORE dump-payload is sanitized (default). +# + Allow the execution of that command. +# May be used with `|` for allowing subcommands (e.g "+config|get") +# - Disallow the execution of that command. +# May be used with `|` for blocking subcommands (e.g "-config|set") +# +@ Allow the execution of all the commands in such category +# with valid categories are like @admin, @set, @sortedset, ... +# and so forth, see the full list in the server.c file where +# the Redis command table is described and defined. +# The special category @all means all the commands, but currently +# present in the server, and that will be loaded in the future +# via modules. +# +|first-arg Allow a specific first argument of an otherwise +# disabled command. It is only supported on commands with +# no sub-commands, and is not allowed as negative form +# like -SELECT|1, only additive starting with "+". This +# feature is deprecated and may be removed in the future. +# allcommands Alias for +@all. Note that it implies the ability to execute +# all the future commands loaded via the modules system. +# nocommands Alias for -@all. +# ~ Add a pattern of keys that can be mentioned as part of +# commands. For instance ~* allows all the keys. The pattern +# is a glob-style pattern like the one of KEYS. +# It is possible to specify multiple patterns. +# %R~ Add key read pattern that specifies which keys can be read +# from. +# %W~ Add key write pattern that specifies which keys can be +# written to. +# allkeys Alias for ~* +# resetkeys Flush the list of allowed keys patterns. +# & Add a glob-style pattern of Pub/Sub channels that can be +# accessed by the user. It is possible to specify multiple channel +# patterns. +# allchannels Alias for &* +# resetchannels Flush the list of allowed channel patterns. +# > Add this password to the list of valid password for the user. +# For example >mypass will add "mypass" to the list. +# This directive clears the "nopass" flag (see later). +# < Remove this password from the list of valid passwords. +# nopass All the set passwords of the user are removed, and the user +# is flagged as requiring no password: it means that every +# password will work against this user. If this directive is +# used for the default user, every new connection will be +# immediately authenticated with the default user without +# any explicit AUTH command required. Note that the "resetpass" +# directive will clear this condition. +# resetpass Flush the list of allowed passwords. Moreover removes the +# "nopass" status. After "resetpass" the user has no associated +# passwords and there is no way to authenticate without adding +# some password (or setting it as "nopass" later). +# reset Performs the following actions: resetpass, resetkeys, resetchannels, +# allchannels (if acl-pubsub-default is set), off, clearselectors, -@all. +# The user returns to the same state it has immediately after its creation. +# () Create a new selector with the options specified within the +# parentheses and attach it to the user. Each option should be +# space separated. The first character must be ( and the last +# character must be ). +# clearselectors Remove all of the currently attached selectors. +# Note this does not change the "root" user permissions, +# which are the permissions directly applied onto the +# user (outside the parentheses). +# +# ACL rules can be specified in any order: for instance you can start with +# passwords, then flags, or key patterns. However note that the additive +# and subtractive rules will CHANGE MEANING depending on the ordering. +# For instance see the following example: +# +# user alice on +@all -DEBUG ~* >somepassword +# +# This will allow "alice" to use all the commands with the exception of the +# DEBUG command, since +@all added all the commands to the set of the commands +# alice can use, and later DEBUG was removed. However if we invert the order +# of two ACL rules the result will be different: +# +# user alice on -DEBUG +@all ~* >somepassword +# +# Now DEBUG was removed when alice had yet no commands in the set of allowed +# commands, later all the commands are added, so the user will be able to +# execute everything. +# +# Basically ACL rules are processed left-to-right. +# +# The following is a list of command categories and their meanings: +# * keyspace - Writing or reading from keys, databases, or their metadata +# in a type agnostic way. Includes DEL, RESTORE, DUMP, RENAME, EXISTS, DBSIZE, +# KEYS, EXPIRE, TTL, FLUSHALL, etc. Commands that may modify the keyspace, +# key or metadata will also have `write` category. Commands that only read +# the keyspace, key or metadata will have the `read` category. +# * read - Reading from keys (values or metadata). Note that commands that don't +# interact with keys, will not have either `read` or `write`. +# * write - Writing to keys (values or metadata) +# * admin - Administrative commands. Normal applications will never need to use +# these. Includes REPLICAOF, CONFIG, DEBUG, SAVE, MONITOR, ACL, SHUTDOWN, etc. +# * dangerous - Potentially dangerous (each should be considered with care for +# various reasons). This includes FLUSHALL, MIGRATE, RESTORE, SORT, KEYS, +# CLIENT, DEBUG, INFO, CONFIG, SAVE, REPLICAOF, etc. +# * connection - Commands affecting the connection or other connections. +# This includes AUTH, SELECT, COMMAND, CLIENT, ECHO, PING, etc. +# * blocking - Potentially blocking the connection until released by another +# command. +# * fast - Fast O(1) commands. May loop on the number of arguments, but not the +# number of elements in the key. +# * slow - All commands that are not Fast. +# * pubsub - PUBLISH / SUBSCRIBE related +# * transaction - WATCH / MULTI / EXEC related commands. +# * scripting - Scripting related. +# * set - Data type: sets related. +# * sortedset - Data type: zsets related. +# * list - Data type: lists related. +# * hash - Data type: hashes related. +# * string - Data type: strings related. +# * bitmap - Data type: bitmaps related. +# * hyperloglog - Data type: hyperloglog related. +# * geo - Data type: geo related. +# * stream - Data type: streams related. +# +# For more information about ACL configuration please refer to +# the Redis web site at https://redis.io/topics/acl + +# ACL LOG +# +# The ACL Log tracks failed commands and authentication events associated +# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked +# by ACLs. The ACL Log is stored in memory. You can reclaim memory with +# ACL LOG RESET. Define the maximum entry length of the ACL Log below. +acllog-max-len 128 + +#################### KERNEL transparent hugepage CONTROL ###################### + +# Usually the kernel Transparent Huge Pages control is set to "madvise" or +# or "never" by default (/sys/kernel/mm/transparent_hugepage/enabled), in which +# case this config has no effect. On systems in which it is set to "always", +# redis will attempt to disable it specifically for the redis process in order +# to avoid latency problems specifically with fork(2) and CoW. +# If for some reason you prefer to keep it enabled, you can set this config to +# "no" and the kernel global to "always". +disable-thp yes + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Note that changing this value in a config file of an existing database and +# restarting the server can lead to data loss. A conversion needs to be done +# by setting it via CONFIG command on a live server first. +# +# Please check https://redis.io/topics/persistence for more information. + +appendonly no + +# The base name of the append only file. +# +# Redis 7 and newer use a set of append-only files to persist the dataset +# and changes applied to it. There are two basic types of files in use: +# +# - Base files, which are a snapshot representing the complete state of the +# dataset at the time the file was created. Base files can be either in +# the form of RDB (binary serialized) or AOF (textual commands). +# - Incremental files, which contain additional commands that were applied +# to the dataset following the previous file. +# +# In addition, manifest files are used to track the files and the order in +# which they were created and should be applied. +# +# Append-only file names are created by Redis following a specific pattern. +# The file name's prefix is based on the 'appendfilename' configuration +# parameter, followed by additional information about the sequence and type. +# +# For example, if appendfilename is set to appendonly.aof, the following file +# names could be derived: +# +# - appendonly.aof.1.base.rdb as a base file. +# - appendonly.aof.1.incr.aof, appendonly.aof.2.incr.aof as incremental files. +# - appendonly.aof.manifest as a manifest file. + +appendfilename "appendonly.aof" + +# For convenience, Redis stores all persistent append-only files in a dedicated +# directory. The name of the directory is determined by the appenddirname +# configuration parameter. + +appenddirname "appendonlydir" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync no". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +# Redis can create append-only base files in either RDB or AOF formats. Using +# the RDB format is always faster and more efficient, and disabling it is only +# supported for backward compatibility purposes. +aof-use-rdb-preamble yes + +# Redis supports recording timestamp annotations in the AOF to support restoring +# the data from a specific point-in-time. However, using this capability changes +# the AOF format in a way that may not be compatible with existing AOF parsers. +aof-timestamp-enabled no diff --git a/roles/mastodon/vars/main.yml b/roles/mastodon/vars/main.yml new file mode 100644 index 0000000..b47af67 --- /dev/null +++ b/roles/mastodon/vars/main.yml @@ -0,0 +1,5 @@ +# The container data volume mount locations +mastodon_postgres_location: "{{ mastodon_install_location }}/postgres" +mastodon_public_location: "{{ mastodon_install_location }}/public" +mastodon_redis_location: "{{ mastodon_install_location }}/redis" +mastodon_elasticsearch_location: "{{ mastodon_install_location }}/elasticsearch"