├── .github └── workflows │ ├── docker-test.yml │ └── shellcheck.yml ├── .gitignore ├── LICENSE ├── README.md ├── contrib └── systemd │ ├── certbot.service │ └── certbot.timer ├── docker-compose.nginx.yml ├── docker-compose.without-nginx.yml ├── docker-compose.yml ├── docs ├── creation-of-nonsuperuser.md └── issuing-letsencrypt-certificate.md ├── env.example ├── nginx ├── conf.d │ └── default.conf └── dhparams4096.pem └── scripts ├── UPGRADE.md ├── issue-certificate.sh └── upgrade-postgres.sh /.github/workflows/docker-test.yml: -------------------------------------------------------------------------------- 1 | name: Docker Test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | validate-compose-files: 11 | name: Validate Docker Compose Files 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Setup environment file 17 | run: | 18 | cp env.example .env 19 | 20 | - name: Validate base docker-compose file 21 | run: docker compose config 22 | 23 | - name: Validate docker-compose with NGINX 24 | run: | 25 | docker compose -f docker-compose.yml -f docker-compose.nginx.yml config 26 | 27 | - name: Validate docker-compose without NGINX 28 | run: docker compose -f docker-compose.yml -f docker-compose.without-nginx.yml config 29 | 30 | test-docker-deployment: 31 | name: Test Docker Deployment 32 | runs-on: ubuntu-latest 33 | needs: validate-compose-files 34 | steps: 35 | - uses: actions/checkout@v3 36 | 37 | - name: Create required directories 38 | run: | 39 | mkdir -p ./volumes/app/mattermost/{config,data,logs,plugins,client/plugins,bleve-indexes} 40 | sudo chown -R 2000:2000 ./volumes/app/mattermost 41 | 42 | - name: Setup environment file 43 | run: | 44 | cp env.example .env 45 | sed -i 's/DOMAIN=mm.example.com/DOMAIN=localhost/g' .env 46 | 47 | - name: Start Mattermost without NGINX 48 | run: | 49 | # Configure docker-compose to use without-nginx setup 50 | docker compose -f docker-compose.yml -f docker-compose.without-nginx.yml up -d 51 | 52 | - name: Verify containers are running 53 | run: | 54 | # Allow time for containers to start 55 | sleep 30 56 | docker ps 57 | docker compose ps 58 | 59 | - name: Check container logs 60 | run: | 61 | # Check logs for any issues 62 | docker compose logs mattermost --tail 20 63 | docker compose logs postgres --tail 20 64 | 65 | - name: Verify Mattermost API is accessible 66 | run: | 67 | # Check that API is responding 68 | curl -f --retry 10 --retry-delay 5 --retry-connrefused http://localhost:8065/api/v4/system/ping || exit 1 69 | 70 | - name: Stop containers 71 | run: | 72 | # Clean up containers 73 | docker compose down 74 | if: always() 75 | -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | name: ShellCheck 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | shellcheck: 11 | name: Shellcheck 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Run ShellCheck 16 | uses: ludeeus/action-shellcheck@master 17 | with: 18 | scandir: "./scripts" 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | volumes 2 | data 3 | certs 4 | .env 5 | docker-compose.override.yml 6 | docker-compose.override.yaml 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mattermost Docker 2 | [![ShellCheck](https://github.com/mattermost/docker/actions/workflows/shellcheck.yml/badge.svg)](https://github.com/mattermost/docker/actions/workflows/shellcheck.yml) 3 | [![Docker Test](https://github.com/mattermost/docker/actions/workflows/docker-test.yml/badge.svg)](https://github.com/mattermost/docker/actions/workflows/docker-test.yml) 4 | 5 | The official Docker deployment solution for Mattermost. 6 | 7 | ## Install & Usage 8 | 9 | Refer to the [Mattermost Docker deployment guide](https://docs.mattermost.com/deployment-guide/server/deploy-containers.html) for instructions on how to install and use this Docker image. 10 | 11 | ## Contribute 12 | PRs are welcome, refer to our [contributing guide](https://developers.mattermost.com/contribute/getting-started/) for an overview of the Mattermost contribution process. 13 | 14 | ## Upgrading from `mattermost-docker` 15 | 16 | This repository replaces the [deprecated mattermost-docker repository](https://github.com/mattermost/mattermost-docker). For an in-depth guide to upgrading, please refer to [this document](https://github.com/mattermost/docker/blob/main/scripts/UPGRADE.md). 17 | -------------------------------------------------------------------------------- /contrib/systemd/certbot.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Certbot certificate renew trigger 3 | After=network-online.target 4 | Wants=network-online.target 5 | 6 | [Service] 7 | Environment="VOLUME_ROOT=/home/admin/mattermost-docker" 8 | Type=oneshot 9 | ExecStart=/usr/bin/docker run --rm --name certbot --network mattermost \ 10 | -v '${VOLUME_ROOT}/certs/etc/letsencrypt:/etc/letsencrypt' \ 11 | -v '${VOLUME_ROOT}/certs/var/lib/letsencrypt:/var/lib/letsencrypt' \ 12 | -v shared-webroot:/webroot \ 13 | certbot/certbot renew --webroot-path /webroot 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /contrib/systemd/certbot.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Certbot certificate renew trigger 3 | 4 | [Timer] 5 | Persistent=true 6 | OnCalendar=*-*-* 4:00:00 7 | 8 | [Install] 9 | WantedBy=timers.target 10 | -------------------------------------------------------------------------------- /docker-compose.nginx.yml: -------------------------------------------------------------------------------- 1 | services: 2 | nginx: 3 | depends_on: 4 | - mattermost 5 | container_name: nginx_mattermost 6 | image: nginx:${NGINX_IMAGE_TAG} 7 | restart: ${RESTART_POLICY} 8 | security_opt: 9 | - no-new-privileges:true 10 | pids_limit: 100 11 | read_only: true 12 | tmpfs: 13 | - /var/run 14 | - /var/cache 15 | - /var/log/nginx 16 | volumes: 17 | - ${NGINX_CONFIG_PATH}:/etc/nginx/conf.d:ro 18 | - ${NGINX_DHPARAMS_FILE}:/dhparams4096.pem 19 | - ${CERT_PATH}:/cert.pem:ro 20 | - ${KEY_PATH}:/key.pem:ro 21 | - shared-webroot:/usr/share/nginx/html 22 | environment: 23 | # timezone inside container 24 | - TZ 25 | ports: 26 | - ${HTTPS_PORT}:443 27 | - ${HTTP_PORT}:80 28 | mattermost: 29 | ports: 30 | - ${CALLS_PORT}:${CALLS_PORT}/udp 31 | - ${CALLS_PORT}:${CALLS_PORT}/tcp 32 | 33 | # Shared volume for Let's Encrypt certificate renewal with a webroot 34 | volumes: 35 | shared-webroot: 36 | name: shared-webroot 37 | 38 | # This network name is being used for Let's Encrypt certificate renewal 39 | networks: 40 | default: 41 | name: mattermost 42 | -------------------------------------------------------------------------------- /docker-compose.without-nginx.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mattermost: 3 | ports: 4 | - ${APP_PORT}:8065 5 | - ${CALLS_PORT}:${CALLS_PORT}/udp 6 | - ${CALLS_PORT}:${CALLS_PORT}/tcp 7 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # https://docs.docker.com/compose/environment-variables/ 2 | services: 3 | postgres: 4 | image: postgres:${POSTGRES_IMAGE_TAG} 5 | restart: ${RESTART_POLICY} 6 | security_opt: 7 | - no-new-privileges:true 8 | pids_limit: 100 9 | read_only: true 10 | tmpfs: 11 | - /tmp 12 | - /var/run/postgresql 13 | volumes: 14 | - ${POSTGRES_DATA_PATH}:/var/lib/postgresql/data 15 | environment: 16 | # timezone inside container 17 | - TZ 18 | 19 | # necessary Postgres options/variables 20 | - POSTGRES_USER 21 | - POSTGRES_PASSWORD 22 | - POSTGRES_DB 23 | 24 | mattermost: 25 | depends_on: 26 | - postgres 27 | image: mattermost/${MATTERMOST_IMAGE}:${MATTERMOST_IMAGE_TAG} 28 | restart: ${RESTART_POLICY} 29 | security_opt: 30 | - no-new-privileges:true 31 | pids_limit: 200 32 | read_only: ${MATTERMOST_CONTAINER_READONLY} 33 | tmpfs: 34 | - /tmp 35 | volumes: 36 | - ${MATTERMOST_CONFIG_PATH}:/mattermost/config:rw 37 | - ${MATTERMOST_DATA_PATH}:/mattermost/data:rw 38 | - ${MATTERMOST_LOGS_PATH}:/mattermost/logs:rw 39 | - ${MATTERMOST_PLUGINS_PATH}:/mattermost/plugins:rw 40 | - ${MATTERMOST_CLIENT_PLUGINS_PATH}:/mattermost/client/plugins:rw 41 | - ${MATTERMOST_BLEVE_INDEXES_PATH}:/mattermost/bleve-indexes:rw 42 | # When you want to use SSO with GitLab, you have to add the cert pki chain of GitLab inside Alpine 43 | # to avoid Token request failed: certificate signed by unknown authority 44 | # (link: https://github.com/mattermost/mattermost-server/issues/13059 and https://github.com/mattermost/docker/issues/34) 45 | # - ${GITLAB_PKI_CHAIN_PATH}:/etc/ssl/certs/pki_chain.pem:ro 46 | environment: 47 | # timezone inside container 48 | - TZ 49 | 50 | # necessary Mattermost options/variables (see env.example) 51 | - MM_SQLSETTINGS_DRIVERNAME 52 | - MM_SQLSETTINGS_DATASOURCE 53 | 54 | # necessary for bleve 55 | - MM_BLEVESETTINGS_INDEXDIR 56 | 57 | # additional settings 58 | - MM_SERVICESETTINGS_SITEURL 59 | 60 | # If you use rolling image tags and feel lucky watchtower can automatically pull new images and 61 | # instantiate containers from it. https://containrrr.dev/watchtower/ 62 | # Please keep in mind watchtower will have access on the docker socket. This can be a security risk. 63 | # 64 | # watchtower: 65 | # container_name: watchtower 66 | # image: containrrr/watchtower:latest 67 | # restart: unless-stopped 68 | # volumes: 69 | # - /var/run/docker.sock:/var/run/docker.sock 70 | -------------------------------------------------------------------------------- /docs/creation-of-nonsuperuser.md: -------------------------------------------------------------------------------- 1 | ## Migrating existing superuser to a less privileged user 2 | 3 | Mattermost-docker used to use the initially created user while database initialization. This is being accomplished by using the 4 | `POSTGRES_USER` environment variable of the PostgreSQL Docker image. While this is convinient because it requires less setup steps 5 | it's best practice and desirable to us a less privileged user to connect to the database. The following steps should be safe and 6 | executable while Mattermost is running. 7 | 8 | **NOTE:** Commands with a **$** prefix denote those are executed as user, **#** as root and commands without a prefix are database commands. 9 | We assume the database name is *mattermost* and the database user *mmuser*. 10 | 11 | ### 1. Find out the name or id of the PostgreSQL container 12 | To get either the name or the id of the running PostgeSQL container we can use `$ sudo docker ps`. 13 | 14 | ### 2. Attaching to the database container 15 | `$ sudo docker exec -it POSTGRES_CONTAINER_NAME/ID /bin/sh` 16 | 17 | ### 3. Connecting to the database 18 | ``` 19 | # psql DATABASE_NAME USERNAME 20 | e.g. 21 | # psql mattermost mmuser 22 | ``` 23 | 24 | ### 4. Checking if the Mattermost user is a superuser 25 | The following PostgreSQL command will print a list of the present users and its attributes. 26 | ``` 27 | \du 28 | ``` 29 | A possible output can look like the following: 30 | 31 | ``` 32 | List of roles 33 | Role name | Attributes | Member of 34 | -----------+------------------------------------------------------------+----------- 35 | mmuser | Superuser, Create role, Create DB, Replication, Bypass RLS | {} 36 | ``` 37 | 38 | ### 5. Creating a new `superuser` and changing existing role attributes 39 | **ATTENTION:** It's strongly recommended to create a database prior alteration. This can be done by stopping the database 40 | and backup the PostgreSQL data path at filesystem level and/or to use `pg_dumpall`. For this attach to the running PostgreSQL 41 | container described in step 2 and execute: 42 | ``` 43 | pg_dump -U mmuser -d mattermost > /var/lib/postgresql/data/BACKUP_MATTERMOST.sql 44 | ``` 45 | This dumps your *mattermost* database to the mounted directory, specified in the docker-compose.yml file. 46 | 47 | After your backup is done you can connect to the database (see step 3) and execute the following SQL queries: 48 | ``` 49 | CREATE ROLE superuser WITH BYPASSRLS REPLICATION CREATEDB CREATEROLE SUPERUSER LOGIN PASSWORD 'superuser_passwd'; 50 | 51 | ALTER DATABASE mattermost OWNER TO superuser; 52 | ALTER DATABASE postgres OWNER TO superuser; 53 | ALTER DATABASE template0 OWNER TO superuser; 54 | ALTER DATABASE template1 OWNER TO superuser; 55 | 56 | GRANT ALL PRIVILEGES ON DATABASE mattermost to mmuser; 57 | 58 | ALTER ROLE mmuser NOBYPASSRLS NOREPLICATION NOCREATEDB NOCREATEROLE NOSUPERUSER; 59 | ``` 60 | 61 | Even though you can apply the changes in a non-downtime it's required to restart the containers. 62 | -------------------------------------------------------------------------------- /docs/issuing-letsencrypt-certificate.md: -------------------------------------------------------------------------------- 1 | ## Issuing a Let's Encrypt certificate 2 | 3 | **NOTE:** Commands with a **$** prefix denote those are executed as user, **#** as root and commands without a prefix are database commands. 4 | 5 | For issuing a Let's Encrypt certificate one can use Docker as well which will save you from messing around with 6 | installing on the host system. 7 | This guide assumes you're inside the mattermost-docker directory but if using absolute paths in the volume bind mounts 8 | (e.g. /home/admin/mattermost-docker instead of `${PWD}`) it doesn't matter because the paths are unique. These commands 9 | requires that DNS records (A or CNAME) have been set and resolve to your server's external IP. 10 | 11 | ### 1. Issuing the certificate using the standalone authenticator (because there is no nginx yet) 12 | ``` 13 | $ sudo docker run -it --rm --name certbot -p 80:80 \ 14 | -v "${PWD}/certs/etc/letsencrypt:/etc/letsencrypt" \ 15 | -v "${PWD}/certs/lib/letsencrypt:/var/lib/letsencrypt" \ 16 | certbot/certbot certonly --standalone -d mm.example.com 17 | ``` 18 | 19 | ### 2. Changing the authenticator to webroot for later renewals 20 | 21 | ``` 22 | $ sudo docker run -it --rm --name certbot \ 23 | -v "${PWD}/certs/etc/letsencrypt:/etc/letsencrypt" \ 24 | -v "${PWD}/certs/lib/letsencrypt:/var/lib/letsencrypt" \ 25 | -v shared-webroot:/usr/share/nginx/html \ 26 | certbot/certbot certonly -a webroot -w /usr/share/nginx/html -d mm.example.com 27 | ``` 28 | 29 | This will ask you to abort or renew the certificate. When choosing to renew `certbot` will alter the renewal 30 | configuration to *webroot*. 31 | As an alternative (which will save you one certificate creation request https://letsencrypt.org/docs/rate-limits/) this can be done by yourself with the following commands 32 | 33 | ``` 34 | $ sudo sed -i 's/standalone/webroot/' ${PWD}/certs/etc/letsencrypt/renewal/mm.example.com.conf 35 | $ sudo tee -a ${PWD}/certs/etc/letsencrypt/renewal/mm.example.com.conf > /dev/null << EOF 36 | webroot_path = /usr/share/nginx/html, 37 | [[webroot_map]] 38 | EOF 39 | ``` 40 | 41 | ### 3. Command for requesting renewal (Let's Encrypt certificates do have a 3 month lifetime) 42 | 43 | ``` 44 | sudo docker run --rm --name certbot \ 45 | --network mattermost \ 46 | -v "${PWD}/certs/etc/letsencrypt:/etc/letsencrypt" \ 47 | -v "${PWD}/certs/lib/letsencrypt:/var/lib/letsencrypt" \ 48 | -v shared-webroot:/usr/share/nginx/html \ 49 | certbot/certbot renew --webroot-path /usr/share/nginx/html 50 | ``` 51 | 52 | This command can be called with a systemd timer on a regulary basis (e.g. once a day). Please take a look at the 53 | *contrib/systemd* folder. 54 | -------------------------------------------------------------------------------- /env.example: -------------------------------------------------------------------------------- 1 | # Domain of service 2 | DOMAIN=mm.example.com 3 | 4 | # Container settings 5 | ## Timezone inside the containers. The value needs to be in the form 'Europe/Berlin'. 6 | ## A list of these tz database names can be looked up at Wikipedia 7 | ## https://en.wikipedia.org/wiki/List_of_tz_database_time_zones 8 | TZ=UTC 9 | RESTART_POLICY=unless-stopped 10 | 11 | # Postgres settings 12 | ## Documentation for this image and available settings can be found on hub.docker.com 13 | ## https://hub.docker.com/_/postgres 14 | ## Please keep in mind this will create a superuser and it's recommended to use a less privileged 15 | ## user to connect to the database. 16 | ## A guide on how to change the database user to a nonsuperuser can be found in docs/creation-of-nonsuperuser.md 17 | POSTGRES_IMAGE_TAG=13-alpine 18 | POSTGRES_DATA_PATH=./volumes/db/var/lib/postgresql/data 19 | 20 | POSTGRES_USER=mmuser 21 | POSTGRES_PASSWORD=mmuser_password 22 | POSTGRES_DB=mattermost 23 | 24 | # Nginx 25 | ## The nginx container will use a configuration found at the NGINX_MATTERMOST_CONFIG. The config aims 26 | ## to be secure and uses a catch-all server vhost which will work out-of-the-box. For additional settings 27 | ## or changes ones can edit it or provide another config. Important note: inside the container, nginx sources 28 | ## every config file inside */etc/nginx/conf.d* ending with a *.conf* file extension. 29 | 30 | ## Inside the container the uid and gid is 101. The folder owner can be set with 31 | ## `sudo chown -R 101:101 ./nginx` if needed. 32 | ## Note that this repository requires nginx version 1.25.1 or later 33 | NGINX_IMAGE_TAG=alpine 34 | 35 | ## The folder containing server blocks and any additional config to nginx.conf 36 | NGINX_CONFIG_PATH=./nginx/conf.d 37 | NGINX_DHPARAMS_FILE=./nginx/dhparams4096.pem 38 | 39 | CERT_PATH=./volumes/web/cert/cert.pem 40 | KEY_PATH=./volumes/web/cert/key-no-password.pem 41 | #GITLAB_PKI_CHAIN_PATH=/pki_chain.pem 42 | #CERT_PATH=./certs/etc/letsencrypt/live/${DOMAIN}/fullchain.pem 43 | #KEY_PATH=./certs/etc/letsencrypt/live/${DOMAIN}/privkey.pem 44 | 45 | ## Exposed ports to the host. Inside the container 80, 443 and 8443 will be used 46 | HTTPS_PORT=443 47 | HTTP_PORT=80 48 | CALLS_PORT=8443 49 | 50 | # Mattermost settings 51 | ## Inside the container the uid and gid is 2000. The folder owner can be set with 52 | ## `sudo chown -R 2000:2000 ./volumes/app/mattermost`. 53 | MATTERMOST_CONFIG_PATH=./volumes/app/mattermost/config 54 | MATTERMOST_DATA_PATH=./volumes/app/mattermost/data 55 | MATTERMOST_LOGS_PATH=./volumes/app/mattermost/logs 56 | MATTERMOST_PLUGINS_PATH=./volumes/app/mattermost/plugins 57 | MATTERMOST_CLIENT_PLUGINS_PATH=./volumes/app/mattermost/client/plugins 58 | MATTERMOST_BLEVE_INDEXES_PATH=./volumes/app/mattermost/bleve-indexes 59 | 60 | ## Bleve index (inside the container) 61 | MM_BLEVESETTINGS_INDEXDIR=/mattermost/bleve-indexes 62 | 63 | ## This will be 'mattermost-enterprise-edition' or 'mattermost-team-edition' based on the version of Mattermost you're installing. 64 | MATTERMOST_IMAGE=mattermost-enterprise-edition 65 | ## Update the image tag if you want to upgrade your Mattermost version. You may also upgrade to the latest one. The example is based on the latest Mattermost ESR version. 66 | MATTERMOST_IMAGE_TAG=10.5.2 67 | 68 | ## Make Mattermost container readonly. This interferes with the regeneration of root.html inside the container. Only use 69 | ## it if you know what you're doing. 70 | ## See https://github.com/mattermost/docker/issues/18 71 | MATTERMOST_CONTAINER_READONLY=false 72 | 73 | ## The app port is only relevant for using Mattermost without the nginx container as reverse proxy. This is not meant 74 | ## to be used with the internal HTTP server exposed but rather in case one wants to host several services on one host 75 | ## or for using it behind another existing reverse proxy. 76 | APP_PORT=8065 77 | 78 | ## Configuration settings for Mattermost. Documentation on the variables and the settings itself can be found at 79 | ## https://docs.mattermost.com/administration/config-settings.html 80 | ## Keep in mind that variables set here will take precedence over the same setting in config.json. This includes 81 | ## the system console as well and settings set with env variables will be greyed out. 82 | 83 | ## Below one can find necessary settings to spin up the Mattermost container 84 | MM_SQLSETTINGS_DRIVERNAME=postgres 85 | MM_SQLSETTINGS_DATASOURCE=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable&connect_timeout=10 86 | 87 | ## Example settings (any additional setting added here also needs to be introduced in the docker-compose.yml) 88 | MM_SERVICESETTINGS_SITEURL=https://${DOMAIN} 89 | -------------------------------------------------------------------------------- /nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | # mattermost 2 | # config can be tested on https://www.ssllabs.com/ssltest/ and a good nginx config generator 3 | # can be found at https://ssl-config.mozilla.org/ 4 | 5 | # proxy cache 6 | proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mattermost_cache:10m max_size=3g inactive=120m use_temp_path=off; 7 | 8 | # upstream used in proxy_pass below 9 | upstream backend { 10 | # ip where Mattermost is running; this relies on a working DNS inside the Docker network 11 | # and uses the hostname of the mattermost container (see service name in docker-compose.yml) 12 | server mattermost:8065; 13 | keepalive 64; 14 | } 15 | 16 | # vhosts definitions 17 | server { 18 | server_name _; 19 | listen 80 default_server; 20 | listen [::]:80 default_server; 21 | 22 | # redirect all HTTP requests to HTTPS with a 301 Moved Permanently response. 23 | return 301 https://$host$request_uri; 24 | } 25 | 26 | server { 27 | server_name _; 28 | listen 443 ssl default_server; 29 | listen [::]:443 ssl default_server; 30 | http2 on; 31 | 32 | # logging 33 | access_log /var/log/nginx/mm.access.log; 34 | error_log /var/log/nginx/mm.error.log warn; 35 | 36 | # gzip for performance 37 | gzip on; 38 | gzip_vary on; 39 | gzip_proxied any; 40 | gzip_comp_level 6; 41 | gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; 42 | 43 | ## ssl 44 | ssl_dhparam /dhparams4096.pem; 45 | ssl_session_timeout 1d; 46 | ssl_session_cache shared:MozSSL:10m; 47 | ssl_session_tickets off; 48 | 49 | # intermediate configuration 50 | ssl_protocols TLSv1.2 TLSv1.3; 51 | ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; 52 | ssl_prefer_server_ciphers off; 53 | 54 | # certs sent to the client in SERVER HELLO are concatenated in ssl_certificate 55 | ssl_certificate /cert.pem; 56 | ssl_certificate_key /key.pem; 57 | 58 | # enable TLSv1.3's 0-RTT. Use $ssl_early_data when reverse proxying to prevent replay attacks. 59 | # https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data 60 | ssl_early_data on; 61 | 62 | # OCSP stapling 63 | ssl_stapling on; 64 | ssl_stapling_verify on; 65 | #resolver 1.1.1.1; 66 | 67 | # verify chain of trust of OCSP response using Root CA and Intermediate certs 68 | #ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; 69 | 70 | ## security headers 71 | # https://securityheaders.com/ 72 | # https://scotthelme.co.uk/tag/security-headers/ 73 | add_header X-Frame-Options "SAMEORIGIN" always; 74 | add_header X-XSS-Protection "1; mode=block" always; 75 | add_header X-Content-Type-Options "nosniff" always; 76 | add_header Referrer-Policy no-referrer; 77 | add_header Strict-Transport-Security "max-age=63072000" always; 78 | add_header Permissions-Policy "interest-cohort=()"; 79 | 80 | ## locations 81 | # ACME-challenge 82 | location ^~ /.well-known { 83 | default_type "text/plain"; 84 | root /usr/share/nginx/html; 85 | allow all; 86 | } 87 | 88 | # disable Google bots from indexing this site 89 | add_header X-Robots-Tag "noindex"; 90 | 91 | location ~ /api/v[0-9]+/(users/)?websocket$ { 92 | proxy_set_header Upgrade $http_upgrade; 93 | proxy_set_header Connection "upgrade"; 94 | client_max_body_size 50M; 95 | proxy_set_header Host $http_host; 96 | proxy_set_header X-Real-IP $remote_addr; 97 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 98 | proxy_set_header X-Forwarded-Proto $scheme; 99 | proxy_set_header X-Frame-Options SAMEORIGIN; 100 | proxy_set_header Early-Data $ssl_early_data; 101 | proxy_buffers 256 16k; 102 | proxy_buffer_size 16k; 103 | client_body_timeout 60; 104 | send_timeout 300; 105 | lingering_timeout 5; 106 | proxy_connect_timeout 90; 107 | proxy_send_timeout 300; 108 | proxy_read_timeout 90s; 109 | proxy_http_version 1.1; 110 | proxy_pass http://backend; 111 | } 112 | 113 | location / { 114 | client_max_body_size 50M; 115 | proxy_set_header Connection ""; 116 | proxy_set_header Host $http_host; 117 | proxy_set_header X-Real-IP $remote_addr; 118 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 119 | proxy_set_header X-Forwarded-Proto $scheme; 120 | proxy_set_header X-Frame-Options SAMEORIGIN; 121 | proxy_set_header Early-Data $ssl_early_data; 122 | proxy_buffers 256 16k; 123 | proxy_buffer_size 16k; 124 | proxy_read_timeout 600s; 125 | proxy_cache mattermost_cache; 126 | proxy_cache_revalidate on; 127 | proxy_cache_min_uses 2; 128 | proxy_cache_use_stale timeout; 129 | proxy_cache_lock on; 130 | proxy_http_version 1.1; 131 | proxy_pass http://backend; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /nginx/dhparams4096.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIICCAKCAgEAj9mn32DwQTAzqQqtvRwzeh2Yxg0UHFmoejLds9qYxnrxS9SWl8OJ 3 | rpx9xo1hb077DnfQ9RE9Dr/p5T4B7y7RhSfNBYqLt6nxTkHgUjvQBQI4K6Rfz6iQ 4 | G2miTrAatwQ2PSZcMPNme4FVh5USe2ppjYl+EqLUex0hNyxcV9U8PdG/TFcaje6g 5 | dgc3SQU6zSk+YK9aKfEFwO4YbfJbrun+t1UIoMtWAAicrF3vygzCRx02/uzGUvlf 6 | pvtsjYwcXFizU5rd+JQ/jomEO5lUUp4FuvlzcT6orPeJe5afKrxG092yHp+qgdCg 7 | QrmrWy9RZFzRxoHPzW1zreinUvqP8qemCBX4J+qgxtqGM+c6aTDsfq5G/YWmVeYT 8 | ue2pFhX+lWBDsPELvzUbuceehmCWjdoII0iDoUHqRqMiZVMjVLiVtd1YhNa41tWZ 9 | kO+EhZkg8rPC/7oqehEx2GOXfjZzA/8+t1YCbhAsL4/wpbuynVgZ3TNR+nVfKs5d 10 | 2Bht+dWfSNpuYyK6oDerycnDRF/cmEGHg0E419H9UPAHktS9ZYGhq7nIgrBiuE1P 11 | PO01p/rXYKGaDqFAK3F86aGxj0+2Am5TfSMiSNobDMp61IRt0P/T0vp2ZE5wpzS+ 12 | 39QGrQpJ8phAr8PE7Q9bo4Z+qabBn+aJkAsFH+M4IkX0JYEjk3wpMssCAQI= 13 | -----END DH PARAMETERS----- 14 | -------------------------------------------------------------------------------- /scripts/UPGRADE.md: -------------------------------------------------------------------------------- 1 | # IMPORTANT: Please make sure you have enough disk space available for the backups! 2 | Because it is more complicated to check the available disk space for various disk formatting options provided by different linux distributions, the script does currently not check for if there is enough disk space. 3 | Please check manually before executing this script! 4 | 5 | ## Upgrading Postgres 6 | 7 | ``` 8 | $ export PATH_TO_MATTERMOST_DOCKER=path/to/mattermost-docker 9 | $ ./scripts/upgrade-postgres.sh 10 | ``` 11 | 12 | Environment variables for upgrading: 13 | `ttf` means, the script 'tries to find' the environment variables. 14 | 15 | | Name | Description | Type | Default | Required | 16 | |------|-------------|------|:---------:|:--------:| 17 | | PATH_TO_MATTERMOST_DOCKER | absolute path to your mattermost-docker folder | `string` | n/a | yes | 18 | | POSTGRES_USER | postgres user to connect to the mattermost database | `string` | ttf | yes | 19 | | POSTGRES_PASSWORD | postgres password for the POSTGRES_USER to connect to the mattermost database | `string` | ttf | yes | 20 | | POSTGRES_DB | postgres database name for the mattermost database | `string` | ttf | yes | 21 | | POSTGRES_OLD_VERSION | postgres database old version which should be upgraded from | `semver` | ttf | yes | 22 | | POSTGRES_NEW_VERSION | postgres database new version which should be upgraded to | `semver` | 13 | yes | 23 | | POSTGRES_DOCKER_TAG | postgres docker tag found [here](https://hub.docker.com/_/postgres) including python3-dev | `string` | 13.2-alpine | yes | 24 | | POSTGRES_OLD_DOCKER_FROM | FROM declaration in the postgres Dockerfile to be replaced | `string` | ttf | yes | 25 | | POSTGRES_NEW_DOCKER_FROM | FROM declaration in the postgres Dockerfile replacing POSTGRES_OLD_DOCKER_FROM | `string` | ttf | yes | 26 | | POSTGRES_UPGRADE_LINE | folder name required to upgrade postgres (Needs to match a folder [here](https://github.com/tianon/docker-postgres-upgrade)) | `string` | ttf | yes | 27 | | MM_OLD_VERSION | mattermost old version which should be upgraded from | `semver` | ttf | yes | 28 | | MM_NEW_VERSION | mattermost new version which should be upgraded to | `semver` | 5.32.1 | yes | 29 | 30 | You can overwrite any of these variables before running this script with: 31 | ``` 32 | $ export VAR_NAME_FROM_ABOVE=yourValue 33 | $ export PATH_TO_MATTERMOST_DOCKER=path/to/mattermost-docker 34 | $ ./scripts/upgrade-postgres.sh 35 | ``` 36 | -------------------------------------------------------------------------------- /scripts/issue-certificate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | cat < <-o PATH> 6 | 7 | Options 8 | -h Print this help 9 | -o Output path (e.g. \${PWD}/certs) 10 | -d Domain certificate is issued for (e.g. mm.example.com) 11 | 12 | EOF 13 | } 14 | 15 | issue_cert_standalone() { 16 | docker run -it --rm --name certbot -p 80:80 \ 17 | -v "${1}/etc/letsencrypt:/etc/letsencrypt" \ 18 | -v "${1}/lib/letsencrypt:/var/lib/letsencrypt" \ 19 | certbot/certbot certonly --standalone -d "${2}" 20 | } 21 | 22 | authenticator_to_webroot() { 23 | sed -i 's/standalone/webroot/' "${1}"/etc/letsencrypt/renewal/"${2}".conf 24 | tee -a "${1}"/etc/letsencrypt/renewal/"${2}".conf >/dev/null <&2 51 | exit 64 52 | ;; 53 | esac 54 | done 55 | 56 | shift $((OPTIND - 1)) 57 | 58 | if [ -z "$domain" ]; then 59 | echo "-d is required" >&2 60 | usage >&2 61 | exit 64 62 | fi 63 | 64 | if [ -z "$output" ]; then 65 | echo "-o is required" >&2 66 | usage >&2 67 | exit 64 68 | fi 69 | 70 | if ! which docker 1>/dev/null; then 71 | echo "Can't find Docker command" >&2 72 | exit 64 73 | fi 74 | 75 | issue_cert_standalone "${output}" "${domain}" 76 | authenticator_to_webroot "${output}" "${domain}" 77 | -------------------------------------------------------------------------------- /scripts/upgrade-postgres.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | 5 | ## 6 | ## Instructions 7 | ## 8 | # Dockerfile stolen from contributions in this issue: https://github.com/mattermost/mattermost-docker/issues/489#issuecomment-790277661 9 | 10 | # 1. Edit the variables below to match your environment. This uses default variables and assumes you're on 5.31.0. 11 | # If you're wanting to use another version of Postgres/Mattermost , update the variables as desired. 12 | 13 | # 2. run 'sudo bash upgrade-postgres.sh' replace upgrade.sh with what you've named the file. 14 | # This may take some time to complete as it's migrating the database to Postgres 13.6 from 9.4 15 | 16 | 17 | if [[ $PATH_TO_MATTERMOST_DOCKER == "" ]]; then 18 | # shellcheck disable=SC2016 19 | echo 'Please export environment variable PATH_TO_MATTERMOST_DOCKER with "$ export PATH_TO_MATTERMOST_DOCKER=/path/to/mattermost-docker", i.e. $PWD before running this script. ' 20 | exit 1 21 | fi 22 | 23 | ## 24 | ## Environment Variables 25 | ## 26 | # Below are default values in the mattermost-docker container. 27 | # The script is trying to fetch those variables first. Should fetching fail, please export the variables before running the script. 28 | if [[ $POSTGRES_USER == "" ]]; then 29 | echo "trying to fetch POSTGRES_USER from $PATH_TO_MATTERMOST_DOCKER/docker-compose.yml" 30 | POSTGRES_USER=$(grep "^.*-.*POSTGRES_USER=.*$" "$PATH_TO_MATTERMOST_DOCKER"/docker-compose.yml | sed s~^.*-.*POSTGRES_USER=~~g) 31 | if [[ $POSTGRES_USER == "" ]]; then 32 | echo "could not find POSTGRES_USER set in $PATH_TO_MATTERMOST_DOCKER/docker-compose.yml" 33 | echo "please run 'export POSTGRES_USER=yourPostgresUser' before running this script" 34 | exit 1 35 | fi 36 | echo "found POSTGRES_USER=redacted" 37 | fi 38 | 39 | if [[ $POSTGRES_PASSWORD == "" ]]; then 40 | echo "trying to fetch POSTGRES_PASSWORD from $PATH_TO_MATTERMOST_DOCKER/docker-compose.yml" 41 | POSTGRES_PASSWORD=$(grep "^.*-.*POSTGRES_PASSWORD=.*$" "$PATH_TO_MATTERMOST_DOCKER"/docker-compose.yml | sed s~^.*-.*POSTGRES_PASSWORD=~~g) 42 | if [[ $POSTGRES_PASSWORD == "" ]]; then 43 | echo "could not find POSTGRES_PASSWORD set in $PATH_TO_MATTERMOST_DOCKER/docker-compose.yml" 44 | echo "please run 'export POSTGRES_PASSWORD=yourPostgresPassword' before running this script" 45 | exit 1 46 | fi 47 | echo "found POSTGRES_PASSWORD=redacted" 48 | fi 49 | 50 | if [[ $POSTGRES_DB == "" ]]; then 51 | echo "trying to fetch POSTGRES_DB from $PATH_TO_MATTERMOST_DOCKER/docker-compose.yml" 52 | POSTGRES_DB=$(grep "^.*-.*POSTGRES_DB=.*$" "$PATH_TO_MATTERMOST_DOCKER"/docker-compose.yml | sed s~^.*-.*POSTGRES_DB=~~g) 53 | if [[ $POSTGRES_DB == "" ]]; then 54 | echo "could not find POSTGRES_DB set in $PATH_TO_MATTERMOST_DOCKER/docker-compose.yml" 55 | echo "please run 'export POSTGRES_DB=yourPostgresDatabase' before running this script" 56 | exit 1 57 | fi 58 | echo "found POSTGRES_DB=$POSTGRES_DB" 59 | fi 60 | 61 | printf "\n" 62 | if [[ $POSTGRES_OLD_VERSION == "" ]]; then 63 | echo "trying to fetch POSTGRES_OLD_VERSION by connecting to database container and echoing the environment variable PG_VERSION" 64 | POSTGRES_OLD_VERSION=$(docker exec mattermost-docker_db_1 bash -c 'echo $PG_VERSION') # i.e. 9.4 65 | if [[ $POSTGRES_OLD_VERSION == "" ]]; then 66 | echo "could not connect to database container to get PG_VERSION" 67 | echo "please run 'export POSTGRES_OLD_VERSION=i.e. 9.4' before running this script" 68 | echo "check by i.e. running 'sudo cat $PATH_TO_MATTERMOST_DOCKER/volumes/db/var/lib/postgresql/data/PG_VERSION'" 69 | exit 1 70 | fi 71 | echo "found POSTGRES_OLD_VERSION=$POSTGRES_OLD_VERSION" 72 | fi 73 | 74 | if [[ $POSTGRES_NEW_VERSION == "" ]]; then 75 | echo "no exported POSTGRES_NEW_VERSION environment variable found" 76 | echo "setting POSTGRES_NEW_VERSION environment variable to default 13" 77 | POSTGRES_NEW_VERSION=13 # i.e. 13 78 | echo "set POSTGRES_NEW_VERSION=$POSTGRES_NEW_VERSION" 79 | fi 80 | 81 | 82 | if [[ $POSTGRES_DOCKER_TAG == "" ]]; then 83 | echo "no exported POSTGRES_DOCKER_TAG environment variable found" 84 | echo "setting POSTGRES_DOCKER_TAG environment variable to default 13.2-alpine" 85 | echo "tag needs to be an alpine release to include python3-dev found here - https://hub.docker.com/_/postgres" 86 | POSTGRES_DOCKER_TAG=13.2-alpine # i.e. '13.2-alpine' 87 | echo "set POSTGRES_DOCKER_TAG=$POSTGRES_DOCKER_TAG" 88 | fi 89 | 90 | if [[ $POSTGRES_OLD_DOCKER_FROM == "" ]]; then 91 | echo "no exported POSTGRES_OLD_DOCKER_FROM environment variable found" 92 | echo "setting POSTGRES_OLD_DOCKER_FROM to default '$(grep 'FROM postgres' "$PATH_TO_MATTERMOST_DOCKER"/db/Dockerfile)'" 93 | POSTGRES_OLD_DOCKER_FROM=$(grep 'FROM postgres' "$PATH_TO_MATTERMOST_DOCKER/db/Dockerfile") 94 | echo "set POSTGRES_OLD_DOCKER_FROM=$POSTGRES_OLD_DOCKER_FROM" 95 | fi 96 | 97 | if [[ $POSTGRES_NEW_DOCKER_FROM == "" ]]; then 98 | echo "no exported POSTGRES_NEW_DOCKER_FROM environment variable found" 99 | echo "setting POSTGRES_NEW_DOCKER_FROM to default 'FROM postgres:$POSTGRES_DOCKER_TAG'" 100 | POSTGRES_NEW_DOCKER_FROM="FROM postgres:$POSTGRES_DOCKER_TAG" 101 | echo "set POSTGRES_NEW_DOCKER_FROM=$POSTGRES_NEW_DOCKER_FROM" 102 | fi 103 | 104 | if [[ $POSTGRES_UPGRADE_LINE == "" ]]; then 105 | echo "no exported POSTGRES_UPGRADE_LINE environment variable found" 106 | echo "setting POSTGRES_UPGRADE_LINE to default $POSTGRES_OLD_VERSION-to-$POSTGRES_NEW_VERSION" 107 | echo "the POSTGRES_UPGRADE_LINE needs to match a folder found here - https://github.com/tianon/docker-postgres-upgrade" 108 | echo "it should read 'old-to-new'" 109 | POSTGRES_UPGRADE_LINE=$POSTGRES_OLD_VERSION-to-$POSTGRES_NEW_VERSION # i.e. '9.4-to-13' 110 | echo "set POSTGRES_UPGRADE_LINE=$POSTGRES_UPGRADE_LINE" 111 | fi 112 | 113 | printf "\n" 114 | if [[ $MM_OLD_VERSION == "" ]]; then 115 | echo "trying to fetch MM_OLD_VERSION from $PATH_TO_MATTERMOST_DOCKER/docker-compose.yml" 116 | MM_OLD_VERSION=$(grep ".*-.*MM_VERSION=.*" "$PATH_TO_MATTERMOST_DOCKER"/docker-compose.yml | sed s~.*-.*MM_VERSION=~~g) 117 | if [[ $MM_OLD_VERSION == "" ]]; then 118 | echo "could not find MM_OLD_VERSION set in $PATH_TO_MATTERMOST_DOCKER/docker-compose.yml" 119 | echo "please run 'export MM_OLD_VERSION=yourMMVersion' before running this script" 120 | exit 1 121 | fi 122 | echo "found MM_OLD_VERSION=$MM_OLD_VERSION" 123 | fi 124 | 125 | if [[ $MM_NEW_VERSION == "" ]]; then 126 | echo "no exported MM_NEW_VERSION environment variable found" 127 | echo "setting MM_NEW_VERSION to default 5.32.1" 128 | MM_NEW_VERSION=5.32.1 129 | echo "found MM_NEW_VERSION=$MM_NEW_VERSION" 130 | fi 131 | 132 | printf "\n" 133 | echo "Path to mattermost-docker: $PATH_TO_MATTERMOST_DOCKER" 134 | echo "Postgres user: redacted" 135 | echo "Postgres password: redacted" 136 | echo "Postgres database name: $POSTGRES_DB" 137 | echo "Postgres old version: $POSTGRES_OLD_VERSION" 138 | echo "Postgres new version: $POSTGRES_NEW_VERSION" 139 | echo "Postgres alpine docker tag including python3-dev: $POSTGRES_DOCKER_TAG" 140 | echo "Postgres old Dockerfile: $POSTGRES_OLD_DOCKER_FROM" 141 | echo "Postgres new Dockerfile: $POSTGRES_NEW_DOCKER_FROM" 142 | echo "Postgres upgrade-line matches a folder here - https://github.com/tianon/docker-postgres-upgrade: $POSTGRES_UPGRADE_LINE" 143 | echo "Mattermost old version: $MM_OLD_VERSION" 144 | echo "Mattermost new version: $MM_NEW_VERSION" 145 | printf "\n" 146 | df -h 147 | read -rp "Please make sure you have enough disk space left on your devices. Try to backup and upgrade now? (y/n)" choice 148 | if [[ "$choice" != "y" && "$choice" != "Y" && "$choice" != "yes" ]]; then 149 | exit 0; 150 | fi 151 | 152 | ## 153 | ## Script Start 154 | ## 155 | cd "$PATH_TO_MATTERMOST_DOCKER" 156 | docker-compose stop 157 | 158 | # Creating a backup folder and backing up the mattermost / database. 159 | mkdir "$PATH_TO_MATTERMOST_DOCKER"/backups 160 | DATE=$(date +'%F-%H-%M') 161 | cp -ra "$PATH_TO_MATTERMOST_DOCKER"/volumes/app/mattermost/ "$PATH_TO_MATTERMOST_DOCKER"/backups/mattermost-backup-"$DATE"/ 162 | cp -ra "$PATH_TO_MATTERMOST_DOCKER"/volumes/db/ "$PATH_TO_MATTERMOST_DOCKER"/backups/database-backup-"$DATE"/ 163 | 164 | mkdir "$PATH_TO_MATTERMOST_DOCKER"/volumes/db/"$POSTGRES_OLD_VERSION" 165 | mv "$PATH_TO_MATTERMOST_DOCKER"/volumes/db/var/lib/postgresql/data/ "$PATH_TO_MATTERMOST_DOCKER"/volumes/db/"$POSTGRES_OLD_VERSION" 166 | rm -rf "$PATH_TO_MATTERMOST_DOCKER"/volumes/db/var 167 | mkdir -p "$PATH_TO_MATTERMOST_DOCKER"/volumes/db/"$POSTGRES_NEW_VERSION"/data 168 | 169 | 170 | sed -i "s/$POSTGRES_OLD_DOCKER_FROM/$POSTGRES_NEW_DOCKER_FROM/" "$PATH_TO_MATTERMOST_DOCKER"/db/Dockerfile 171 | sed -i "s/python-dev/python3-dev/" "$PATH_TO_MATTERMOST_DOCKER"/db/Dockerfile 172 | sed -i "s/$MM_OLD_VERSION/$MM_NEW_VERSION/" "$PATH_TO_MATTERMOST_DOCKER"/app/Dockerfile 173 | 174 | 175 | # replacing the old postgres path with a new path 176 | sed -i "s#./volumes/db/var/lib/postgresql/data:/var/lib/postgresql/data#./volumes/db/$POSTGRES_NEW_VERSION/data:/var/lib/postgresql/data#" "$PATH_TO_MATTERMOST_DOCKER"/docker-compose.yml 177 | 178 | # migrate the database to the new postgres version 179 | docker run --rm \ 180 | -e PGUSER="$POSTGRES_USER" \ 181 | -e POSTGRES_INITDB_ARGS=" -U $POSTGRES_USER" \ 182 | -e POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \ 183 | -e POSTGRES_DB="$POSTGRES_DB" \ 184 | -v "$PATH_TO_MATTERMOST_DOCKER"/volumes/db:/var/lib/postgresql \ 185 | tianon/postgres-upgrade:"$POSTGRES_UPGRADE_LINE" \ 186 | --link 187 | 188 | cp -p "$PATH_TO_MATTERMOST_DOCKER"/volumes/db/"$POSTGRES_OLD_VERSION"/data/pg_hba.conf "$PATH_TO_MATTERMOST_DOCKER"/volumes/db/"$POSTGRES_NEW_VERSION"/data/ 189 | 190 | # rebuild the containers 191 | docker-compose build 192 | docker-compose up -d 193 | 194 | # reindex the database 195 | echo "REINDEX SCHEMA CONCURRENTLY public;" | docker exec mattermost-docker_db_1 psql -U "$POSTGRES_USER" "$POSTGRES_DB" 196 | cd - 197 | --------------------------------------------------------------------------------