├── .circleci └── config.yml ├── .dockerignore ├── .github ├── FUNDING.yml └── pull_request_template.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── UPGRADING.md ├── certbot-deploy-hook ├── custom_zulip_files └── README.custom_zulip_files.md ├── docker-compose.yml ├── entrypoint.sh ├── kubernetes ├── chart │ └── zulip │ │ ├── .helmignore │ │ ├── CHANGELOG.md │ │ ├── Chart.lock │ │ ├── Chart.yaml │ │ ├── README.md │ │ ├── README.md.gotmpl │ │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── cm-post-setup-scripts.yaml │ │ ├── ingress.yaml │ │ ├── pvc.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── statefulset.yaml │ │ ├── values-local.yaml.example │ │ └── values.yaml └── manual │ ├── zulip-rc.yml │ └── zulip-svc.yml └── upgrade-postgresql /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | orbs: 3 | shellcheck: circleci/shellcheck@1.3.11 4 | workflows: 5 | shellcheck: 6 | jobs: 7 | - shellcheck/check 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .gitignore 3 | AUTHORS 4 | LICENSE 5 | README 6 | README.md 7 | TODO 8 | TODO.md 9 | kubernetes/ 10 | scripts/ 11 | docker-compose.yml 12 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: zulip 2 | patreon: zulip 3 | open_collective: zulip 4 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fixes: 4 | 5 | 9 | 10 | **How did you test this PR?** 11 | 12 |
13 | Self-review checklist 14 | 15 | 17 | 18 | 20 | 21 | - [ ] [Self-reviewed](https://zulip.readthedocs.io/en/latest/contributing/code-reviewing.html#how-to-review-code) the changes for clarity and maintainability 22 | (variable names, code reuse, readability, etc.). 23 | 24 | Communicate decisions, questions, and potential concerns. 25 | 26 | - [ ] Explains differences from previous plans (e.g., issue description). 27 | - [ ] Highlights technical choices and bugs encountered. 28 | - [ ] Calls out remaining decisions and concerns. 29 | - [ ] Automated tests verify logic where appropriate. 30 | 31 | Individual commits are ready for review (see [commit discipline](https://zulip.readthedocs.io/en/latest/contributing/commit-discipline.html)). 32 | 33 | - [ ] Each commit is a coherent idea. 34 | - [ ] Commit message(s) explain reasoning and motivation for changes. 35 | 36 | Completed manual review and testing of the following: 37 | 38 | - [ ] Visual appearance of the changes. 39 | - [ ] Responsiveness and internationalization. 40 | - [ ] Strings and tooltips. 41 | - [ ] End-to-end functionality of buttons, interactions and flows. 42 | - [ ] Corner cases, error conditions, and easily imagined bugs. 43 |
44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | .DS_Store 3 | 4 | # Various IDEs 5 | .project 6 | .idea/ 7 | *.tmproj 8 | 9 | # dev files 10 | docker-compose-dev.yml 11 | kubernetes/*-dev.yml 12 | kubernetes/chart/zulip/values-local.yaml 13 | kubernetes/chart/zulip/charts/ 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at Galexrt@googlemail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # This is a 2-stage Docker build. In the first stage, we build a 2 | # Zulip development environment image and use 3 | # tools/build-release-tarball to generate a production release tarball 4 | # from the provided Git ref. 5 | FROM ubuntu:24.04 AS base 6 | 7 | # Set up working locales and upgrade the base image 8 | ENV LANG="C.UTF-8" 9 | 10 | ARG UBUNTU_MIRROR 11 | 12 | RUN { [ ! "$UBUNTU_MIRROR" ] || sed -i "s|http://\(\w*\.\)*archive\.ubuntu\.com/ubuntu/\? |$UBUNTU_MIRROR |" /etc/apt/sources.list; } && \ 13 | apt-get -q update && \ 14 | apt-get -q dist-upgrade -y && \ 15 | DEBIAN_FRONTEND=noninteractive \ 16 | apt-get -q install --no-install-recommends -y ca-certificates git locales python3 sudo tzdata && \ 17 | touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu && \ 18 | useradd -d /home/zulip -m zulip -u 1000 19 | 20 | FROM base AS build 21 | 22 | RUN echo 'zulip ALL=(ALL:ALL) NOPASSWD:ALL' >> /etc/sudoers 23 | 24 | USER zulip 25 | WORKDIR /home/zulip 26 | 27 | # You can specify these in docker-compose.yml or with 28 | # docker build --build-arg "ZULIP_GIT_REF=git_branch_name" . 29 | ARG ZULIP_GIT_URL=https://github.com/zulip/zulip.git 30 | ARG ZULIP_GIT_REF=10.3 31 | 32 | RUN git clone "$ZULIP_GIT_URL" && \ 33 | cd zulip && \ 34 | git checkout -b current "$ZULIP_GIT_REF" 35 | 36 | WORKDIR /home/zulip/zulip 37 | 38 | ARG CUSTOM_CA_CERTIFICATES 39 | 40 | # Finally, we provision the development environment and build a release tarball 41 | RUN SKIP_VENV_SHELL_WARNING=1 ./tools/provision --build-release-tarball-only && \ 42 | uv run --no-sync ./tools/build-release-tarball docker && \ 43 | mv /tmp/tmp.*/zulip-server-docker.tar.gz /tmp/zulip-server-docker.tar.gz 44 | 45 | 46 | # In the second stage, we build the production image from the release tarball 47 | FROM base 48 | 49 | ENV DATA_DIR="/data" 50 | 51 | # Then, with a second image, we install the production release tarball. 52 | COPY --from=build /tmp/zulip-server-docker.tar.gz /root/ 53 | COPY custom_zulip_files/ /root/custom_zulip 54 | 55 | ARG CUSTOM_CA_CERTIFICATES 56 | 57 | RUN \ 58 | # Make sure Nginx is started by Supervisor. 59 | dpkg-divert --add --rename /etc/init.d/nginx && \ 60 | ln -s /bin/true /etc/init.d/nginx && \ 61 | mkdir -p "$DATA_DIR" && \ 62 | cd /root && \ 63 | tar -xf zulip-server-docker.tar.gz && \ 64 | rm -f zulip-server-docker.tar.gz && \ 65 | mv zulip-server-docker zulip && \ 66 | cp -rf /root/custom_zulip/* /root/zulip && \ 67 | rm -rf /root/custom_zulip && \ 68 | /root/zulip/scripts/setup/install --hostname="$(hostname)" --email="docker-zulip" \ 69 | --puppet-classes="zulip::profile::docker" --postgresql-version=14 && \ 70 | rm -f /etc/zulip/zulip-secrets.conf /etc/zulip/settings.py && \ 71 | apt-get -qq autoremove --purge -y && \ 72 | apt-get -qq clean && \ 73 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 74 | 75 | COPY entrypoint.sh /sbin/entrypoint.sh 76 | COPY certbot-deploy-hook /sbin/certbot-deploy-hook 77 | 78 | VOLUME ["$DATA_DIR"] 79 | EXPOSE 80 443 80 | 81 | ENTRYPOINT ["/sbin/entrypoint.sh"] 82 | CMD ["app:run"] 83 | -------------------------------------------------------------------------------- /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 2016 Alexander Trost 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 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zulip Docker image overview 2 | 3 | [![](https://images.microbadger.com/badges/image/zulip/docker-zulip.svg)](https://microbadger.com/images/zulip/docker-zulip "Get your own image badge on microbadger.com") [![**docker-zulip** stream](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://chat.zulip.org/#narrow/stream/backend/topic/docker) 4 | 5 | `docker-zulip` is the official Docker container image for running a 6 | [Zulip server](https://zulip.com) in 7 | [production][prod-overview]. Built images are available from: [Docker 8 | Hub](https://hub.docker.com/r/zulip/docker-zulip): 9 | 10 | ```console 11 | $ docker pull zulip/docker-zulip:10.3-0 12 | ``` 13 | 14 | Current Zulip version: `10.3` 15 | Current Docker image version: `10.3-0` 16 | 17 | We recommend using the Docker image if your organization has a 18 | preference for deploying services using Docker. Deploying with Docker 19 | moderately increases the effort required to install, maintain, and 20 | upgrade a Zulip installation, compared with the [standard Zulip 21 | installer][normal-install]. 22 | 23 | [normal-install]: https://zulip.readthedocs.io/en/latest/production/install.html 24 | [zulip-architecture]: https://zulip.readthedocs.io/en/latest/overview/architecture-overview.html 25 | 26 | ## Prerequisites 27 | 28 | To use `docker-zulip`, you need the following: 29 | 30 | - An installation of [Docker][install-docker] and 31 | [Docker Compose][install-docker-compose] or a Kubernetes runtime 32 | engine. 33 | - We [recommend at least 2GB of available RAM][prod-requirements] for 34 | running a production Zulip server; you'll want 4GB if you're 35 | building the container (rather than using the pre-built images). If 36 | you're just testing and/or aren't expecting a lot of users/messages, 37 | you can get away with significantly less especially for the 38 | `postgres`, `memcached`, etc. containers, because Docker makes it 39 | easy to sharply limit the RAM allocated to the services Zulip 40 | depends on, like Redis, memcached, and PostgreSQL (at the cost of 41 | potential performance issues). 42 | - This project doesn't support `docker-rootless`; Zulip needs root 43 | access to set properties like the maximum number of open file 44 | descriptions via `ulimit` (which is important for it to handle 45 | thousands of connected clients). 46 | 47 | If you aren't already a Docker expert, we recommend starting by 48 | reading our brief overview of how Docker and containers work in the 49 | next section for important background that the rest of this 50 | documentation will assume. 51 | 52 | Otherwise, you can jump to our documentation for your preferred 53 | container runtime: 54 | 55 | - [Docker Compose](#running-a-zulip-server-with-docker-compose) 56 | - [Kubernetes](#running-a-zulip-server-with-kubernetes) 57 | 58 | [install-docker]: https://docs.docker.com/install/ 59 | [install-docker-compose]: https://docs.docker.com/compose/install/ 60 | [prod-overview]: https://zulip.readthedocs.io/en/latest/production/overview.html 61 | [prod-requirements]: https://zulip.readthedocs.io/en/latest/production/requirements.html 62 | 63 | ## The Docker data storage model 64 | 65 | Docker and other container systems are built around shareable 66 | container images. An image is a read-only template with instructions 67 | for creating a container. 68 | 69 | Often, an image is based on another image, with a bit of additional 70 | customization. For example, Zulip's `zulip-postgresql` image extends 71 | the standard `postgresql` image (by installing a couple `postgres` 72 | extensions). Meanwhile, the `zulip` image is built on top of a 73 | standard `ubuntu` image, adding all the code for a Zulip 74 | application/web server. 75 | 76 | Every time you boot a container based on a given image, it's like 77 | booting off a CD-ROM: you get the exact same image (and anything 78 | written to the image's filesystem is lost). To handle persistent 79 | state that needs to persist after the Docker equivalent of a reboot or 80 | upgrades (like uploaded files or the Zulip database), container 81 | systems let you configure certain directories inside the container 82 | from the host. 83 | 84 | This project's `docker-compose.yml` configuration file uses [Docker 85 | managed volumes][volumes] to store [persistent Zulip 86 | data][persistent-data]. If you use the Docker Compose deployment, you 87 | should make sure that Zulip's volumes are backed up, to ensure that 88 | Zulip's data is backed up. 89 | 90 | This project defines a Docker image for a Zulip server, as well as 91 | sample configuration to run that Zulip server with each of the major 92 | [services that Zulip uses][zulip-architecture] in its own container: 93 | `redis`, `postgres`, `rabbitmq`, `memcached`. 94 | 95 | [volumes]: https://docs.docker.com/storage/volumes/ 96 | [persistent-data]: https://zulip.readthedocs.io/en/latest/production/export-and-import.html#backups 97 | 98 | ## Running a Zulip server with docker-compose 99 | 100 | To use this project, we recommend starting by cloning the repository (since 101 | you'll want to edit the `docker-compose.yml` file in this project): 102 | 103 | ``` 104 | git clone https://github.com/zulip/docker-zulip.git 105 | cd docker-zulip 106 | # Edit `docker-compose.yml` to configure; see docs below 107 | ``` 108 | 109 | If you're in hurry to try Zulip, you can skip to [start the Zulip 110 | server](#starting-the-server), but for production use, you'll need to 111 | generate secrets and do some configuration. 112 | 113 | ### Configuration 114 | 115 | With `docker-compose`, it is traditional to configure a service by 116 | setting environment variables declared in the `zulip -> environment` 117 | section of the `docker-compose.yml` file; this image follows that 118 | convention. 119 | 120 | **Mandatory settings**. You must configure these settings (more 121 | discussion in the main [Zulip installation docs][install-normal]): 122 | 123 | - `SETTING_EXTERNAL_HOST`: The hostname your users will use to 124 | connect to your Zulip server. If you're testing on your laptop, 125 | the default of `localhost.localdomain` is great. 126 | - `SETTING_ZULIP_ADMINISTRATOR`: The email address to receive error 127 | and support emails generated by the Zulip server and its users. 128 | 129 | **Mandatory settings for production use**. Before you allow production 130 | traffic, you need to generate secrets. We recommend using long random 131 | strings of alphanumeric characters for your secrets; not every special 132 | character works. 133 | 134 | - `POSTGRES_PASSWORD` is the password for the PostgreSQL 135 | instance. `SECRETS_postgres_password` configures the Zulip server to 136 | know the PostgreSQL password. While `SECRETS_postgres_password` is 137 | synced to the Zulip container on every boot, `POSTGRES_PASSWORD` is 138 | only accessed by the PostgreSQL container on first boot, so if you 139 | later want to change your PostgreSQL password after booting the 140 | container, you'll need to either do an [ALTER 141 | ROLE][postgres-alter-role] query inside the `postgres` container or 142 | rebuild the PostgreSQL database (only if you don't need your data!). 143 | - `RABBITMQ_DEFAULT_PASS` and `SECRETS_rabbitmq_password` are similar, 144 | just for the RabbitMQ container. 145 | - `MEMCACHED_PASSWORD` and `SECRETS_memcached_password` are similar, 146 | just for the memcached container. 147 | - `REDIS_PASSWORD` and `SECRETS_redis_password` are similar, just for 148 | the Redis container. 149 | - `SECRETS_secret_key` should be a long (e.g. 50 characters), random 150 | string. This value is important to keep secret and constant over 151 | time, since it is used to (among other things) sign login cookies 152 | (so if you change this, all your users will be forcibly logged out). 153 | - `SETTING_EMAIL_*`: Where you configure Zulip's ability to send 154 | [outgoing email][outgoing-email]. 155 | 156 | [postgres-alter-role]: https://www.postgresql.org/docs/9.3/static/sql-alterrole.html 157 | 158 | **Other settings**. If an environment variable name doesn't start with 159 | `SETTING` or `SECRETS` in `docker-compose.yml`, it is specific to the 160 | Docker environment. Standard [Zulip server settings][server-settings] 161 | are secrets are set using the following syntax: 162 | 163 | - `SETTING_MY_SETTING` will become `MY_SETTING` in 164 | `/etc/zulip/settings.py` 165 | - `SECRETS_my_secret` will become `my_secret` in 166 | `/etc/zulip/zulip-secrets.conf`. 167 | 168 | Reading the comments in the sample 169 | [Zulip's settings.py file][prod-settings-template] is the best way to 170 | learn about the full set of Zulip's supported server-level settings. 171 | 172 | Most settings in Zulip are just strings, but some are lists (etc.) 173 | which you need to encode in the YAML file. For example, 174 | 175 | - For `AUTHENTICATION_BACKENDS`, you enter `ZULIP_AUTH_BACKENDS` as a 176 | comma-separated list of the backend names 177 | (E.g. `"EmailAuthBackend,GitHubAuthBackend"`). 178 | 179 | - LDAP authentication currently requires 180 | [`MANUAL_CONFIGURATION`](#manual-configuration) in order to encode 181 | the `LDAPSearch` logic, see below. 182 | 183 | **Reducing RAM usage**. By default, the Zulip server automatically detect 184 | whether the system has enough memory to run Zulip queue processors in the 185 | higher-throughput but more multiprocess mode (or to save 1.5GiB of RAM with 186 | the multi-threaded mode). This algorithm might see the host's memory, not the 187 | docker container's memory. Set to `QUEUE_WORKERS_MULTIPROCESS` to `true` or 188 | `false` to override the automatic calculation. 189 | 190 | **SSL Certificates**. By default, the image will generate a self-signed certificate. 191 | You can set `SSL_CERTIFICATE_GENERATION: "certbot"` within `docker-compose.yml` 192 | to enable automatically-renewed Let's Encrypt certificates. By using certbot 193 | here, you are agreeing to the [Let's Encrypt 194 | ToS](https://community.letsencrypt.org/tos). 195 | 196 | You can also provide an SSL certificate for your Zulip server by 197 | putting it in `/opt/docker/zulip/zulip/certs/` (by default, the 198 | `zulip` container startup script will generate a self-signed certificate and 199 | install it in that directory). 200 | 201 | **Reverse proxies**. To tell Zulip that it is behind a reverse proxy 202 | or load balancer, you must set `LOADBALANCER_IPS` to a comma-separated 203 | list of IPs or CIDR ranges. This will tell Zulip to pass the real IP 204 | of the client, instead of the IP of the proxy itself, by [setting the 205 | IPs][loadbalancer-ips] under `[loadbalancer]` in `zulip.conf`. 206 | 207 | Your proxy must provide both `X-Forwarded-For` and 208 | `X-Forwarded-Proto` headers. See the Zulip documentation for sample 209 | [nginx][nginx-proxy], [Apache2][apache2-proxy], and 210 | [HAProxy][haproxy-proxy] configurations, as well as notes for [other 211 | proxies][other-proxy]. 212 | 213 | [loadbalancer-ips]: https://zulip.readthedocs.io/en/latest/production/reverse-proxies.html#configuring-zulip-to-trust-proxies 214 | [nginx-proxy]: https://zulip.readthedocs.io/en/latest/production/reverse-proxies.html#nginx-configuration 215 | [apache2-proxy]: https://zulip.readthedocs.io/en/latest/production/reverse-proxies.html#apache2-configuration 216 | [haproxy-proxy]: https://zulip.readthedocs.io/en/latest/production/reverse-proxies.html#haproxy-configuration 217 | [other-proxy]: https://zulip.readthedocs.io/en/latest/production/reverse-proxies.html#other-proxies 218 | 219 | ### Manual configuration 220 | 221 | The way the environment variables configuration process described in 222 | the last section works is that the `entrypoint.sh` script that runs 223 | when the Docker image starts up will generate a 224 | [Zulip settings.py file][server-settings] file based on your settings 225 | every time you boot the container. This is convenient, in that you 226 | only need to edit the `docker-compose.yml` file to configure your 227 | Zulip server's settings. 228 | 229 | An alternative approach is to set `MANUAL_CONFIGURATION: "True"` and 230 | `LINK_SETTINGS_TO_DATA: "True"` in `docker-compose.yml`. If you do that, you 231 | can provide a `settings.py` file and a `zulip-secrets.conf` file in 232 | `/opt/docker/zulip/zulip/settings/etc-zulip/`, and the container will use those. 233 | 234 | ### Starting the server 235 | 236 | You can boot your Zulip installation with: 237 | 238 | ``` 239 | docker-compose pull 240 | docker-compose up 241 | ``` 242 | 243 | This will boot the 5 containers declared in `docker-compose.yml`. The 244 | `docker-compose` command will print a bunch of output, and then 245 | eventually hang once everything is happily booted, usually ending with 246 | a bunch of lines like this: 247 | 248 | ``` 249 | rabbitmq_1 | =INFO REPORT==== 27-May-2018::23:26:58 === 250 | rabbitmq_1 | accepting AMQP connection <0.534.0> (172.18.0.3:49504 251 | -> 172.18.0.5:5672) 252 | ``` 253 | 254 | You can inspect what containers are running in another shell with 255 | `docker-compose ps` (remember to `cd` into the `docker-zulip` 256 | directory first). 257 | 258 | If you hit `Ctrl-C`, that will stop your Zulip server cluster. If 259 | you'd prefer to have the containers run in the background, you can use 260 | `docker-compose up -d`. 261 | 262 | If you want to build the Zulip image yourself, you can do that by 263 | running `docker-compose build`; see also 264 | [the documentation on building a custom Git version version](UPGRADING.md#upgrading-from-a-git-repository). 265 | 266 | ### Connecting to your Zulip server 267 | 268 | You can now connect to your Zulip server. For example, if you set 269 | this up on a laptop with the default port mappings and 270 | `SETTING_EXTERNAL_HOST`, typing `http://localhost/` will take you to 271 | your server. Note that in this default scenario, (1) you'll have to 272 | proceed past a self-signed SSL error, and (2) you won't be able to 273 | login until you create an organization, but visiting the URL is a good 274 | way to confirm that your networking configuration is working 275 | correctly. 276 | 277 | ### Creating your organization 278 | 279 | You can now follow the normal Zulip installer instructions for how to 280 | [create a Zulip organization and log in][create-organization] to your 281 | new Zulip server. You'll generate the realm creation link as follows: 282 | 283 | ```bash 284 | docker-compose exec -u zulip zulip \ 285 | "/home/zulip/deployments/current/manage.py generate_realm_creation_link" 286 | ``` 287 | 288 | But don't forget to review the [getting started][next-steps] links at 289 | the end of the main installation guide. 290 | 291 | [next-steps]: https://zulip.readthedocs.io/en/latest/production/install.html#getting-started-with-zulip 292 | 293 | ### Running management commands 294 | 295 | From time to time, you'll need to attach a shell to the Zulip 296 | container so that you can run `manage.py` commands, check logs, etc. 297 | The following are helpful examples: 298 | 299 | ```bash 300 | # Get a (root) shell in the container so you can access logs 301 | docker-compose exec zulip bash 302 | # Run a Zulip management command 303 | docker-compose exec -u zulip zulip \ 304 | "/home/zulip/deployments/current/manage.py list_realms" 305 | ``` 306 | 307 | Since that process for running management commands is a pain, we recommend 308 | [using a wrapper script][wrapper-tool] for running management commands. 309 | 310 | [wrapper-tool]: https://github.com/zulip/docker-zulip/wiki/Running-Management-Commands 311 | 312 | ### Using a custom certificate bundle for outgoing HTTP connections 313 | 314 | If you are sitting behind a custom CA and want to build the Zulip 315 | image yourself, special care is required. 316 | 317 | The Zulip build process installs packages via `yarn` and `pip`, and 318 | these need packages to be configured to use your custom CA 319 | certificates. You will need to get your certificate bundle into the 320 | docker image, either by adding a `COPY` somewhere or by replacing the 321 | `FROM`s with a custom Ubuntu image that includes your bundle. The 322 | recommended way is to have your own base image which has your bundle 323 | ready at the default `/etc/ssl/certs/ca-certificates.crt`. 324 | 325 | The next and last step is to set up the `CUSTOM_CA_CERTIFICATES` 326 | argument in `docker-compose.yml` to point to your CA bundle, e.g. 327 | to `/etc/ssl/certs/ca-certificates.crt`. 328 | 329 | At this point you are ready to build Zulip. 330 | 331 | ### Upgrading 332 | 333 | See the [separate upgrading document](UPGRADING.md) for upgrading 334 | instructions. 335 | 336 | ## Running a Zulip server with Kubernetes 337 | 338 | A Kubernetes pod file is in the `kubernetes/` folder; you can run it 339 | with `kubectl create -f ./kubernetes/`. 340 | 341 | You should read the `docker-compose` section above to understand how 342 | this works, since it's a very similar setup. You'll want to clone 343 | this repository, edit the `zulip-rc.yml` to configure the image, etc. 344 | 345 | ### Installing minikube for testing 346 | 347 | The fastest way to get Kubernetes up and running for testing without 348 | signing up for a cloud service is to install 349 | [Minikube][install-minikube] on your system. 350 | 351 | [install-minikube]: https://kubernetes.io/docs/tasks/tools/install-minikube/ 352 | 353 | ### Helm charts 354 | 355 | Read the [Helm Chart README](kubernetes/chart/zulip/README.md) to learn more 356 | about installing Zulip on a Kubernetes cluster with Helm. 357 | 358 | Feedback is welcome in the [helm-chart-thread]: 359 | https://chat.zulip.org/#narrow/stream/21-provision-help/subject/K8.20and.20Helm/near/589098 360 | 361 | ### Scaling out and high availability 362 | 363 | This image is not designed to make it easy to run multiple copies of 364 | the `zulip` application server container (and you need to know a lot 365 | about Zulip to do this sort of thing successfully). If you're 366 | interested in running a high-availability Zulip installation, your best 367 | bet is to get in touch with the Zulip team at `sales@zulip.com`. 368 | 369 | ## Networking and reverse proxy configuration 370 | 371 | When running your container in production, you may want to put your 372 | Zulip container behind an HTTP proxy. 373 | [This wiki page][proxy-wiki-page] documents how to do this correctly 374 | with `nginx`. 375 | 376 | See also the 377 | [Zulip documentation on reverse proxies][reverse-proxy-docs] 378 | 379 | [proxy-wiki-page]: https://github.com/zulip/docker-zulip/wiki/Proxying-via-nginx-on-host-machine 380 | [reverse-proxy-docs]: https://zulip.readthedocs.io/en/latest/production/reverse-proxies.html#reverse-proxies 381 | 382 | By default, Zulip will only interact with user traffic over HTTPS. 383 | However, if your networking environment is such that the Zulip server 384 | is behind a load balancer and you need the Zulip server to respond 385 | over HTTP, you can configure that via setting `DISABLE_HTTPS: "True"` 386 | in the Docker environment (`docker-compose.yml`). 387 | 388 | ## Troubleshooting 389 | 390 | Common issues include: 391 | 392 | - Invalid configuration resulting in the `zulip` container not 393 | starting; check `docker-compose ps` to see if it started, and then 394 | read the logs for the Zulip container to see why it failed. 395 | - A new Zulip setting not being passed through the Docker 396 | [entrypoint.sh script](/entrypoint.sh) properly. If you 397 | run into this sort of problem you can work around it by specifying a 398 | `ZULIP_CUSTOM_SETTINGS` with one setting per line below, but please 399 | report an issue so that we can fix this for everyone else. 400 | 401 | ## Community support 402 | 403 | You can get community support and tell the developers about your 404 | experiences using this project on 405 | [#production-help](https://chat.zulip.org/#narrow/stream/31-production-help) on 406 | [chat.zulip.org](https://chat.zulip.org/), the Zulip community server. 407 | 408 | In late May 2018, we completed a complete rewrite of this project's 409 | documentation, so we'd love any and all feedback! 410 | 411 | ## Contributing 412 | 413 | We love community contributions, and respond quickly to issues and 414 | PRs. Some particularly useful ways to contribute right now are: 415 | 416 | - Contribute to this documentation by opening issues about what 417 | confused you or submitting pull requests! 418 | - Reporting bugs or rough edges! 419 | 420 | ## Credits 421 | 422 | Huge thanks to everyone who has contributed. Special thanks to 423 | [Alexander Trost](http://github.com/galexrt/), who created 424 | `docker-zulip` and did a huge amount of the early work required to 425 | make a high-quality Docker image for Zulip possible. 426 | 427 | [install-normal]: https://zulip.readthedocs.io/en/latest/production/install.html#installer-options 428 | [outgoing-email]: https://zulip.readthedocs.io/en/latest/production/email.html 429 | [server-settings]: https://zulip.readthedocs.io/en/latest/production/settings.html 430 | [prod-settings-template]: https://github.com/zulip/zulip/blob/main/zproject/prod_settings_template.py 431 | [create-organization]: http://zulip.readthedocs.io/en/latest/production/install.html#step-3-create-a-zulip-organization-and-log-in 432 | -------------------------------------------------------------------------------- /UPGRADING.md: -------------------------------------------------------------------------------- 1 | # Upgrading instructions for `docker-compose` 2 | 3 | You can upgrade your Zulip installation to any newer version of Zulip with the 4 | following instructions. At a high level, the strategy is to download a new 5 | image, stop the `zulip` container, and then boot it back up with the new 6 | image. When the upgraded `zulip` container boots the first time, it will run the 7 | necessary database migrations with `manage.py migrate`. 8 | 9 | If you ever find you need to downgrade your Zulip server, you'll need to use 10 | `manage.py migrate` to downgrade the database schema manually. 11 | 12 | All of the instructions below assume you are using the provided 13 | `docker-compose.yml`. 14 | 15 | ## Upgrading to a release 16 | 17 | 0. (Optional) Upgrading does not delete your data, but it's generally good 18 | practice to [back up your Zulip 19 | data](http://zulip.readthedocs.io/en/latest/prod-maintain-secure-upgrade.html#backups) 20 | before upgrading to make switching back to the old version simple. You can 21 | find your docker data volumes by looking at the `volumes` lines in 22 | `docker-compose.yml` e.g. `/opt/docker/zulip/postgresql/data/`. 23 | 24 | Note that `docker-zulip` did not support for Zulip's built-in 25 | `restore-backup` tool before Zulip 3.0. 26 | 27 | 1. Pull the new image version, e.g. for `2.0.8` run: 28 | 29 | ```shell 30 | docker pull zulip/docker-zulip:2.0.8-0 31 | ``` 32 | 33 | We recommend always upgrading to the latest minor release within a major 34 | release series. 35 | 36 | 2. Update this project to the corresponding `docker-zulip` version and resolve 37 | any merge conflicts in `docker-compose.yml`. This is important as new Zulip 38 | releases may require additional settings to be specified in 39 | `docker-compose.yml` (E.g. authentication settings for `memcached` became 40 | mandatory in the `2.1.2` release). 41 | 42 | **Note:** Do not make any changes to the database version or volume. If there 43 | is a difference in database version, leave those unchanged for now, and 44 | complete that upgrade separately after the Zulip upgrade; see [the section 45 | below][pg-upgrade]. 46 | 47 | [pg-upgrade]: #upgrading-zulipzulip-postgresql-to-14-version-60-0-and-above 48 | 49 | 3. Verify that your updated `docker-compose.yml` points to the desired image 50 | version, e.g.: 51 | 52 | ```yaml 53 | zulip: 54 | image: "zulip/docker-zulip:2.0.1-0" 55 | ``` 56 | 57 | 4. You can execute the upgrade by running: 58 | 59 | ```shell 60 | # Stops the old zulip container; this begins your downtime 61 | docker-compose stop 62 | # Boots the new zulip container; this ends your downtime 63 | docker-compose up 64 | # Deletes the old container images 65 | docker-compose rm 66 | ``` 67 | 68 | That's it! Zulip is now running the updated version. 69 | You can confirm you're running the latest version by running: 70 | 71 | ```shell 72 | docker-compose exec -u zulip zulip cat /home/zulip/deployments/current/version.py 73 | ``` 74 | 75 | ## Upgrading from a Git repository 76 | 77 | 1. Edit `docker-compose.yml` to comment out the `image` line, and specify the 78 | Git commit you'd like to build the zulip container from. E.g.: 79 | 80 | ```yaml 81 | zulip: 82 | # image: "zulip/docker-zulip:2.0.1-0" 83 | build: 84 | context: . 85 | args: 86 | # Change these if you want to build zulip from a different repo/branch 87 | ZULIP_GIT_URL: https://github.com/zulip/zulip.git 88 | ZULIP_GIT_REF: main 89 | ``` 90 | 91 | You can set `ZULIP_GIT_URL` to any clone of the zulip/zulip git repository, 92 | and `ZULIP_GIT_REF` to be any ref name in that repository (e.g. `main` or 93 | `1.9.0` or `445932cc8613c77ced023125248c8b966b3b7528`). 94 | 95 | 2. Run `docker-compose build zulip` to build a Zulip Docker image from the 96 | specified Git version. 97 | 98 | Then stop and restart the container as described in the previous section. 99 | 100 | ## Upgrading to use Docker volumes (version 6.0-0 and above) 101 | 102 | As of Docker Zulip 6.0-0, we have switched the volume storage from being in 103 | directories under `/opt/docker/zulip/` on the Docker host system, to using named 104 | Docker managed volumes. In your `docker-compose.yml`, you should either preserve 105 | the previous `/opt/docker/zulip/` paths for your volumes, or migrate the 106 | contents to individual Docker volumes. 107 | 108 | If you elect to switch to managed Docker volumes, you can copy the data out of 109 | `/opt/docker/zulip` and onto managed volumes using the following: 110 | 111 | ```shell 112 | # Stop the containers 113 | docker-compose stop 114 | 115 | # Copy the data into new managed volumes: 116 | zulip_volume_sync() { docker run -it --rm -v "/opt/docker/zulip/$1:/src" -v "$(basename "$(pwd)")_${2:$1}":/dst ubuntu:20.04 sh -c 'cd /src; cp -a . /dst' ; } 117 | zulip_volume_sync postgresql postgresql-10 118 | zulip_volume_sync zulip 119 | zulip_volume_sync rabbitmq 120 | zulip_volume_sync redis 121 | 122 | # Edit your `docker-compose.yml` to use, e.g. `postgresql-10:/var/lib/postgresql/data:rw` 123 | # rather than `/opt/docker/zulip/postgresql/data:/var/lib/postgresql/data:rw` as a volume. 124 | $EDITOR docker-compose.yml 125 | 126 | # Start the containers again 127 | docker-compose start 128 | ``` 129 | 130 | ## Upgrading zulip/zulip-postgresql to 14 (version 6.0-0 and above) 131 | 132 | As of Docker Zulip 6.0-0, we have upgraded the version of PostgreSQL which our 133 | docker-compose configuration uses, from PostgreSQL 10 (which is no longer 134 | supported) to PostgreSQL 14. Because the on-disk storage is not compatible 135 | between PostgreSQL versions, this requires more than simply switching which 136 | PostgreSQL docker image is used — the data must be dumped from PostgreSQL 10, 137 | and imported into a running PostgreSQL 14. 138 | 139 | You should not adjust the `image` of the database when upgrading to Zulip Server 140 | 6.0. 141 | 142 | After upgrading the `zulip` service, using the usual steps, to the 143 | `zulip/docker-zulip:6.0-0` tag, you can upgrade the PostgreSQL image version by 144 | running the included `./upgrade-postgresql` tool. This will create a 145 | Docker-managed volume named `postgresql-14` to store its data, and will adjust 146 | the `docker-compose.yml` file to use that. 147 | 148 | You can perform this step either before or after updating to use Docker volumes 149 | (above). In either case, the updated `docker-compose.yml` will use a new Docker 150 | volume for the upgraded PostgreSQL 14 data. 151 | 152 | The `upgrade-postgresql` tool requires `docker-compose` 2.1.1 or higher. 153 | 154 | If the tool does not work, you have too old a `docker-compose`, or you would 155 | prefer to perform the steps manually, see the steps below. These instructions 156 | assume that you have not changed the default Postgres data path 157 | (`/opt/docker/zulip/postgresql/data`) in your `docker-compose.yml`. If you have 158 | changed it, please replace all occurrences of 159 | `/opt/docker/zulip/postgresql/data` with your path, or volume. 160 | 161 | 1. Make a backup of your Zulip Postgres data directory. 162 | 163 | 2. Stop the Zulip container: 164 | 165 | ```shell 166 | docker-compose stop zulip 167 | ``` 168 | 169 | 3. Create a new (upgraded) Postgres container using a different data directory: 170 | 171 | ```shell 172 | docker run -d \ 173 | --name postgresnew \ 174 | -e POSTGRES_DB=zulip \ 175 | -e POSTGRES_USER=zulip \ 176 | -e POSTGRES_PASSWORD=zulip \ 177 | -v "$(basename "$(pwd)")_postgresql-14:/var/lib/postgresql/data:rw" \ 178 | zulip/zulip-postgresql:14 179 | ``` 180 | 181 | 4. Use `pg_dumpall` to dump all data from the existing Postgres container to the 182 | new Postgres container, and reset the password (for SCRAM-SHA-256 auth 183 | upgrade): 184 | 185 | ```shell 186 | docker-compose exec database pg_dumpall -U zulip | \ 187 | docker exec -i postgresnew psql -U zulip 188 | 189 | echo "ALTER USER zulip WITH PASSWORD 'REPLACE_WITH_SECURE_POSTGRES_PASSWORD';" | 190 | docker exec -i postgresnew psql -U zulip 191 | ``` 192 | 193 | 5. Stop and remove both Postgres containers: 194 | 195 | ```shell 196 | docker-compose rm --stop database 197 | docker stop postgresnew 198 | docker rm postgresnew 199 | ``` 200 | 201 | 6. Edit your `docker-compose.yml` to use the `zulip/zulip-postgresql:14` image 202 | for the `database` container. 203 | 204 | 7. Edit your `docker-compose.yml` to provide 205 | `postgresql-14:/var/lib/postgresql/data:rw` as the `volume` for the 206 | `database` service. 207 | 208 | 8. Start Zulip up again: 209 | ```shell 210 | docker-compose up 211 | ``` 212 | 213 | ## Upgrading from the old galexrt/docker-zulip 214 | 215 | If you are using an earlier version of `galexrt/docker-zulip` which used the 216 | `quay.io/galexrt/postgres-zulip-tsearchextras:latest` PostgreSQL image, you need 217 | to run a few manual steps to upgrade to the `zulip/zulip-postgresql` PostgreSQL 218 | image (because we've significantly upgraded the major postgres version). 219 | 220 | These instructions assume that you have not changed the default PostgreSQL data 221 | path (`/opt/docker/zulip/postgresql/data`) in your `docker-compose.yml`. If you 222 | have changed it, please replace all occurences of 223 | `/opt/docker/zulip/postgresql/data` with your path. 224 | 225 | 1. Make a backup of your Zulip PostgreSQL data directory. 226 | 227 | 2. Stop all Zulip containers, except the postgres one (e.g. use `docker stop` 228 | and not `docker-compose stop`). 229 | 230 | 3. Create a new (upgraded) PostgreSQL container using a different data 231 | directory: 232 | 233 | ```shell 234 | docker run -d \ 235 | --name postgresnew \ 236 | -e POSTGRES_DB=zulip \ 237 | -e POSTGRES_USER=zulip \ 238 | -e POSTGRES_PASSWORD=zulip \ 239 | -v /opt/docker/zulip/postgresql/new:/var/lib/postgresql/data:rw \ 240 | zulip/zulip-postgresql:latest 241 | ``` 242 | 243 | 4. Use `pg_dumpall` to dump all data from the existing PostgreSQL container to 244 | the new PostgreSQL container: 245 | 246 | ```shell 247 | docker-compose exec database pg_dumpall -U postgres | \ 248 | docker exec -i postgresnew psql -U postgres 249 | ``` 250 | 251 | 5. Stop and remove both PostgreSQL containers: 252 | 253 | ```shell 254 | docker-compose rm --stop database 255 | docker rm --stop postgresnew 256 | ``` 257 | 258 | 6. Edit your `docker-compose.yml` to use the `zulip/zulip-postgresql:latest` 259 | image for the `database` container (this is the default in 260 | `zulip/docker-zulip`). 261 | 262 | 7. Replace the old PostgreSQL data directory with upgraded data directory: 263 | 264 | ```shell 265 | mv /opt/docker/zulip/postgresql/data /opt/docker/zulip/postgresql/old 266 | mv /opt/docker/zulip/postgresql/new /opt/docker/zulip/postgresql/data 267 | ``` 268 | 269 | 8. Delete the old existing containers: 270 | 271 | ```shell 272 | docker-compose rm 273 | ``` 274 | 275 | 9. Start Zulip up again: 276 | 277 | ```shell 278 | docker-compose up 279 | ``` 280 | -------------------------------------------------------------------------------- /certbot-deploy-hook: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | backup() { 6 | if [ -e "$1" ]; then 7 | # If the user is setting up our automatic certbot-management on a 8 | # system that already has certs for Zulip, use some extra caution 9 | # to keep the old certs available. This naming is consistent with Zulip's 10 | # own setup-certbot backups. 11 | mv -f --backup=numbered "$1" "$1".setup-certbot || true 12 | fi 13 | } 14 | 15 | source_cert_dir=/etc/letsencrypt/live/"$SETTING_EXTERNAL_HOST" 16 | dest_cert_dir="$DATA_DIR"/certs 17 | 18 | # Persist the certs to the data directory. 19 | backup "$dest_cert_dir"/zulip.key 20 | backup "$dest_cert_dir"/zulip.combined-chain.crt 21 | cp -f "$source_cert_dir"/privkey.pem "$dest_cert_dir"/zulip.key 22 | cp -f "$source_cert_dir"/fullchain.pem "$dest_cert_dir"/zulip.combined-chain.crt 23 | 24 | # Ensure nginx can find them. 25 | ln -nsf "$dest_cert_dir"/zulip.key /etc/ssl/private/zulip.key 26 | ln -nsf "$dest_cert_dir"/zulip.combined-chain.crt /etc/ssl/certs/zulip.combined-chain.crt 27 | 28 | # Restart various services so the new certs can be used. 29 | supervisorctl restart nginx 30 | -------------------------------------------------------------------------------- /custom_zulip_files/README.custom_zulip_files.md: -------------------------------------------------------------------------------- 1 | The custom_zulip_files mechanism allows you to test edits to Zulip 2 | before making changes in the upstream repo. It works by copying the 3 | contents of this directory on top of the main zulip/zulip checkout as 4 | part of the Docker build process. 5 | 6 | As an example, if you want to test a change to 7 | `scripts/setup/generate-self-signed-cert`, you would grab a copy of 8 | the script from zulip/zulip, place it at 9 | `custom_zulip_files/scripts/setup/generate-self-signed-cert`, make 10 | your local edits, and then run `docker-compose build`. 11 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | database: 3 | image: "zulip/zulip-postgresql:14" 4 | restart: unless-stopped 5 | environment: 6 | POSTGRES_DB: "zulip" 7 | POSTGRES_USER: "zulip" 8 | ## Note that you need to do a manual `ALTER ROLE` query if you 9 | ## change this on a system after booting the postgres container 10 | ## the first time on a host. Instructions are available in README.md. 11 | POSTGRES_PASSWORD: "REPLACE_WITH_SECURE_POSTGRES_PASSWORD" 12 | volumes: 13 | - "postgresql-14:/var/lib/postgresql/data:rw" 14 | memcached: 15 | image: "memcached:alpine" 16 | restart: unless-stopped 17 | command: 18 | - "sh" 19 | - "-euc" 20 | - | 21 | echo 'mech_list: plain' > "$$SASL_CONF_PATH" 22 | echo "zulip@$$HOSTNAME:$$MEMCACHED_PASSWORD" > "$$MEMCACHED_SASL_PWDB" 23 | echo "zulip@localhost:$$MEMCACHED_PASSWORD" >> "$$MEMCACHED_SASL_PWDB" 24 | exec memcached -S 25 | environment: 26 | SASL_CONF_PATH: "/home/memcache/memcached.conf" 27 | MEMCACHED_SASL_PWDB: "/home/memcache/memcached-sasl-db" 28 | MEMCACHED_PASSWORD: "REPLACE_WITH_SECURE_MEMCACHED_PASSWORD" 29 | rabbitmq: 30 | image: "rabbitmq:4.0.7" 31 | restart: unless-stopped 32 | environment: 33 | RABBITMQ_DEFAULT_USER: "zulip" 34 | RABBITMQ_DEFAULT_PASS: "REPLACE_WITH_SECURE_RABBITMQ_PASSWORD" 35 | volumes: 36 | - "rabbitmq:/var/lib/rabbitmq:rw" 37 | redis: 38 | image: "redis:alpine" 39 | restart: unless-stopped 40 | command: 41 | - "sh" 42 | - "-euc" 43 | - | 44 | echo "requirepass '$$REDIS_PASSWORD'" > /etc/redis.conf 45 | exec redis-server /etc/redis.conf 46 | environment: 47 | REDIS_PASSWORD: "REPLACE_WITH_SECURE_REDIS_PASSWORD" 48 | volumes: 49 | - "redis:/data:rw" 50 | zulip: 51 | image: "zulip/docker-zulip:10.3-0" 52 | restart: unless-stopped 53 | build: 54 | context: . 55 | args: 56 | ## Change these if you want to build zulip from a different repo/branch 57 | ZULIP_GIT_URL: https://github.com/zulip/zulip.git 58 | ZULIP_GIT_REF: "10.3" 59 | ## Set this up if you plan to use your own CA certificate bundle for building 60 | # CUSTOM_CA_CERTIFICATES: 61 | ports: 62 | - "80:80" 63 | - "443:443" 64 | environment: 65 | ## See https://github.com/zulip/docker-zulip#configuration for 66 | ## details on this section and how to discover the many 67 | ## additional settings that are supported here. 68 | DB_HOST: "database" 69 | DB_HOST_PORT: "5432" 70 | DB_USER: "zulip" 71 | SSL_CERTIFICATE_GENERATION: "self-signed" 72 | SETTING_MEMCACHED_LOCATION: "memcached:11211" 73 | SETTING_RABBITMQ_HOST: "rabbitmq" 74 | SETTING_REDIS_HOST: "redis" 75 | SECRETS_email_password: "123456789" 76 | ## These should match RABBITMQ_DEFAULT_PASS, POSTGRES_PASSWORD, 77 | ## MEMCACHED_PASSWORD, and REDIS_PASSWORD above. 78 | SECRETS_rabbitmq_password: "REPLACE_WITH_SECURE_RABBITMQ_PASSWORD" 79 | SECRETS_postgres_password: "REPLACE_WITH_SECURE_POSTGRES_PASSWORD" 80 | SECRETS_memcached_password: "REPLACE_WITH_SECURE_MEMCACHED_PASSWORD" 81 | SECRETS_redis_password: "REPLACE_WITH_SECURE_REDIS_PASSWORD" 82 | SECRETS_secret_key: "REPLACE_WITH_SECURE_SECRET_KEY" 83 | SETTING_EXTERNAL_HOST: "localhost.localdomain" 84 | SETTING_ZULIP_ADMINISTRATOR: "admin@example.com" 85 | SETTING_EMAIL_HOST: "" # e.g. smtp.example.com 86 | SETTING_EMAIL_HOST_USER: "noreply@example.com" 87 | SETTING_EMAIL_PORT: "587" 88 | ## It seems that the email server needs to use ssl or tls and can't be used without it 89 | SETTING_EMAIL_USE_SSL: "False" 90 | SETTING_EMAIL_USE_TLS: "True" 91 | ZULIP_AUTH_BACKENDS: "EmailAuthBackend" 92 | ## Uncomment this when configuring the mobile push notifications service 93 | # SETTING_ZULIP_SERVICE_PUSH_NOTIFICATIONS: "True" 94 | # SETTING_ZULIP_SERVICE_SUBMIT_USAGE_STATISTICS: "True" 95 | 96 | ## If you're using a reverse proxy, you'll want to provide the 97 | ## comma-separated set of IP addresses to trust here. 98 | # LOADBALANCER_IPS: "" 99 | 100 | ## By default, files uploaded by users and profile pictures are 101 | ## stored directly on the Zulip server. You can configure files 102 | ## to be stored in Amazon S3 or a compatible data store 103 | ## here. See docs at: 104 | ## 105 | ## https://zulip.readthedocs.io/en/latest/production/upload-backends.html 106 | ## 107 | ## If you want to use the S3 backend, you must set 108 | ## SETTING_LOCAL_UPLOADS_DIR to None as well as configuring the 109 | ## other fields. 110 | # SETTING_LOCAL_UPLOADS_DIR: "None" 111 | # SETTING_S3_AUTH_UPLOADS_BUCKET: "" 112 | # SETTING_S3_AVATAR_BUCKET: "" 113 | # SETTING_S3_ENDPOINT_URL: "None" 114 | # SETTING_S3_REGION: "None" 115 | volumes: 116 | - "zulip:/data:rw" 117 | ulimits: 118 | nofile: 119 | soft: 1000000 120 | hard: 1048576 121 | volumes: 122 | zulip: 123 | postgresql-14: 124 | rabbitmq: 125 | redis: 126 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$DEBUG" = "true" ] || [ "$DEBUG" = "True" ]; then 4 | set -x 5 | set -o functrace 6 | fi 7 | set -e 8 | shopt -s extglob 9 | 10 | # DB aka Database 11 | DB_HOST="${DB_HOST:-127.0.0.1}" 12 | DB_HOST_PORT="${DB_HOST_PORT:-5432}" 13 | DB_NAME="${DB_NAME:-zulip}" 14 | DB_USER="${DB_USER:-zulip}" 15 | REMOTE_POSTGRES_SSLMODE="${REMOTE_POSTGRES_SSLMODE:-prefer}" 16 | # RabbitMQ 17 | SETTING_RABBITMQ_HOST="${SETTING_RABBITMQ_HOST:-127.0.0.1}" 18 | SETTING_RABBITMQ_USER="${SETTING_RABBITMQ_USER:-zulip}" 19 | export RABBITMQ_NODE="$SETTING_RABBITMQ_HOST" 20 | # Redis 21 | SETTING_RATE_LIMITING="${SETTING_RATE_LIMITING:-True}" 22 | SETTING_REDIS_HOST="${SETTING_REDIS_HOST:-127.0.0.1}" 23 | SETTING_REDIS_PORT="${SETTING_REDIS_PORT:-6379}" 24 | # Memcached 25 | if [ -z "$SETTING_MEMCACHED_LOCATION" ]; then 26 | SETTING_MEMCACHED_LOCATION="127.0.0.1:11211" 27 | fi 28 | # Nginx settings 29 | DISABLE_HTTPS="${DISABLE_HTTPS:-false}" 30 | NGINX_WORKERS="${NGINX_WORKERS:-2}" 31 | NGINX_PROXY_BUFFERING="${NGINX_PROXY_BUFFERING:-off}" 32 | NGINX_MAX_UPLOAD_SIZE="${NGINX_MAX_UPLOAD_SIZE:-80m}" 33 | # Zulip certifcate parameters 34 | SSL_CERTIFICATE_GENERATION="${SSL_CERTIFICATE_GENERATION:self-signed}" 35 | # Zulip related settings 36 | ZULIP_AUTH_BACKENDS="${ZULIP_AUTH_BACKENDS:-EmailAuthBackend}" 37 | ZULIP_RUN_POST_SETUP_SCRIPTS="${ZULIP_RUN_POST_SETUP_SCRIPTS:-True}" 38 | # Zulip user setup 39 | FORCE_FIRST_START_INIT="${FORCE_FIRST_START_INIT:-False}" 40 | # Auto backup settings 41 | AUTO_BACKUP_ENABLED="${AUTO_BACKUP_ENABLED:-True}" 42 | AUTO_BACKUP_INTERVAL="${AUTO_BACKUP_INTERVAL:-30 3 * * *}" 43 | # Zulip configuration function specific variable(s) 44 | SPECIAL_SETTING_DETECTION_MODE="${SPECIAL_SETTING_DETECTION_MODE:-}" 45 | MANUAL_CONFIGURATION="${MANUAL_CONFIGURATION:-false}" 46 | LINK_SETTINGS_TO_DATA="${LINK_SETTINGS_TO_DATA:-false}" 47 | # entrypoint.sh specific variable(s) 48 | SETTINGS_PY="/etc/zulip/settings.py" 49 | 50 | # BEGIN appRun functions 51 | # === initialConfiguration === 52 | prepareDirectories() { 53 | mkdir -p "$DATA_DIR" "$DATA_DIR/backups" "$DATA_DIR/certs" "$DATA_DIR/letsencrypt" "$DATA_DIR/uploads" 54 | [ -e /etc/letsencrypt ] || ln -ns "$DATA_DIR/letsencrypt" /etc/letsencrypt 55 | echo "Preparing and linking the uploads folder ..." 56 | rm -rf /home/zulip/uploads 57 | ln -sfT "$DATA_DIR/uploads" /home/zulip/uploads 58 | chown zulip:zulip -R "$DATA_DIR/uploads" 59 | # Link settings folder 60 | if [ "$LINK_SETTINGS_TO_DATA" = "True" ] || [ "$LINK_SETTINGS_TO_DATA" = "true" ]; then 61 | # Create settings directories 62 | if [ ! -d "$DATA_DIR/settings" ]; then 63 | mkdir -p "$DATA_DIR/settings" 64 | fi 65 | if [ ! -d "$DATA_DIR/settings/etc-zulip" ]; then 66 | cp -rf /etc/zulip "$DATA_DIR/settings/etc-zulip" 67 | fi 68 | # Link /etc/zulip/ settings folder 69 | rm -rf /etc/zulip 70 | ln -sfT "$DATA_DIR/settings/etc-zulip" /etc/zulip 71 | fi 72 | echo "Prepared and linked the uploads directory." 73 | } 74 | setConfigurationValue() { 75 | if [ -z "$1" ]; then 76 | echo "No KEY given for setConfigurationValue." 77 | return 1 78 | fi 79 | if [ -z "$3" ]; then 80 | echo "No FILE given for setConfigurationValue." 81 | return 1 82 | fi 83 | local KEY="$1" 84 | local VALUE 85 | local FILE="$3" 86 | local TYPE="$4" 87 | if [ -z "$TYPE" ]; then 88 | case "$2" in 89 | [Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Nn]one) 90 | TYPE="bool" 91 | ;; 92 | +([0-9])) 93 | TYPE="integer" 94 | ;; 95 | [\[\(]*[\]\)]) 96 | TYPE="array" 97 | ;; 98 | *) 99 | TYPE="string" 100 | ;; 101 | esac 102 | fi 103 | case "$TYPE" in 104 | emptyreturn) 105 | if [ -z "$2" ]; then 106 | return 0 107 | fi 108 | ;; 109 | literal) 110 | VALUE="$1" 111 | ;; 112 | bool|boolean|int|integer|array) 113 | VALUE="$KEY = $2" 114 | ;; 115 | string|*) 116 | VALUE="$KEY = '${2//\'/\'}'" 117 | ;; 118 | esac 119 | echo "$VALUE" >> "$FILE" 120 | echo "Setting key \"$KEY\", type \"$TYPE\" in file \"$FILE\"." 121 | } 122 | nginxConfiguration() { 123 | echo "Executing nginx configuration ..." 124 | sed -i "s/worker_processes .*/worker_processes $NGINX_WORKERS;/g" /etc/nginx/nginx.conf 125 | sed -i "s/client_max_body_size .*/client_max_body_size $NGINX_MAX_UPLOAD_SIZE;/g" /etc/nginx/nginx.conf 126 | sed -i "s/proxy_buffering .*/proxy_buffering $NGINX_PROXY_BUFFERING;/g" /etc/nginx/zulip-include/proxy_longpolling 127 | echo "Nginx configuration succeeded." 128 | } 129 | puppetConfiguration() { 130 | echo "Executing puppet configuration ..." 131 | 132 | if [ "$DISABLE_HTTPS" == "True" ] || [ "$DISABLE_HTTPS" == "true" ]; then 133 | echo "Disabling https in nginx." 134 | crudini --set /etc/zulip/zulip.conf application_server http_only true 135 | fi 136 | if [ "$QUEUE_WORKERS_MULTIPROCESS" == "True" ] || [ "$QUEUE_WORKERS_MULTIPROCESS" == "true" ]; then 137 | echo "Setting queue workers to run in multiprocess mode ..." 138 | crudini --set /etc/zulip/zulip.conf application_server queue_workers_multiprocess true 139 | elif [ "$QUEUE_WORKERS_MULTIPROCESS" == "False" ] || [ "$QUEUE_WORKERS_MULTIPROCESS" == "false" ]; then 140 | echo "Setting queue workers to run in multithreaded mode ..." 141 | crudini --set /etc/zulip/zulip.conf application_server queue_workers_multiprocess false 142 | fi 143 | 144 | if [ -n "$LOADBALANCER_IPS" ]; then 145 | echo "Setting IPs for load balancer" 146 | crudini --set /etc/zulip/zulip.conf loadbalancer ips "${LOADBALANCER_IPS}" 147 | fi 148 | 149 | /home/zulip/deployments/current/scripts/zulip-puppet-apply -f 150 | } 151 | configureCerts() { 152 | case "$SSL_CERTIFICATE_GENERATION" in 153 | self-signed) 154 | GENERATE_SELF_SIGNED_CERT="True" 155 | GENERATE_CERTBOT_CERT="False" 156 | ;; 157 | 158 | certbot) 159 | GENERATE_SELF_SIGNED_CERT="False" 160 | GENERATE_CERTBOT_CERT="True" 161 | ;; 162 | *) 163 | echo "Not requesting auto-generated self-signed certs." 164 | GENERATE_CERTBOT_CERT="False" 165 | GENERATE_SELF_SIGNED_CERT="False" 166 | ;; 167 | esac 168 | if [ ! -e "$DATA_DIR/certs/zulip.key" ] && [ ! -e "$DATA_DIR/certs/zulip.combined-chain.crt" ]; then 169 | 170 | if [ "$GENERATE_CERTBOT_CERT" = "True" ]; then 171 | # Zulip isn't yet running, so the certbot's challenge can't be met. 172 | # We'll schedule this for later. 173 | echo "Scheduling LetsEncrypt cert generation ..." 174 | GENERATE_CERTBOT_CERT_SCHEDULED=True 175 | 176 | # Generate self-signed certs just to get Zulip going. 177 | GENERATE_SELF_SIGNED_CERT=True 178 | fi 179 | 180 | if [ "$GENERATE_SELF_SIGNED_CERT" = "True" ]; then 181 | echo "Generating self-signed certificates ..." 182 | mkdir -p "$DATA_DIR/certs" 183 | /home/zulip/deployments/current/scripts/setup/generate-self-signed-cert "$SETTING_EXTERNAL_HOST" 184 | mv /etc/ssl/private/zulip.key "$DATA_DIR/certs/zulip.key" 185 | mv /etc/ssl/certs/zulip.combined-chain.crt "$DATA_DIR/certs/zulip.combined-chain.crt" 186 | echo "Self-signed certificate generation succeeded." 187 | else 188 | echo "Certificates already exist. No need to generate them. Continuing." 189 | fi 190 | fi 191 | if [ ! -e "$DATA_DIR/certs/zulip.key" ]; then 192 | echo "SSL private key zulip.key is not present in $DATA_DIR." 193 | echo "Certificates configuration failed." 194 | echo "Consider setting SSL_CERTIFICATE_GENERATION in the environment to auto-generate" 195 | exit 1 196 | fi 197 | if [ ! -e "$DATA_DIR/certs/zulip.combined-chain.crt" ]; then 198 | echo "SSL public key zulip.combined-chain.crt is not present in $DATA_DIR." 199 | echo "Certificates configuration failed." 200 | echo "Consider setting SSL_CERTIFICATE_GENERATION in the environment to auto-generate" 201 | exit 1 202 | fi 203 | ln -sfT "$DATA_DIR/certs/zulip.key" /etc/ssl/private/zulip.key 204 | ln -sfT "$DATA_DIR/certs/zulip.combined-chain.crt" /etc/ssl/certs/zulip.combined-chain.crt 205 | echo "Certificates configuration succeeded." 206 | } 207 | secretsConfiguration() { 208 | echo "Setting Zulip secrets ..." 209 | if [ ! -e "$DATA_DIR/zulip-secrets.conf" ]; then 210 | echo "Generating Zulip secrets ..." 211 | /root/zulip/scripts/setup/generate_secrets.py --production 212 | mv "/etc/zulip/zulip-secrets.conf" "$DATA_DIR/zulip-secrets.conf" 213 | ln -ns "$DATA_DIR/zulip-secrets.conf" "/etc/zulip/zulip-secrets.conf" 214 | else 215 | ln -nsf "$DATA_DIR/zulip-secrets.conf" "/etc/zulip/zulip-secrets.conf" 216 | echo "Generating Zulip secrets ..." 217 | /root/zulip/scripts/setup/generate_secrets.py --production 218 | fi 219 | echo "Secrets generation succeeded." 220 | local key 221 | for key in "${!SECRETS_@}"; do 222 | [[ "$key" == SECRETS_*([0-9A-Z_a-z-]) ]] || continue 223 | local SECRET_KEY="${key#SECRETS_}" 224 | local SECRET_VAR="${!key}" 225 | if [ -z "$SECRET_VAR" ]; then 226 | echo "Empty secret for key \"$SECRET_KEY\"." 227 | fi 228 | crudini --set "$DATA_DIR/zulip-secrets.conf" "secrets" "${SECRET_KEY}" "${SECRET_VAR}" 229 | done 230 | echo "Zulip secrets configuration succeeded." 231 | } 232 | databaseConfiguration() { 233 | echo "Setting database configuration ..." 234 | setConfigurationValue "REMOTE_POSTGRES_HOST" "$DB_HOST" "$SETTINGS_PY" "string" 235 | setConfigurationValue "REMOTE_POSTGRES_PORT" "$DB_HOST_PORT" "$SETTINGS_PY" "string" 236 | setConfigurationValue "REMOTE_POSTGRES_SSLMODE" "$REMOTE_POSTGRES_SSLMODE" "$SETTINGS_PY" "string" 237 | # The password will be set in secretsConfiguration 238 | echo "Database configuration succeeded." 239 | } 240 | authenticationBackends() { 241 | echo "Activating authentication backends ..." 242 | local FIRST=true 243 | local auth_backends 244 | IFS=, read -r -a auth_backends <<< "$ZULIP_AUTH_BACKENDS" 245 | for AUTH_BACKEND in "${auth_backends[@]}"; do 246 | if [ "$FIRST" = true ]; then 247 | setConfigurationValue "AUTHENTICATION_BACKENDS" "('zproject.backends.${AUTH_BACKEND//\'/\'}',)" "$SETTINGS_PY" "array" 248 | FIRST=false 249 | else 250 | setConfigurationValue "AUTHENTICATION_BACKENDS += ('zproject.backends.${AUTH_BACKEND//\'/\'}',)" "" "$SETTINGS_PY" "literal" 251 | fi 252 | echo "Adding authentication backend \"$AUTH_BACKEND\"." 253 | done 254 | echo "Authentication backend activation succeeded." 255 | } 256 | zulipConfiguration() { 257 | echo "Executing Zulip configuration ..." 258 | if [ -n "$ZULIP_CUSTOM_SETTINGS" ]; then 259 | echo -e "\n$ZULIP_CUSTOM_SETTINGS" >> "$SETTINGS_PY" 260 | fi 261 | local key 262 | for key in "${!SETTING_@}"; do 263 | [[ "$key" == SETTING_*([0-9A-Za-z_]) ]] || continue 264 | local setting_key="${key#SETTING_}" 265 | local setting_var="${!key}" 266 | local type="string" 267 | if [ -z "$setting_var" ]; then 268 | echo "Empty var for key \"$setting_key\"." 269 | continue 270 | fi 271 | # Zulip settings.py / zproject specific overrides here 272 | if [ "$setting_key" = "AUTH_LDAP_CONNECTION_OPTIONS" ] || \ 273 | [ "$setting_key" = "AUTH_LDAP_GLOBAL_OPTIONS" ] || \ 274 | [ "$setting_key" = "AUTH_LDAP_USER_SEARCH" ] || \ 275 | [ "$setting_key" = "AUTH_LDAP_GROUP_SEARCH" ] || \ 276 | [ "$setting_key" = "AUTH_LDAP_REVERSE_EMAIL_SEARCH" ] || \ 277 | [ "$setting_key" = "AUTH_LDAP_USER_ATTR_MAP" ] || \ 278 | [ "$setting_key" = "AUTH_LDAP_USER_FLAGS_BY_GROUP" ] || \ 279 | [ "$setting_key" = "AUTH_LDAP_GROUP_TYPE" ] || \ 280 | [ "$setting_key" = "AUTH_LDAP_ADVANCED_REALM_ACCESS_CONTROL" ] || \ 281 | [ "$setting_key" = "LDAP_SYNCHRONIZED_GROUPS_BY_REALM" ] || \ 282 | [ "$setting_key" = "SOCIAL_AUTH_OIDC_ENABLED_IDPS" ] || \ 283 | [ "$setting_key" = "SOCIAL_AUTH_SAML_ENABLED_IDPS" ] || \ 284 | [ "$setting_key" = "SOCIAL_AUTH_SAML_ORG_INFO" ] || \ 285 | { [ "$setting_key" = "LDAP_APPEND_DOMAIN" ] && [ "$setting_var" = "None" ]; } || \ 286 | [ "$setting_key" = "SCIM_CONFIG" ] || \ 287 | [ "$setting_key" = "SECURE_PROXY_SSL_HEADER" ] || \ 288 | [[ "$setting_key" = "CSRF_"* ]] || \ 289 | [ "$setting_key" = "REALM_HOSTS" ] || \ 290 | [ "$setting_key" = "ALLOWED_HOSTS" ]; then 291 | type="array" 292 | fi 293 | if [ "$SPECIAL_SETTING_DETECTION_MODE" = "True" ] || [ "$SPECIAL_SETTING_DETECTION_MODE" = "true" ] || \ 294 | [ "$type" = "string" ]; then 295 | type="" 296 | fi 297 | if [ "$setting_key" = "EMAIL_HOST_USER" ] || \ 298 | [ "$setting_key" = "EMAIL_HOST_PASSWORD" ] || \ 299 | [ "$setting_key" = "EXTERNAL_HOST" ]; then 300 | type="string" 301 | fi 302 | setConfigurationValue "$setting_key" "$setting_var" "$SETTINGS_PY" "$type" 303 | done 304 | if ! su zulip -c "/home/zulip/deployments/current/manage.py checkconfig"; then 305 | echo "Error in the Zulip configuration. Exiting." 306 | exit 1 307 | fi 308 | echo "Zulip configuration succeeded." 309 | } 310 | autoBackupConfiguration() { 311 | if [ "$AUTO_BACKUP_ENABLED" != "True" ] && [ "$AUTO_BACKUP_ENABLED" != "true" ]; then 312 | rm -f /etc/cron.d/autobackup 313 | echo "Auto backup is disabled. Continuing." 314 | return 0 315 | fi 316 | printf 'MAILTO=""\n%s cd /;/sbin/entrypoint.sh app:backup\n' "$AUTO_BACKUP_INTERVAL" > /etc/cron.d/autobackup 317 | echo "Auto backup enabled." 318 | } 319 | initialConfiguration() { 320 | echo "=== Begin Initial Configuration Phase ===" 321 | prepareDirectories 322 | puppetConfiguration 323 | nginxConfiguration 324 | configureCerts 325 | if [ "$MANUAL_CONFIGURATION" = "False" ] || [ "$MANUAL_CONFIGURATION" = "false" ]; then 326 | # Start with the settings template file. 327 | cp -a /home/zulip/deployments/current/zproject/prod_settings_template.py "$SETTINGS_PY" 328 | databaseConfiguration 329 | secretsConfiguration 330 | authenticationBackends 331 | zulipConfiguration 332 | fi 333 | autoBackupConfiguration 334 | echo "=== End Initial Configuration Phase ===" 335 | } 336 | # === bootstrappingEnvironment === 337 | waitingForDatabase() { 338 | local TIMEOUT=60 339 | echo "Waiting for database server to allow connections ..." 340 | while ! PGPASSWORD="${SECRETS_postgres_password?}" /usr/bin/pg_isready -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" -t 1 >/dev/null 2>&1 341 | do 342 | if ! ((TIMEOUT--)); then 343 | echo "Could not connect to database server. Exiting." 344 | exit 1 345 | fi 346 | echo -n "." 347 | sleep 1 348 | done 349 | } 350 | zulipFirstStartInit() { 351 | echo "Executing Zulip first start init ..." 352 | if [ -e "$DATA_DIR/.initiated" ] && [ "$FORCE_FIRST_START_INIT" != "True" ] && [ "$FORCE_FIRST_START_INIT" != "true" ]; then 353 | echo "First Start Init not needed. Continuing." 354 | return 0 355 | fi 356 | local RETURN_CODE=0 357 | set +e 358 | su zulip -c /home/zulip/deployments/current/scripts/setup/initialize-database 359 | RETURN_CODE=$? 360 | if [[ $RETURN_CODE != 0 ]]; then 361 | echo "Zulip first start database initi failed in \"initialize-database\" exit code $RETURN_CODE. Exiting." 362 | exit $RETURN_CODE 363 | fi 364 | set -e 365 | touch "$DATA_DIR/.initiated" 366 | echo "Zulip first start init sucessful." 367 | } 368 | zulipMigration() { 369 | echo "Running new database migrations..." 370 | set +e 371 | su zulip -c "/home/zulip/deployments/current/manage.py migrate --noinput" 372 | local RETURN_CODE=$? 373 | if [[ $RETURN_CODE != 0 ]]; then 374 | echo "Zulip migration failed with exit code $RETURN_CODE. Exiting." 375 | exit $RETURN_CODE 376 | fi 377 | set -e 378 | rm -rf "$DATA_DIR/.zulip-*" 379 | touch "$DATA_DIR/.zulip-$ZULIP_VERSION" 380 | echo "Database migrations completed." 381 | } 382 | runPostSetupScripts() { 383 | echo "Post setup scripts execution ..." 384 | if [ "$ZULIP_RUN_POST_SETUP_SCRIPTS" != "True" ] && [ "$ZULIP_RUN_POST_SETUP_SCRIPTS" != "true" ]; then 385 | echo "Not running post setup scripts. ZULIP_RUN_POST_SETUP_SCRIPTS isn't true." 386 | return 0 387 | fi 388 | if [ ! -d "$DATA_DIR/post-setup.d/" ]; then 389 | echo "No post-setup.d folder found. Continuing." 390 | return 0 391 | fi 392 | if [ ! "$(ls "$DATA_DIR/post-setup.d/")" ]; then 393 | echo "No post setup scripts found in \"$DATA_DIR/post-setup.d/\"." 394 | return 0 395 | fi 396 | set +e 397 | for file in "$DATA_DIR"/post-setup.d/*; do 398 | if [ -x "$file" ]; then 399 | echo "Executing \"$file\" ..." 400 | bash -c "$file" 401 | echo "Executed \"$file\". Return code $?." 402 | else 403 | echo "Permissions denied for \"$file\". Please check the permissions. Exiting." 404 | exit 1 405 | fi 406 | done 407 | set -e 408 | echo "Post setup scripts execution succeeded." 409 | } 410 | function runCertbotAsNeeded() { 411 | if [ ! "$GENERATE_CERTBOT_CERT_SCHEDULED" = "True" ]; then 412 | echo "Certbot is not scheduled to run." 413 | return 414 | fi 415 | 416 | echo "Waiting for nginx to come online before generating certbot certificate ..." 417 | while ! curl -sk "$SETTING_EXTERNAL_HOST" >/dev/null 2>&1; do 418 | sleep 1; 419 | done 420 | 421 | echo "Generating LetsEncrypt/certbot certificate ..." 422 | 423 | # Remove the self-signed certs which were only needed to get Zulip going. 424 | rm -f "$DATA_DIR"/certs/zulip.key "$DATA_DIR"/certs/zulip.combined-chain.crt 425 | 426 | ln -sf /sbin/certbot-deploy-hook /etc/letsencrypt/renewal-hooks/deploy/docker-deploy-hook 427 | 428 | # Accept the terms of service automatically. 429 | /home/zulip/deployments/current/scripts/setup/setup-certbot \ 430 | --agree-tos \ 431 | --email="$SETTING_ZULIP_ADMINISTRATOR" \ 432 | --skip-symlink \ 433 | -- \ 434 | "$SETTING_EXTERNAL_HOST" 435 | 436 | echo "LetsEncrypt cert generated." 437 | } 438 | bootstrappingEnvironment() { 439 | echo "=== Begin Bootstrap Phase ===" 440 | waitingForDatabase 441 | zulipFirstStartInit 442 | zulipMigration 443 | runPostSetupScripts 444 | # Hack: We run this in the background, since we need nginx to be 445 | # started before we can create the certificate. See #142 for 446 | # details on how we can clean this up. 447 | runCertbotAsNeeded & 448 | echo "=== End Bootstrap Phase ===" 449 | } 450 | # END appRun functions 451 | # BEGIN app functions 452 | appRun() { 453 | initialConfiguration 454 | bootstrappingEnvironment 455 | echo "=== Begin Run Phase ===" 456 | echo "Starting Zulip using supervisor with \"/etc/supervisor/supervisord.conf\" config ..." 457 | echo "" 458 | unset HOME # avoid propagating HOME=/root to subprocesses not running as root 459 | exec supervisord -n -c "/etc/supervisor/supervisord.conf" 460 | } 461 | appInit() { 462 | echo "=== Running initial setup ===" 463 | initialConfiguration 464 | bootstrappingEnvironment 465 | } 466 | appManagePy() { 467 | COMMAND="$1" 468 | shift 1 469 | if [ -z "$COMMAND" ]; then 470 | echo "No command given for manage.py. Defaulting to \"shell\"." 471 | COMMAND="shell" 472 | fi 473 | echo "Running manage.py ..." 474 | set +e 475 | exec su zulip -c "/home/zulip/deployments/current/manage.py $(printf '%q ' "$COMMAND" "$@")" 476 | } 477 | appBackup() { 478 | echo "Starting backup process ..." 479 | local TIMESTAMP 480 | TIMESTAMP=$(date -u -Iseconds | tr ':' '_') 481 | if [ -d "/tmp/backup-$TIMESTAMP" ]; then 482 | echo "Temporary backup folder for \"$TIMESTAMP\" already exists. Aborting." 483 | echo "Backup process failed. Exiting." 484 | exit 1 485 | fi 486 | local BACKUP_FOLDER 487 | BACKUP_FOLDER="/tmp/backup-$TIMESTAMP)" 488 | mkdir -p "$BACKUP_FOLDER" 489 | waitingForDatabase 490 | pg_dump -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" "$DB_NAME" > "$BACKUP_FOLDER/database-postgres.sql" 491 | tar -zcvf "$DATA_DIR/backups/backup-$TIMESTAMP.tar.gz" "$BACKUP_FOLDER/" 492 | rm -r "${BACKUP_FOLDER:?}/" 493 | echo "Backup process succeeded." 494 | exit 0 495 | } 496 | appRestore() { 497 | echo "Starting restore process ..." 498 | if [ -z "$(ls -A "$DATA_DIR/backups/")" ]; then 499 | echo "No backups to restore found in \"$DATA_DIR/backups/\"." 500 | echo "Restore process failed. Exiting." 501 | exit 1 502 | fi 503 | while true; do 504 | local backups=("$DATA_DIR"/backups/*) 505 | printf '|-> %s\n' "${backups[@]#"$DATA_DIR"/backups/}" 506 | echo "Please enter backup filename (full filename with extension): " 507 | read -r BACKUP_FILE 508 | if [ -z "$BACKUP_FILE" ]; then 509 | echo "Empty filename given. Please try again." 510 | echo "" 511 | continue 512 | fi 513 | if [ ! -e "$DATA_DIR/backups/$BACKUP_FILE" ]; then 514 | echo "File \"$BACKUP_FILE\" not found. Please try again." 515 | echo "" 516 | fi 517 | break 518 | done 519 | echo "File \"$BACKUP_FILE\" found." 520 | echo "" 521 | echo "===============================================================" 522 | echo "!! WARNING !! Your current data will be deleted!" 523 | echo "!! WARNING !! YOU HAVE BEEN WARNED! You can abort with \"CTRL+C\"." 524 | echo "!! WARNING !! Waiting 10 seconds before continuing ..." 525 | echo "===============================================================" 526 | echo "" 527 | local TIMEOUT 528 | for TIMEOUT in {10..1}; do 529 | echo "$TIMEOUT" 530 | sleep 1 531 | done 532 | echo "!! WARNING !! Starting restore process ... !! WARNING !!" 533 | waitingForDatabase 534 | tar -zxvf "$DATA_DIR/backups/$BACKUP_FILE" -C /tmp 535 | psql -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" "$DB_NAME" < "/tmp/$(basename "$BACKUP_FILE" | cut -d. -f1)/database-postgres.sql" 536 | rm -r "/tmp/$(basename "$BACKUP_FILE" | cut -d. -f1)/" 537 | echo "Restore process succeeded. Exiting." 538 | exit 0 539 | } 540 | appCerts() { 541 | configureCerts 542 | } 543 | appHelp() { 544 | echo "Available commands:" 545 | echo "> app:help - Show this help menu and exit" 546 | echo "> app:version - Container Zulip server version" 547 | echo "> app:managepy - Run Zulip's manage.py script (defaults to \"shell\")" 548 | echo "> app:backup - Create backups of Zulip instances" 549 | echo "> app:restore - Restore backups of Zulip instances" 550 | echo "> app:certs - Create self-signed certificates" 551 | echo "> app:run - Run the Zulip server" 552 | echo "> app:init - Run inital setup of Zulip server" 553 | echo "> [COMMAND] - Run given command with arguments in shell" 554 | } 555 | appVersion() { 556 | echo "This container contains:" 557 | echo "> Zulip server $ZULIP_VERSION" 558 | echo "> Checksum: $ZULIP_CHECKSUM" 559 | exit 0 560 | } 561 | # END app functions 562 | 563 | case "$1" in 564 | app:run) 565 | appRun 566 | ;; 567 | app:init) 568 | appInit 569 | ;; 570 | app:managepy) 571 | shift 1 572 | appManagePy "$@" 573 | ;; 574 | app:backup) 575 | appBackup 576 | ;; 577 | app:restore) 578 | appRestore 579 | ;; 580 | app:certs) 581 | appCerts 582 | ;; 583 | app:help) 584 | appHelp 585 | ;; 586 | app:version) 587 | appVersion 588 | ;; 589 | *) 590 | exec "$@" || appHelp 591 | ;; 592 | esac 593 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.10.3] - 2025-05-15 2 | 3 | - Update to Zulip Server 10.3 4 | 5 | ## [0.10.2] - 2025-04-15 6 | 7 | - Update to Zulip Server 10.2 8 | 9 | ## [0.10.1] - 2025-03-28 10 | 11 | - Update to Zulip Server 10.1 12 | 13 | ## [0.10.0] - 2025-03-20 14 | 15 | - Update to Zulip Server 10.0 16 | 17 | ## [0.9.40] - 2025-01-16 18 | 19 | - Update to Zulip Server 9.4 20 | 21 | ## [0.9.30] - 2024-11-23 22 | 23 | - Update to Zulip Server 9.3 24 | 25 | ## [0.9.20] - 2024-09-16 26 | 27 | - Update to Zulip Server 9.2 28 | - Change nginx's max-upload size to match standard Zulip (80m) 29 | 30 | ## [0.9.13] - 2024-09-09 31 | 32 | - More thoroughly remove the `ubuntu` user 33 | 34 | ## [0.9.12] - 2024-09-09 35 | 36 | - Fix consistent user-ID for `zulip` user 37 | 38 | ## [0.9.11] - 2024-08-28 39 | 40 | - Packaging updates for Docker 41 | 42 | ## [0.9.1] - 2024-08-02 43 | 44 | - Update Zulip Server to 9.1 45 | 46 | ## [0.9.1] - 2024-07-25 47 | 48 | - Update Zulip Server to 9.1 49 | 50 | ## [0.8.4] - 2024-05-09 51 | 52 | - Update Zulip Server to 8.4 53 | 54 | ## [0.8.3] - 2024-03-19 55 | 56 | - Update Zulip Server to 8.3 57 | 58 | ## [0.8.2] - 2024-02-16 59 | 60 | - Update Zulip Server to 8.2 61 | 62 | ## [0.8.1] - 2024-01-24 63 | 64 | - Update Zulip Server to 8.1 65 | 66 | ## [0.8.0] - 2023-12-15 67 | 68 | - Update Zulip Server to 8.0 69 | 70 | ## [0.7.5] - 2023-11-17 71 | 72 | - Update Zulip Server to 7.5 73 | 74 | ## [0.7.4] - 2023-09-15 75 | 76 | - Update Zulip Server to 7.4 77 | 78 | ## [0.7.3] - 2023-08-25 79 | 80 | - Update Zulip Server to 7.3 81 | 82 | ## [0.7.2] - 2023-07-05 83 | 84 | - Update Zulip Server to 7.2 85 | 86 | ## [0.7.1] - 2023-06-13 87 | 88 | - Update Zulip Server to 7.1 89 | 90 | ## [0.7.0] - 2023-05-31 91 | 92 | - Update Zulip Server to 7.0 93 | 94 | ## [0.6.2] - 2023-05-19 95 | 96 | - Update Zulip Server to 6.2 97 | 98 | ## [0.6.1] - 2023-01-23 99 | 100 | - Update Zulip Server to 6.1 101 | 102 | ## [0.6.0] - 2022-11-23 103 | 104 | - Update Zulip Server to 6.0 105 | 106 | ## [0.5.0] - 2022-11-16 107 | 108 | - Update Zulip Server to 5.7 109 | 110 | ## [0.4.0] - 2022-06-21 111 | 112 | - Update Zulip Server 5.2 to 5.6 113 | 114 | ## [0.3.0] - 2022-04-21 115 | 116 | - Update dependencies: 117 | 118 | - Helm charts: 119 | 120 | | Repository | Name | Version | 121 | | ---------------------------------- | ---------- | ------- | 122 | | https://charts.bitnami.com/bitnami | memcached | 6.0.16 | 123 | | https://charts.bitnami.com/bitnami | postgresql | 11.1.22 | 124 | | https://charts.bitnami.com/bitnami | rabbitmq | 8.32.0 | 125 | | https://charts.bitnami.com/bitnami | redis | 16.8.7 | 126 | 127 | - Update postgres 10 to postgres 14 128 | - Update Zulip 4.7 to 5.2 129 | 130 | - Remove autoscaling code 131 | - Remove readiness probe because its function is the same as the liveness probe 132 | 133 | ## [0.2.0] - 2021-11-22 134 | 135 | - Use dependency charts from the Bitnami repository for Memcached, Rabbitmq, 136 | Redis and PostgreSQL 137 | - Use a StatefulSet instead of a Deployment 138 | - Add the possibility to run postSetup scripts 139 | 140 | ## [0.1.0] - 2020-12-30 141 | 142 | - First version of helm chart created 143 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: memcached 3 | repository: oci://registry-1.docker.io/bitnamicharts 4 | version: 7.4.16 5 | - name: rabbitmq 6 | repository: oci://registry-1.docker.io/bitnamicharts 7 | version: 14.7.0 8 | - name: redis 9 | repository: oci://registry-1.docker.io/bitnamicharts 10 | version: 20.1.4 11 | - name: postgresql 12 | repository: oci://registry-1.docker.io/bitnamicharts 13 | version: 15.5.32 14 | digest: sha256:511a69dac54b26810b3b269751c8115d5b8ed7a9ecfe5cc4a656e7feb5dbd1ff 15 | generated: "2024-09-23T17:27:41.004706+02:00" 16 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | description: Zulip is an open source threaded team chat that helps teams stay productive and focused. 3 | name: zulip 4 | type: application 5 | icon: https://raw.githubusercontent.com/zulip/zulip/main/static/images/logo/zulip-icon-square.svg 6 | ## This is the chart version. This version number should be 7 | ## incremented each time you make changes to the chart and its 8 | ## templates, including the app version. Versions are expected to 9 | ## follow Semantic Versioning (https://semver.org/) 10 | version: 0.10.3 11 | 12 | ## This is the version number of the application being deployed. This 13 | ## version number should be incremented each time you make changes to 14 | ## the application. Versions are not expected to follow Semantic 15 | ## Versioning. They should reflect the version the application is 16 | ## using. It is recommended to use it with quotes. 17 | appVersion: "10.3-0" 18 | dependencies: 19 | - name: memcached 20 | repository: oci://registry-1.docker.io/bitnamicharts 21 | tags: 22 | - memcached 23 | version: 7.4.16 24 | - name: rabbitmq 25 | repository: oci://registry-1.docker.io/bitnamicharts 26 | tags: 27 | - rabbitmq 28 | version: 14.7.0 29 | - name: redis 30 | repository: oci://registry-1.docker.io/bitnamicharts 31 | tags: 32 | - redis 33 | version: 20.1.4 34 | - name: postgresql 35 | repository: oci://registry-1.docker.io/bitnamicharts 36 | tags: 37 | - postgresql 38 | ## Note: values.yaml overwrites posgresql image to zulip/zulip-postgresql:14 39 | version: 15.5.32 40 | 41 | sources: 42 | - https://github.com/zulip/zulip 43 | - https://github.com/zulip/docker-zulip 44 | - https://hub.docker.com/r/zulip/docker-zulip 45 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/README.md: -------------------------------------------------------------------------------- 1 | # Zulip 2 | 3 | ![Version: 0.10.3](https://img.shields.io/badge/Version-0.10.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 10.3-0](https://img.shields.io/badge/AppVersion-10.3--0-informational?style=flat-square) 4 | 5 | [Zulip](https://zulip.com/) is an open source threaded team chat that helps teams stay productive and focused. 6 | 7 | Helm chart based on https://github.com/zulip/docker-zulip 8 | 9 | ## Installation 10 | 11 | Copy `values-local.yaml.example`, modify it as instructed in the comments, then 12 | install with the following commands: 13 | 14 | ``` 15 | helm dependency update # Get helm dependency charts 16 | helm install -f ./values-local.yaml zulip . # Install Zulip 17 | ``` 18 | 19 | This will show a message on how to reach your Zulip installation and how to 20 | create your first realm. Wait for all your pods to be ready before you continue. 21 | You can run `kubectl get pods` to their current state. Once all pods are ready, 22 | you can run the commands to create a Realm, and you can reach Zulip following 23 | the instructions as well. 24 | 25 | ### Installing on Minikube 26 | 27 | You need to do a few things to make 28 | [minikube](https://minikube.sigs.k8s.io/docs/) serve Zulip with a TLS 29 | certificate. Without it, Zulip will not work. 30 | 31 | If you haven't already, you need to set up `cert-manager` inside your minikube. 32 | 33 | First, enable the "ingress" minikube addon ([more info available 34 | here](https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/#enable-the-ingress-controller)) 35 | 36 | ``` 37 | minikube addons enable ingress 38 | ``` 39 | 40 | Second, [install cert-manager into your minikube 41 | cluster](https://cert-manager.io/docs/installation/#default-static-install): 42 | 43 | ``` 44 | kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.yaml 45 | ``` 46 | 47 | Now you'll need to add an issuer that issues self-signed certificates. Copy this 48 | into a file, `self-signed-issuer.yaml` 49 | 50 | ``` 51 | apiVersion: cert-manager.io/v1 52 | kind: ClusterIssuer 53 | metadata: 54 | name: selfsigned 55 | namespace: cert-manager 56 | spec: 57 | selfSigned: {} 58 | ``` 59 | 60 | Now apply the issuer: `kubectl apply -f self-signed-issuer.yaml` 61 | 62 | We'll host Zulip on `zulip.local`. Add that to your `/etc/hosts` file and 63 | point it to the IP address you get with the command `minikube ip`. 64 | 65 | Now you're ready to follow [the installation instructions above](#installation). 66 | 67 | ## Values 68 | 69 | | Key | Type | Default | Description | 70 | |-----|------|---------|-------------| 71 | | affinity | object | `{}` | | 72 | | fullnameOverride | string | `""` | | 73 | | image.pullPolicy | string | `"IfNotPresent"` | | 74 | | image.repository | string | `"zulip/docker-zulip"` | | 75 | | image.tag | string | `"10.3-0"` | | 76 | | imagePullSecrets | list | `[]` | | 77 | | ingress.annotations | object | `{}` | | 78 | | ingress.enabled | bool | `false` | | 79 | | ingress.hosts[0].host | string | `"zulip.example.com"` | | 80 | | ingress.hosts[0].paths[0].path | string | `"/"` | | 81 | | ingress.tls | list | `[]` | | 82 | | livenessProbe.enabled | bool | `true` | | 83 | | livenessProbe.failureThreshold | int | `3` | | 84 | | livenessProbe.initialDelaySeconds | int | `10` | | 85 | | livenessProbe.periodSeconds | int | `10` | | 86 | | livenessProbe.successThreshold | int | `1` | | 87 | | livenessProbe.timeoutSeconds | int | `5` | | 88 | | memcached.memcachedUsername | string | `"zulip@localhost"` | | 89 | | nameOverride | string | `""` | | 90 | | nodeSelector | object | `{}` | | 91 | | podAnnotations | object | `{}` | | 92 | | podLabels | object | `{}` | | 93 | | podSecurityContext | object | `{}` | | 94 | | postSetup.scripts | object | `{}` | | 95 | | postgresql.auth.database | string | `"zulip"` | | 96 | | postgresql.auth.username | string | `"zulip"` | | 97 | | postgresql.image.repository | string | `"zulip/zulip-postgresql"` | | 98 | | postgresql.image.tag | int | `14` | | 99 | | postgresql.primary.containerSecurityContext.runAsUser | int | `0` | | 100 | | rabbitmq.auth.username | string | `"zulip"` | | 101 | | rabbitmq.persistence.enabled | bool | `false` | | 102 | | redis.architecture | string | `"standalone"` | | 103 | | redis.master.persistence.enabled | bool | `false` | | 104 | | resources | object | `{}` | | 105 | | securityContext | object | `{}` | | 106 | | service.port | int | `80` | | 107 | | service.type | string | `"ClusterIP"` | | 108 | | serviceAccount.annotations | object | `{}` | | 109 | | serviceAccount.create | bool | `true` | | 110 | | serviceAccount.name | string | `""` | | 111 | | sidecars | list | `[]` | | 112 | | startupProbe.enabled | bool | `true` | | 113 | | startupProbe.failureThreshold | int | `30` | | 114 | | startupProbe.initialDelaySeconds | int | `10` | | 115 | | startupProbe.periodSeconds | int | `10` | | 116 | | startupProbe.successThreshold | int | `1` | | 117 | | startupProbe.timeoutSeconds | int | `5` | | 118 | | statefulSetAnnotations | object | `{}` | | 119 | | statefulSetLabels | object | `{}` | | 120 | | tolerations | list | `[]` | | 121 | | zulip.environment.DISABLE_HTTPS | bool | `true` | | 122 | | zulip.environment.SECRETS_email_password | string | `"123456789"` | | 123 | | zulip.environment.SETTING_EMAIL_HOST | string | `""` | | 124 | | zulip.environment.SETTING_EMAIL_HOST_USER | string | `"noreply@example.com"` | | 125 | | zulip.environment.SETTING_EMAIL_PORT | string | `"587"` | | 126 | | zulip.environment.SETTING_EMAIL_USE_SSL | string | `"False"` | | 127 | | zulip.environment.SETTING_EMAIL_USE_TLS | string | `"True"` | | 128 | | zulip.environment.SETTING_EXTERNAL_HOST | string | `"zulip.example.com"` | | 129 | | zulip.environment.SETTING_ZULIP_ADMINISTRATOR | string | `"admin@example.com"` | | 130 | | zulip.environment.SSL_CERTIFICATE_GENERATION | string | `"self-signed"` | | 131 | | zulip.environment.ZULIP_AUTH_BACKENDS | string | `"EmailAuthBackend"` | | 132 | | zulip.persistence.accessMode | string | `"ReadWriteOnce"` | | 133 | | zulip.persistence.enabled | bool | `true` | | 134 | | zulip.persistence.size | string | `"10Gi"` | | 135 | | zulip.persistence.storageClass | string | `nil` | | 136 | 137 | ## About this helm chart 138 | 139 | This helm chart sets up a StatefulSet that runs a Zulip pod, that in turn runs 140 | the [docker-zulip](https://hub.docker.com/r/zulip/docker-zulip/) Dockerized 141 | Zulip version. Configuration of Zulip happens through environment variables that 142 | are defined in the `values.yaml` under `zulip.environment`. These environment 143 | variables are forwarded to the Docker container, you can read more about 144 | configuring Zulip through environment variables 145 | [here](https://github.com/zulip/docker-zulip/#configuration). 146 | 147 | ### Dependencies 148 | 149 | The chart uses Memcached, RabbitMQ and Redis helm charts defined in 150 | the Bitnami Helm repository. Most of these are configured following their 151 | default settings, but you can check 152 | https://github.com/bitnami/charts/tree/master/bitnami/ for more configuration 153 | options of the subcharts. 154 | 155 | For PostgreSQL the chart also uses the Bitnami chart to install it on the 156 | Kubernetes cluster. However, in this case we use Zulip's 157 | [zulip-postgresql](https://hub.docker.com/r/zulip/zulip-postgresql) docker 158 | image, because it contains the Postgresql plugins that are needed to run Zulip. 159 | 160 | ## Requirements 161 | 162 | | Repository | Name | Version | 163 | |------------|------|---------| 164 | | oci://registry-1.docker.io/bitnamicharts | memcached | 7.4.16 | 165 | | oci://registry-1.docker.io/bitnamicharts | postgresql | 15.5.32 | 166 | | oci://registry-1.docker.io/bitnamicharts | rabbitmq | 14.7.0 | 167 | | oci://registry-1.docker.io/bitnamicharts | redis | 20.1.4 | 168 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/README.md.gotmpl: -------------------------------------------------------------------------------- 1 | # Zulip 2 | 3 | {{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} 4 | 5 | [Zulip](https://zulip.com/) is an open source threaded team chat that helps teams stay productive and focused. 6 | 7 | Helm chart based on https://github.com/zulip/docker-zulip 8 | 9 | ## Installation 10 | 11 | Copy `values-local.yaml.example`, modify it as instructed in the comments, then 12 | install with the following commands: 13 | 14 | ``` 15 | helm dependency update # Get helm dependency charts 16 | helm install -f ./values-local.yaml zulip . # Install Zulip 17 | ``` 18 | 19 | This will show a message on how to reach your Zulip installation and how to 20 | create your first realm. Wait for all your pods to be ready before you continue. 21 | You can run `kubectl get pods` to their current state. Once all pods are ready, 22 | you can run the commands to create a Realm, and you can reach Zulip following 23 | the instructions as well. 24 | 25 | ### Installing on Minikube 26 | 27 | You need to do a few things to make 28 | [minikube](https://minikube.sigs.k8s.io/docs/) serve Zulip with a TLS 29 | certificate. Without it, Zulip will not work. 30 | 31 | If you haven't already, you need to set up `cert-manager` inside your minikube. 32 | 33 | First, enable the "ingress" minikube addon ([more info available 34 | here](https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/#enable-the-ingress-controller)) 35 | 36 | ``` 37 | minikube addons enable ingress 38 | ``` 39 | 40 | Second, [install cert-manager into your minikube 41 | cluster](https://cert-manager.io/docs/installation/#default-static-install): 42 | 43 | ``` 44 | kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.yaml 45 | ``` 46 | 47 | Now you'll need to add an issuer that issues self-signed certificates. Copy this 48 | into a file, `self-signed-issuer.yaml` 49 | 50 | ``` 51 | apiVersion: cert-manager.io/v1 52 | kind: ClusterIssuer 53 | metadata: 54 | name: selfsigned 55 | namespace: cert-manager 56 | spec: 57 | selfSigned: {} 58 | ``` 59 | 60 | Now apply the issuer: `kubectl apply -f self-signed-issuer.yaml` 61 | 62 | We'll host Zulip on `zulip.local`. Add that to your `/etc/hosts` file and 63 | point it to the IP address you get with the command `minikube ip`. 64 | 65 | Now you're ready to follow [the installation instructions above](#installation). 66 | 67 | {{ template "chart.valuesSection" . }} 68 | 69 | ## About this helm chart 70 | 71 | This helm chart sets up a StatefulSet that runs a Zulip pod, that in turn runs 72 | the [docker-zulip](https://hub.docker.com/r/zulip/docker-zulip/) Dockerized 73 | Zulip version. Configuration of Zulip happens through environment variables that 74 | are defined in the `values.yaml` under `zulip.environment`. These environment 75 | variables are forwarded to the Docker container, you can read more about 76 | configuring Zulip through environment variables 77 | [here](https://github.com/zulip/docker-zulip/#configuration). 78 | 79 | ### Dependencies 80 | 81 | The chart uses Memcached, RabbitMQ and Redis helm charts defined in 82 | the Bitnami Helm repository. Most of these are configured following their 83 | default settings, but you can check 84 | https://github.com/bitnami/charts/tree/master/bitnami/ for more configuration 85 | options of the subcharts. 86 | 87 | For PostgreSQL the chart also uses the Bitnami chart to install it on the 88 | Kubernetes cluster. However, in this case we use Zulip's 89 | [zulip-postgresql](https://hub.docker.com/r/zulip/zulip-postgresql) docker 90 | image, because it contains the Postgresql plugins that are needed to run Zulip. 91 | 92 | {{ template "chart.requirementsSection" . }} 93 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. To create a realm so you can sign in: 2 | 3 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ template "zulip.name" . }}" -o jsonpath="{.items[0].metadata.name}") 4 | kubectl -n {{ .Release.Namespace }} exec -it "$POD_NAME" -c zulip -- sudo -u zulip /home/zulip/deployments/current/manage.py generate_realm_creation_link 5 | 6 | 2. Zulip will be available on: 7 | 8 | 9 | {{- if .Values.ingress.enabled }} 10 | {{- range $host := .Values.ingress.hosts }} 11 | {{- range .paths }} 12 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} 13 | {{- end }} 14 | {{- end }} 15 | {{- else if contains "NodePort" .Values.service.type }} 16 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "zulip.fullname" . }}) 17 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 18 | echo http://$NODE_IP:$NODE_PORT 19 | {{- else if contains "LoadBalancer" .Values.service.type }} 20 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 21 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "zulip.fullname" . }}' 22 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "zulip.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 23 | echo http://$SERVICE_IP:{{ .Values.service.port }} 24 | {{- else if contains "ClusterIP" .Values.service.type }} 25 | echo "Visit http://127.0.0.1:{{ .Values.service.port }} to use your application" 26 | kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ .Release.Name }}-http {{ .Values.service.port }}:{{ .Values.service.port }} 27 | {{- end }} 28 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "zulip.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "zulip.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "zulip.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "zulip.labels" -}} 37 | helm.sh/chart: {{ include "zulip.chart" . }} 38 | {{ include "zulip.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "zulip.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "zulip.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the service account to use 55 | */}} 56 | {{- define "zulip.serviceAccountName" -}} 57 | {{- if .Values.serviceAccount.create }} 58 | {{- default (include "zulip.fullname" .) .Values.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | 64 | {{/* 65 | include all env variables for Zulip pods 66 | */}} 67 | {{- define "zulip.env" -}} 68 | 69 | - name: DB_HOST 70 | value: "{{ template "postgresql.v1.primary.fullname" .Subcharts.postgresql }}" 71 | - name: DB_HOST_PORT 72 | value: "{{ template "postgresql.v1.service.port" .Subcharts.postgresql }}" 73 | - name: DB_USER 74 | value: "postgres" 75 | - name: SETTING_MEMCACHED_LOCATION 76 | value: "{{ template "common.names.fullname" .Subcharts.memcached }}:11211" 77 | - name: SETTING_RABBITMQ_HOST 78 | value: "{{ template "common.names.fullname" .Subcharts.rabbitmq }}" 79 | - name: SETTING_REDIS_HOST 80 | value: "{{ template "common.names.fullname" .Subcharts.redis }}-headless" 81 | - name: SECRETS_rabbitmq_password 82 | value: "{{ .Values.rabbitmq.auth.password }}" 83 | - name: SECRETS_postgres_password 84 | value: "{{ .Values.postgresql.auth.password }}" 85 | - name: SECRETS_memcached_password 86 | value: "{{ .Values.memcached.memcachedPassword }}" 87 | - name: SECRETS_redis_password 88 | value: "{{ .Values.redis.auth.password }}" 89 | - name: SECRETS_secret_key 90 | value: "{{ .Values.zulip.password }}" 91 | {{- range $key, $value := .Values.zulip.environment }} 92 | - name: {{ $key }} 93 | value: {{ $value | quote }} 94 | {{- end }} 95 | {{- end }} 96 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/templates/cm-post-setup-scripts.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: "{{ .Release.Name }}-post-setup-scripts" 5 | labels: 6 | {{- include "zulip.labels" . | nindent 4 }} 7 | data: 8 | {{- range $scriptName, $scriptContents := .Values.postSetup.scripts }} 9 | {{ $scriptName }}: | 10 | {{- $scriptContents | nindent 4 }} 11 | {{- end }} 12 | 13 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "zulip.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | apiVersion: networking.k8s.io/v1 5 | kind: Ingress 6 | metadata: 7 | name: {{ $fullName }} 8 | labels: 9 | {{- include "zulip.labels" . | nindent 4 }} 10 | {{- with .Values.ingress.annotations }} 11 | annotations: 12 | {{- toYaml . | nindent 4 }} 13 | {{- end }} 14 | spec: 15 | {{- if .Values.ingress.tls }} 16 | tls: 17 | {{- range .Values.ingress.tls }} 18 | - hosts: 19 | {{- range .hosts }} 20 | - {{ . | quote }} 21 | {{- end }} 22 | secretName: {{ .secretName }} 23 | {{- end }} 24 | {{- end }} 25 | rules: 26 | {{- range .Values.ingress.hosts }} 27 | - host: {{ .host | quote }} 28 | http: 29 | paths: 30 | {{- range .paths }} 31 | - path: {{ .path }} 32 | pathType: ImplementationSpecific 33 | backend: 34 | service: 35 | name: {{ $fullName }} 36 | port: 37 | number: {{ $svcPort }} 38 | {{- end }} 39 | {{- end }} 40 | {{- end }} 41 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/templates/pvc.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.zulip.persistence.enabled -}} 2 | {{- if not .Values.zulip.persistence.existingClaim -}} 3 | kind: PersistentVolumeClaim 4 | apiVersion: v1 5 | metadata: 6 | name: {{ template "zulip.fullname" . }}-data 7 | labels: 8 | {{- include "zulip.labels" . | nindent 4 }} 9 | {{- if .Values.zulip.persistence.annotations }} 10 | annotations: 11 | {{ toYaml .Values.zulip.persistence.annotations | indent 4 }} 12 | {{- end }} 13 | spec: 14 | accessModes: 15 | - {{ .Values.zulip.persistence.accessMode | quote }} 16 | resources: 17 | requests: 18 | storage: {{ .Values.zulip.persistence.size | quote }} 19 | {{- if .Values.zulip.persistence.storageClass }} 20 | {{- if (eq "-" .Values.zulip.persistence.storageClass) }} 21 | storageClassName: "" 22 | {{- else }} 23 | storageClassName: "{{ .Values.zulip.persistence.storageClass }}" 24 | {{- end }} 25 | {{- end }} 26 | {{- end -}} 27 | {{- end -}} 28 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "zulip.fullname" . }} 5 | labels: 6 | {{- include "zulip.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | {{- if .Values.zulip.environment.DISABLE_HTTPS }} 12 | targetPort: http 13 | {{- else }} 14 | targetPort: https 15 | {{- end }} 16 | protocol: TCP 17 | name: http 18 | selector: 19 | {{- include "zulip.selectorLabels" . | nindent 4 }} 20 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "zulip.serviceAccountName" . }} 6 | labels: 7 | {{- include "zulip.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/templates/statefulset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: {{ include "zulip.fullname" . }} 5 | labels: 6 | {{- include "zulip.labels" . | nindent 4 }} 7 | {{- if .Values.statefulSetLabels }} 8 | {{- toYaml .Values.statefulSetLabels | nindent 4 }} 9 | {{- end }} 10 | {{- if .Values.statefulSetAnnotations }} 11 | annotations: 12 | {{- toYaml .Values.statefulSetAnnotations | nindent 4 }} 13 | {{- end }} 14 | spec: 15 | selector: 16 | matchLabels: 17 | {{- include "zulip.selectorLabels" . | nindent 6 }} 18 | serviceName: {{ include "zulip.fullname" . }} 19 | template: 20 | metadata: 21 | {{- with .Values.podAnnotations }} 22 | annotations: 23 | {{- toYaml . | nindent 8 }} 24 | {{- end }} 25 | labels: 26 | {{- include "zulip.selectorLabels" . | nindent 8 }} 27 | {{- if .Values.podLabels }} 28 | {{- toYaml .Values.podLabels | nindent 8 }} 29 | {{- end }} 30 | spec: 31 | {{- with .Values.imagePullSecrets }} 32 | imagePullSecrets: 33 | {{- toYaml . | nindent 8 }} 34 | {{- end }} 35 | serviceAccountName: {{ include "zulip.serviceAccountName" . }} 36 | securityContext: 37 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 38 | containers: 39 | {{- if .Values.sidecars }} 40 | {{- with .Values.sidecars }} 41 | {{- toYaml . | nindent 8 }} 42 | {{- end }} 43 | {{- end }} 44 | - name: {{ .Chart.Name }} 45 | securityContext: 46 | {{- toYaml .Values.securityContext | nindent 12 }} 47 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 48 | imagePullPolicy: {{ .Values.image.pullPolicy }} 49 | ports: 50 | - name: http 51 | containerPort: 80 52 | protocol: TCP 53 | - name: https 54 | containerPort: 443 55 | protocol: TCP 56 | volumeMounts: 57 | - name: {{ include "zulip.fullname" . }}-persistent-storage 58 | mountPath: /data 59 | - name: {{ include "zulip.fullname" . }}-post-setup-scripts 60 | mountPath: /data/post-setup.d 61 | env: 62 | {{ include "zulip.env" . | nindent 12 }} 63 | resources: 64 | {{- toYaml .Values.resources | nindent 12 }} 65 | {{- if .Values.livenessProbe.enabled }} 66 | livenessProbe: 67 | httpGet: 68 | path: / 69 | port: http 70 | httpHeaders: 71 | - name: Host 72 | value: {{ .Values.zulip.environment.SETTING_EXTERNAL_HOST | quote }} 73 | initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} 74 | periodSeconds: {{ .Values.livenessProbe.periodSeconds }} 75 | timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} 76 | successThreshold: {{ .Values.livenessProbe.successThreshold }} 77 | failureThreshold: {{ .Values.livenessProbe.failureThreshold }} 78 | {{- end }} 79 | {{- if .Values.startupProbe.enabled }} 80 | startupProbe: 81 | httpGet: 82 | path: / 83 | port: http 84 | httpHeaders: 85 | - name: Host 86 | value: {{ .Values.zulip.environment.SETTING_EXTERNAL_HOST | quote }} 87 | initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} 88 | periodSeconds: {{ .Values.startupProbe.periodSeconds }} 89 | timeoutSeconds: {{ .Values.startupProbe.timeoutSeconds }} 90 | successThreshold: {{ .Values.startupProbe.successThreshold }} 91 | failureThreshold: {{ .Values.startupProbe.failureThreshold }} 92 | {{- end }} 93 | 94 | volumes: 95 | - name: {{ include "zulip.fullname" . }}-persistent-storage 96 | persistentVolumeClaim: 97 | claimName: {{ if .Values.zulip.persistence.existingClaim }}{{ .Values.zulip.persistence.existingClaim }}{{- else }}{{ template "zulip.fullname" . }}-data{{- end }} 98 | - name: {{ include "zulip.fullname" . }}-post-setup-scripts 99 | configMap: 100 | name: {{ include "zulip.fullname" . }}-post-setup-scripts 101 | defaultMode: 0750 102 | {{- with .Values.nodeSelector }} 103 | nodeSelector: 104 | {{- toYaml . | nindent 8 }} 105 | {{- end }} 106 | {{- with .Values.affinity }} 107 | affinity: 108 | {{- toYaml . | nindent 8 }} 109 | {{- end }} 110 | {{- with .Values.tolerations }} 111 | tolerations: 112 | {{- toYaml . | nindent 8 }} 113 | {{- end }} 114 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/values-local.yaml.example: -------------------------------------------------------------------------------- 1 | # Replace each occurrence of "set-secure-password" with a different random 2 | # password and uncomment it. 3 | # Replace each occurrence of "zulip.example.com" with the domain you want Zulip 4 | # to be reached on. 5 | 6 | zulip: 7 | # password: set-secure-password 8 | 9 | # Add any other environment variables you want to configure here. 10 | # based on; https://github.com/zulip/docker-zulip/blob/main/docker-compose.yml#L63 11 | # these values will be merged with db secrets and hosts/ports 12 | environment: 13 | # Domain 14 | SETTING_EXTERNAL_HOST: zulip.example.com 15 | ZULIP_AUTH_BACKENDS: 'EmailAuthBackend' 16 | 17 | # SMTP settings 18 | SECRETS_email_password: '123456789' 19 | SETTING_ZULIP_ADMINISTRATOR: 'admin@example.com' 20 | SETTING_EMAIL_HOST: '' # e.g. smtp.example.com 21 | SETTING_EMAIL_HOST_USER: 'noreply@example.com' 22 | SETTING_EMAIL_PORT: '587' 23 | SETTING_EMAIL_USE_SSL: 'False' 24 | SETTING_EMAIL_USE_TLS: 'True' 25 | 26 | ingress: 27 | enabled: true 28 | annotations: 29 | # Get certificates with cert-manager 30 | kubernetes.io/tls-acme: "true" 31 | # Uncomment this to use the cert-manager you added to Minikube 32 | # cert-manager.io/cluster-issuer: selfsigned 33 | hosts: 34 | - host: zulip.example.com 35 | paths: 36 | - path: / 37 | tls: 38 | - secretName: zulip-tls-secret 39 | hosts: 40 | - zulip.example.com 41 | 42 | memcached: 43 | # memcachedPassword: set-secure-password 44 | 45 | rabbitmq: 46 | auth: 47 | # password: set-secure-password 48 | # erlangCookie: set-secure-password 49 | 50 | redis: 51 | auth: 52 | # password: set-secure-password 53 | 54 | postgresql: 55 | auth: 56 | # # postgres admin user password 57 | # postgresqlPassword: set-secure-password 58 | # # postgres zulip user password 59 | # password: set-secure-password 60 | 61 | -------------------------------------------------------------------------------- /kubernetes/chart/zulip/values.yaml: -------------------------------------------------------------------------------- 1 | ## Default values for zulip. 2 | ## This is a YAML-formatted file. 3 | ## 4 | ## Declare variables to be passed into your templates. 5 | ## If you make any changes to the documentation here, regenerate the README.md 6 | ## with: 7 | ## 8 | ## ``` 9 | ## docker run --rm --volume "$(pwd):/helm-docs" -u $(id -u) jnorwood/helm-docs:latest 10 | ## ``` 11 | 12 | image: 13 | ## Defaults to https://hub.docker.com/zulip/docker-zulip, but can be 14 | ## overwritten with a full HTTPS address. 15 | repository: zulip/docker-zulip 16 | ## Pull policy for Zulip docker image. 17 | ## Ref: https://kubernetes.io/docs/concepts/containers/images/#pre-pulled-images 18 | pullPolicy: IfNotPresent 19 | ## Zulip image tag (immutable tags are recommended) 20 | tag: "10.3-0" 21 | 22 | ## Global Docker registry secret names as an array. 23 | imagePullSecrets: [] 24 | 25 | ## Partially override common.names.fullname template (will maintain the release name). 26 | nameOverride: "" 27 | 28 | ## Fully override common.names.fullname template. 29 | fullnameOverride: "" 30 | 31 | serviceAccount: 32 | ## Specifies whether a service account should be created. 33 | create: true 34 | ## Annotations to add to the service account. 35 | annotations: {} 36 | ## The name of the service account to use. 37 | ## If not set and create is true, a name is generated using the fullname template 38 | name: "" 39 | 40 | ## Custom labels to add to the Zulip StatefulSet. 41 | statefulSetLabels: {} 42 | ## Custom annotations to add to the Zulip StatefulSet. 43 | statefulSetAnnotations: {} 44 | 45 | ## Custom labels to add to the Zulip Pod. 46 | podLabels: {} 47 | ## Custom annotations to add to the Zulip Pod. 48 | podAnnotations: {} 49 | 50 | ## Can be used to override the default PodSecurityContext (fsGroup, 51 | ## runAsUser and runAsGroup) of the Zulip _Pod_. 52 | podSecurityContext: 53 | {} 54 | # fsGroup: 1000 55 | # runAsUser: 1000 56 | # runAsGroup: 1000 57 | 58 | ## Can be used to override the default SecurityContext of the Zulip _container_. 59 | securityContext: 60 | {} 61 | # capabilities: 62 | # drop: 63 | # - ALL 64 | # readOnlyRootFilesystem: true 65 | # runAsNonRoot: true 66 | # runAsUser: 1000 67 | 68 | ## Service type and port for the Kubernetes service that connects to 69 | ## Zulip. Default of ClusterIP needs an Ingress to be used. 70 | service: 71 | type: ClusterIP 72 | port: 80 73 | 74 | ingress: 75 | ## Enable this to use an Ingress to reach the Zulip service. 76 | enabled: false 77 | ## Can be used to add custom Ingress annotations. 78 | annotations: 79 | {} 80 | # kubernetes.io/ingress.class: nginx 81 | # kubernetes.io/tls-acme: "true" 82 | hosts: 83 | ## Host for the Ingress. Should be the same as 84 | ## `zulip.environment.SETTING_EXTERNAL_HOST`. 85 | - host: zulip.example.com 86 | ## Serves Zulip root of the chosen host domain. 87 | paths: 88 | - path: / 89 | ## Set a specific secret to read the TLS certificate from. If you 90 | ## use cert-manager, it will save the TLS secret here. If you do 91 | ## not, you need to manually create a secret with your TLS 92 | ## certificate. 93 | tls: [] 94 | # - secretName: chart-example-tls 95 | # hosts: 96 | # - zulip.example.com 97 | 98 | resources: 99 | {} 100 | ## We usually recommend not to specify default resources and to 101 | ## leave this as a conscious choice for the user. This also 102 | ## increases chances charts run on environments with little 103 | ## resources, such as Minikube. If you do want to specify resources, 104 | ## uncomment the following lines, adjust them as necessary, and 105 | ## remove the curly braces after 'resources:'. 106 | # limits: 107 | # cpu: 100m 108 | # memory: 128Mi 109 | # requests: 110 | # cpu: 100m 111 | # memory: 128Mi 112 | 113 | ## Optionally add a nodeSelector to the Zulip pod, so it runs on a specific 114 | ## node. 115 | ## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector 116 | nodeSelector: {} 117 | 118 | ## Tolerations for pod assignment. 119 | ## Ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ 120 | tolerations: [] 121 | 122 | ## Affinity for pod assignment. 123 | ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity 124 | affinity: {} 125 | 126 | zulip: 127 | ## Environment variables based on 128 | ## https://github.com/zulip/docker-zulip/blob/main/docker-compose.yml#L63 129 | environment: 130 | ## Disable the default Zulip requirement of HTTPS. HTTPS and 131 | ## certificates are managed by the Kubernetes cluster, so by 132 | ## default it's disabled inside the container. 133 | DISABLE_HTTPS: true 134 | ## Set SSL certificate generation to self-signed because 135 | ## Kubernetes manages the client-facing SSL certs. 136 | SSL_CERTIFICATE_GENERATION: self-signed 137 | ## Domain Zulip is hosted on. 138 | SETTING_EXTERNAL_HOST: zulip.example.com 139 | ## SMTP email password. 140 | SECRETS_email_password: "123456789" 141 | SETTING_ZULIP_ADMINISTRATOR: "admin@example.com" 142 | SETTING_EMAIL_HOST: "" # e.g. smtp.example.com 143 | SETTING_EMAIL_HOST_USER: "noreply@example.com" 144 | SETTING_EMAIL_PORT: "587" 145 | SETTING_EMAIL_USE_SSL: "False" 146 | SETTING_EMAIL_USE_TLS: "True" 147 | ZULIP_AUTH_BACKENDS: "EmailAuthBackend" 148 | ## If `persistence.existingClaim` is not set, a PVC (Persistent 149 | ## Volume Claim) is generated with these specifications. 150 | persistence: 151 | enabled: true 152 | accessMode: ReadWriteOnce 153 | size: 10Gi 154 | ## Set storageClass to use. 155 | storageClass: 156 | ## Use an already existing PVC 157 | # existingClaim: "" 158 | 159 | ## Liveness probe values. 160 | ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes 161 | livenessProbe: 162 | enabled: true 163 | initialDelaySeconds: 10 164 | periodSeconds: 10 165 | timeoutSeconds: 5 166 | failureThreshold: 3 167 | successThreshold: 1 168 | ## Startup probe values. 169 | ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes 170 | startupProbe: 171 | enabled: true 172 | initialDelaySeconds: 10 173 | periodSeconds: 10 174 | timeoutSeconds: 5 175 | failureThreshold: 30 176 | successThreshold: 1 177 | 178 | postSetup: 179 | ## The Docker entrypoint script runs commands from `/data/post-setup.d` after 180 | ## the Zulip application's Setup phase has completed. Scripts can be added here 181 | ## as `script_filename: