├── .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://microbadger.com/images/zulip/docker-zulip "Get your own image badge on microbadger.com") [](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 |   
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: