From 2e36d75bb691fafca011f5365aeb11df3a2910e7 Mon Sep 17 00:00:00 2001 From: Kuba Orlik Date: Fri, 11 Sep 2026 19:53:40 +0200 Subject: [PATCH] Initial commit --- README.md | 10 +++ defaults/main.yml | 6 ++ meta/main.yml | 17 ++++ tasks/backup.yml | 15 ++++ tasks/main.yml | 101 +++++++++++++++++++++++ templates/docker-compose.override.yml.j2 | 92 +++++++++++++++++++++ templates/post-cert.sh | 11 +++ 7 files changed, 252 insertions(+) create mode 100644 README.md create mode 100644 defaults/main.yml create mode 100644 meta/main.yml create mode 100644 tasks/backup.yml create mode 100644 tasks/main.yml create mode 100644 templates/docker-compose.override.yml.j2 create mode 100644 templates/post-cert.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..3fd6df9 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +```yml +- role: "mailcow" + vars: + mailcow_repo_dir: "/opt/mailcow-dockerized" + mailcow_dockerized_commit: "586b3a2ed1c" + mailcow_hostname_fqdn: "mail.strona-czynna.pl" + mailcow_storage_dir: "/mnt/juice/mailcow" + mailcow_local_http_port: "9001" + mailcow_timezone: "Europe/Warsaw" +``` diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..54b633b --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,6 @@ +mailcow_repo_dir: "/opt/mailcow-dockerized" +mailcow_dockerized_commit: "586b3a2ed1c" +mailcow_hostname_fqdn: "mail.strona-czynna.pl" +mailcow_storage_dir: "/mnt/juice/mailcow" +mailcow_local_http_port: "9001" +mailcow_timezone: "Europe/Warsaw" diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..fe4809b --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,17 @@ +--- +galaxy_info: + author: kuba + description: Setup mailcow on a VPS + license: MIT + min_ansible_version: "2.12" + platforms: + - name: Debian + versions: + - bullseye + - bookworm + - name: Ubuntu + versions: + - focal + - jammy + +dependencies: [] diff --git a/tasks/backup.yml b/tasks/backup.yml new file mode 100644 index 0000000..88f5c00 --- /dev/null +++ b/tasks/backup.yml @@ -0,0 +1,15 @@ +## The playbook first runs the 'backup.yml' from each role +- name: create the /backup-dirs file + file: + path: "/backup-dirs" + state: "touch" + mode: "0400" + changed_when: "true" # we always want ti give roles a chance to register dirs + +- ansible.builtin.lineinfile: + path: "/backup-dirs" + line: "{{ mailcow_storage_dir }}" + +- ansible.builtin.lineinfile: + path: "/backup-dirs" + line: "{{ mailcow_repo_dir }}" diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..6d14591 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,101 @@ +- name: Install required system packages # https://docs.mailcow.email/getstarted/install/ + apt: + name: + - git + - openssl + - curl + - grep + - jq + state: latest + update_cache: true + +- name: Open the necessary ports + community.general.ufw: + rule: allow + port: "{{item}}" + loop: + - 25 + - 465 + - 587 + - 143 + - 993 + - 110 + - 995 + - 4190 + +- name: "Make sure that the storage dir exists" + file: + path: "{{ mailcow_storage_dir }}" + state: "directory" + +- name: "Make sure that the storage dir exists for each volume" + file: + path: "{{ mailcow_storage_dir }}/{{item}}" + state: "directory" + loop: + - vmail-vol-1 + - vmail-index-vol-1 + - mysql-vol-1 + - mysql-socket-vol-1 + - redis-vol-1 + - rspamd-vol-1 + - postfix-vol-1 + - postfix-tlspol-vol-1 + - crypt-vol-1 + - sogo-web-vol-1 + - sogo-userdata-backup-vol-1 + - clamd-db-vol-1 + +- name: Clone/checkout mailcow repo + ansible.builtin.git: + repo: "https://github.com/mailcow/mailcow-dockerized" + dest: "{{ mailcow_repo_dir }}" + version: "{{ mailcow_dockerized_commit }}" + update: false + +- name: "Generate the config file" + shell: + cmd: "MAILCOW_HOSTNAME={{mailcow_hostname_fqdn}} MAILCOW_BRANCH=master MAILCOW_TZ={{mailcow_timezone}} ./generate_config.sh" + chdir: "{{ mailcow_repo_dir }}" + creates: "{{ mailcow_repo_dir }}/mailcow.conf" + +- name: "Disable mailcow letsencrypt" + lineinfile: + path: "{{ mailcow_repo_dir }}/mailcow.conf" + regexp: "^SKIP_LETS_ENCRYPT=" + line: SKIP_LETS_ENCRYPT=y + +- name: "Create post-cert script for mailcow" + template: + src: "post-cert.sh" + dest: "{{ mailcow_repo_dir }}/post-cert.sh" + mode: "0777" + +- name: "Setup post-cert renewal script to import certs into mailcow" + cron: + name: "import certs to mailcow" + minute: 45 + hour: 2 + job: "{{ mailcow_repo_dir }}/post-cert.sh" + +- name: "docker compose down" + community.docker.docker_compose_v2: + state: "absent" + project_src: "{{ mailcow_repo_dir }}" + +- name: Ensure HTTP_REDIRECT is set to 'n' + ansible.builtin.lineinfile: + path: "{{ mailcow_repo_dir }}/mailcow.conf" # Replace with the actual file path + regexp: "^HTTP_REDIRECT=" + line: "HTTP_REDIRECT=n" + +- name: "Generate docker-compose override" + template: + src: "docker-compose.override.yml.j2" + dest: "{{ mailcow_repo_dir }}/docker-compose.override.yml" + +- name: "docker compose up -d mailcow" + community.docker.docker_compose_v2: + state: "present" + pull: missing + project_src: "{{ mailcow_repo_dir }}" diff --git a/templates/docker-compose.override.yml.j2 b/templates/docker-compose.override.yml.j2 new file mode 100644 index 0000000..196bad4 --- /dev/null +++ b/templates/docker-compose.override.yml.j2 @@ -0,0 +1,92 @@ + +services: + nginx-mailcow: + ports: !override + - 127.0.0.1:{{mailcow_local_http_port}}:80 + + +# Don't use the default docker volumes but instead put the data in juicefs +volumes: + vmail-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/vmail-vol-1 + o: bind + + vmail-index-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/vmail-index-vol-1 + o: bind + + mysql-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/mysql-vol-1 + o: bind + + mysql-socket-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/mysql-socket-vol-1 + o: bind + + redis-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/redis-vol-1 + o: bind + + rspamd-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/rspamd-vol-1 + o: bind + + postfix-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/postfix-vol-1 + o: bind + + postfix-tlspol-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/postfix-tlspol-vol-1 + o: bind + + crypt-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/crypt-vol-1 + o: bind + + sogo-web-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/sogo-web-vol-1 + o: bind + + sogo-userdata-backup-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/sogo-userdata-backup-vol-1 + o: bind + + clamd-db-vol-1: + driver: local + driver_opts: + type: none + device: {{ mailcow_storage_dir }}/clamd-db-vol-1 + o: bind \ No newline at end of file diff --git a/templates/post-cert.sh b/templates/post-cert.sh new file mode 100644 index 0000000..220f633 --- /dev/null +++ b/templates/post-cert.sh @@ -0,0 +1,11 @@ +#!/bin/bash +rm "{{mailcow_repo_dir}}/data/assets/ssl/key.pem" +rm "{{mailcow_repo_dir}}/data/assets/ssl/cert.pem" + +cp "/etc/letsencrypt/live/{{mailcow_hostname_fqdn}}/fullchain.pem" "{{mailcow_repo_dir}}/data/assets/ssl/cert.pem" +cp "/etc/letsencrypt/live/{{mailcow_hostname_fqdn}}/privkey.pem" "{{mailcow_repo_dir}}/data/assets/ssl/key.pem" + +postfix_c=$(/usr/bin/docker ps -qaf name=postfix-mailcow) +dovecot_c=$(/usr/bin/docker ps -qaf name=dovecot-mailcow) +nginx_c=$(/usr/bin/docker ps -qaf name=nginx-mailcow) +/usr/bin/docker restart ${postfix_c} ${dovecot_c} ${nginx_c}