2018-05-22 20:29:33 +02:00
|
|
|
# Deployment options
|
|
|
|
|
|
|
|
The default Zulip installation instructions will install a complete
|
|
|
|
Zulip server, with all of the services it needs, on a single machine.
|
|
|
|
|
|
|
|
For production deployment, however, it's common to want to do
|
2021-08-20 21:53:28 +02:00
|
|
|
something more complicated. This page documents the options for doing so.
|
2018-05-22 20:29:33 +02:00
|
|
|
|
2018-10-17 00:19:51 +02:00
|
|
|
## Installing Zulip from Git
|
|
|
|
|
|
|
|
To install a development version of Zulip from Git, just clone the Git
|
|
|
|
repository from GitHub:
|
|
|
|
|
2021-08-20 07:09:04 +02:00
|
|
|
```bash
|
2018-10-17 00:19:51 +02:00
|
|
|
# First, install Git if you don't have it installed already
|
|
|
|
sudo apt install git
|
|
|
|
git clone https://github.com/zulip/zulip.git zulip-server-git
|
|
|
|
```
|
|
|
|
|
|
|
|
and then
|
2022-02-16 01:39:15 +01:00
|
|
|
[continue the normal installation instructions](install.md#step-2-install-zulip).
|
2023-01-17 04:33:42 +01:00
|
|
|
You can also [upgrade Zulip from Git](upgrade.md#upgrading-from-a-git-repository).
|
2018-10-17 00:19:51 +02:00
|
|
|
|
2021-09-01 00:15:31 +02:00
|
|
|
The most common use case for this is upgrading to `main` to get a
|
2020-07-08 21:14:51 +02:00
|
|
|
feature that hasn't made it into an official release yet (often
|
2021-08-20 21:53:28 +02:00
|
|
|
support for a new base OS release). See [upgrading to
|
2021-09-01 00:15:31 +02:00
|
|
|
main][upgrade-to-main] for notes on how `main` works and the
|
2020-07-08 21:14:51 +02:00
|
|
|
support story for it, and [upgrading to future
|
|
|
|
releases][upgrade-to-future-release] for notes on upgrading Zulip
|
|
|
|
afterwards.
|
|
|
|
|
|
|
|
In particular, we are always very glad to investigate problems with
|
2021-09-01 00:15:31 +02:00
|
|
|
installing Zulip from `main`; they are rare and help us ensure that
|
2020-07-08 21:14:51 +02:00
|
|
|
our next major release has a reliable install experience.
|
|
|
|
|
2023-01-17 03:02:58 +01:00
|
|
|
[upgrade-to-main]: modify.md#upgrading-to-main
|
|
|
|
[upgrade-to-future-release]: modify.md#upgrading-to-future-releases
|
2020-07-08 21:14:51 +02:00
|
|
|
|
2018-10-17 00:27:03 +02:00
|
|
|
## Zulip in Docker
|
|
|
|
|
|
|
|
Zulip has an officially supported, experimental
|
2021-08-20 21:53:28 +02:00
|
|
|
[docker image](https://github.com/zulip/docker-zulip). Please note
|
2022-02-24 00:17:21 +01:00
|
|
|
that Zulip's [normal installer](install.md) has been
|
2018-10-17 00:27:03 +02:00
|
|
|
extremely reliable for years, whereas the Docker image is new and has
|
|
|
|
rough edges, so we recommend the normal installer unless you have a
|
|
|
|
specific reason to prefer Docker.
|
|
|
|
|
2021-05-10 07:02:14 +02:00
|
|
|
## Advanced installer options
|
2021-04-18 04:01:20 +02:00
|
|
|
|
|
|
|
The Zulip installer supports the following advanced installer options
|
|
|
|
as well as those mentioned in the
|
2022-02-16 01:39:15 +01:00
|
|
|
[install](install.md#installer-options) documentation:
|
2021-04-18 04:01:20 +02:00
|
|
|
|
2021-08-20 21:45:39 +02:00
|
|
|
- `--postgresql-version`: Sets the version of PostgreSQL that will be
|
2023-12-21 21:46:51 +01:00
|
|
|
installed. We currently support PostgreSQL 12, 13, 14, 15, and 16, with 16
|
|
|
|
being the default.
|
2021-04-18 04:01:20 +02:00
|
|
|
|
2021-08-20 21:45:39 +02:00
|
|
|
- `--postgresql-database-name=exampledbname`: With this option, you
|
2021-03-28 05:41:50 +02:00
|
|
|
can customize the default database name. If you do not set this. The
|
|
|
|
default database name will be `zulip`. This setting can only be set
|
|
|
|
on the first install.
|
|
|
|
|
2021-08-20 21:45:39 +02:00
|
|
|
- `--postgresql-database-user=exampledbuser`: With this option, you
|
2021-03-28 05:41:50 +02:00
|
|
|
can customize the default database user. If you do not set this. The
|
|
|
|
default database user will be `zulip`. This setting can only be set
|
|
|
|
on the first install.
|
|
|
|
|
2021-08-20 21:45:39 +02:00
|
|
|
- `--postgresql-missing-dictionaries`: Set
|
2023-05-31 00:28:07 +02:00
|
|
|
`postgresql.missing_dictionaries` ([docs][missing-dicts]) in the
|
2021-04-18 04:01:20 +02:00
|
|
|
Zulip settings, which omits some configuration needed for full-text
|
|
|
|
indexing. This should be used with [cloud managed databases like
|
|
|
|
RDS](#using-zulip-with-amazon-rds-as-the-database). This option
|
|
|
|
conflicts with `--no-overwrite-settings`.
|
|
|
|
|
2021-08-20 21:45:39 +02:00
|
|
|
- `--no-init-db`: This option instructs the installer to not do any
|
2021-04-18 04:01:20 +02:00
|
|
|
database initialization. This should be used when you already have a
|
|
|
|
Zulip database.
|
|
|
|
|
2021-08-20 21:45:39 +02:00
|
|
|
- `--no-overwrite-settings`: This option preserves existing
|
2021-04-18 04:01:20 +02:00
|
|
|
`/etc/zulip` configuration files.
|
|
|
|
|
2023-05-31 00:28:07 +02:00
|
|
|
[missing-dicts]: #missing_dictionaries
|
|
|
|
|
2021-06-03 01:42:15 +02:00
|
|
|
## Installing on an existing server
|
|
|
|
|
|
|
|
Zulip's installation process assumes it is the only application
|
|
|
|
running on the server; though installing alongside other applications
|
|
|
|
is not recommended, we do have [some notes on the
|
2022-02-24 00:17:21 +01:00
|
|
|
process](install-existing-server.md).
|
2021-06-03 01:42:15 +02:00
|
|
|
|
2023-01-30 21:30:34 +01:00
|
|
|
## Deployment hooks
|
|
|
|
|
|
|
|
Zulip's upgrades have a hook system which allows for arbitrary
|
|
|
|
user-configured actions to run before and after an upgrade; see the
|
2023-01-28 02:00:15 +01:00
|
|
|
[upgrading documentation](upgrade.md#deployment-hooks) for details on
|
|
|
|
how to write your own.
|
|
|
|
|
2023-05-30 20:04:29 +02:00
|
|
|
### Zulip message deploy hook
|
|
|
|
|
|
|
|
Zulip can use its deploy hooks to send a message immediately before and after
|
|
|
|
conducting an upgrade. To configure this:
|
|
|
|
|
|
|
|
1. Add `, zulip::hooks::zulip_notify` to the `puppet_classes` line in
|
|
|
|
`/etc/zulip/zulip.conf`
|
|
|
|
1. Add a `[zulip_notify]` section to `/etc/zulip/zulip.conf`:
|
|
|
|
```ini
|
|
|
|
[zulip_notify]
|
|
|
|
bot_email = your-bot@zulip.example.com
|
|
|
|
server = zulip.example.com
|
|
|
|
stream = deployments
|
|
|
|
```
|
2023-05-31 00:28:07 +02:00
|
|
|
1. Add the [api key](https://zulip.com/api/api-keys#get-a-bots-api-key) for the
|
2023-05-30 20:04:29 +02:00
|
|
|
bot user in `/etc/zulip/zulip-secrets.conf` as `zulip_release_api_key`:
|
|
|
|
```ini
|
|
|
|
# Replace with your own bot's token, found in the Zulip UI
|
|
|
|
zulip_release_api_key = abcd1234E6DK0F7pNSqaMSuzd8C5i7Eu
|
|
|
|
```
|
|
|
|
1. As root, run `/home/zulip/deployments/current/scripts/zulip-puppet-apply`.
|
2023-01-28 02:00:15 +01:00
|
|
|
|
|
|
|
### Sentry deploy hook
|
|
|
|
|
|
|
|
Zulip can use its deploy hooks to create [Sentry
|
|
|
|
releases][sentry-release], which can help associate Sentry [error
|
|
|
|
logging][sentry-error] with specific releases. If you are deploying
|
|
|
|
Zulip from Git, it can be aware of which Zulip commits are associated
|
|
|
|
with the release, and help identify which commits might be relevant to
|
|
|
|
an error.
|
|
|
|
|
|
|
|
To do so:
|
|
|
|
|
|
|
|
1. Enable [Sentry error logging][sentry-error].
|
|
|
|
2. Add a new [internal Sentry integration][sentry-internal] named
|
|
|
|
"Release annotator".
|
|
|
|
3. Grant the internal integration the [permissions][sentry-perms] of
|
|
|
|
"Admin" on "Release".
|
|
|
|
4. Add `, zulip::hooks::sentry` to the `puppet_classes` line in `/etc/zulip/zulip.conf`
|
|
|
|
5. Add a `[sentry]` section to `/etc/zulip/zulip.conf`:
|
|
|
|
```ini
|
|
|
|
[sentry]
|
|
|
|
organization = your-organization-name
|
|
|
|
project = your-project-name
|
|
|
|
```
|
2023-05-30 19:55:02 +02:00
|
|
|
6. Add the [authentication token][sentry-tokens] for your internal Sentry integration
|
2023-01-28 02:00:15 +01:00
|
|
|
to your `/etc/zulip/zulip-secrets.conf`:
|
|
|
|
```ini
|
|
|
|
# Replace with your own token, found in Sentry
|
|
|
|
sentry_release_auth_token = 6c12f890c1c864666e64ee9c959c4552b3de473a076815e7669f53793fa16afc
|
|
|
|
```
|
|
|
|
7. As root, run `/home/zulip/deployments/current/scripts/zulip-puppet-apply`.
|
|
|
|
|
|
|
|
If you are deploying Zulip from Git, you will also need to:
|
|
|
|
|
|
|
|
1. In your Zulip project, add the [GitHub integration][sentry-github].
|
|
|
|
2. Configure the `zulip/zulip` GitHub project for your Sentry project.
|
|
|
|
You should do this even if you are deploying a private fork of
|
|
|
|
Zulip.
|
|
|
|
3. Additionally grant the internal integration "Read & Write" on
|
|
|
|
"Organization"; this is necessary to associate the commits with the
|
|
|
|
release.
|
|
|
|
|
|
|
|
[sentry-release]: https://docs.sentry.io/product/releases/
|
|
|
|
[sentry-error]: ../subsystems/logging.md#sentry-error-logging
|
|
|
|
[sentry-github]: https://docs.sentry.io/product/integrations/source-code-mgmt/github/
|
|
|
|
[sentry-internal]: https://docs.sentry.io/product/integrations/integration-platform/internal-integration/
|
|
|
|
[sentry-perms]: https://docs.sentry.io/product/integrations/integration-platform/#permissions
|
|
|
|
[sentry-tokens]: https://docs.sentry.io/product/integrations/integration-platform/internal-integration#auth-tokens
|
2023-01-30 21:30:34 +01:00
|
|
|
|
2018-05-22 20:29:33 +02:00
|
|
|
## Running Zulip's service dependencies on different machines
|
|
|
|
|
|
|
|
Zulip has full support for each top-level service living on its own
|
|
|
|
machine.
|
|
|
|
|
2020-10-26 22:27:53 +01:00
|
|
|
You can configure remote servers for PostgreSQL, RabbitMQ, Redis,
|
2018-05-22 20:29:33 +02:00
|
|
|
in `/etc/zulip/settings.py`; just search for the service name in that
|
|
|
|
file and you'll find inline documentation in comments for how to
|
|
|
|
configure it.
|
|
|
|
|
|
|
|
Since some of these services require some configuration on the node
|
2020-10-26 22:27:53 +01:00
|
|
|
itself (e.g. installing our PostgreSQL extensions), we have designed
|
2020-10-23 02:43:28 +02:00
|
|
|
the Puppet configuration that Zulip uses for installing and upgrading
|
2018-05-22 20:29:33 +02:00
|
|
|
configuration to be completely modular.
|
|
|
|
|
2020-10-20 02:49:54 +02:00
|
|
|
For example, to install a Zulip Redis server on a machine, you can run
|
|
|
|
the following after unpacking a Zulip production release tarball:
|
2018-05-22 20:29:33 +02:00
|
|
|
|
2021-08-20 07:09:04 +02:00
|
|
|
```bash
|
2020-10-20 02:49:54 +02:00
|
|
|
env PUPPET_CLASSES=zulip::profile::redis ./scripts/setup/install
|
2018-05-22 20:29:33 +02:00
|
|
|
```
|
|
|
|
|
2020-10-20 02:49:54 +02:00
|
|
|
All puppet modules under `zulip::profile` are allowed to be configured
|
2021-08-20 21:53:28 +02:00
|
|
|
stand-alone on a host. You can see most likely manifests you might
|
2020-10-20 02:49:54 +02:00
|
|
|
want to choose in the list of includes in [the main manifest for the
|
2020-10-20 03:49:23 +02:00
|
|
|
default all-in-one Zulip server][standalone.pp], though it's also
|
2020-10-20 02:49:54 +02:00
|
|
|
possible to subclass some of the lower-level manifests defined in that
|
2021-08-20 21:53:28 +02:00
|
|
|
directory if you want to customize. A good example of doing this is
|
2024-02-06 21:40:19 +01:00
|
|
|
in the [kandra Puppet configuration][zulipchat-puppet] that we use
|
2020-10-20 02:49:54 +02:00
|
|
|
as part of managing chat.zulip.org and zulip.com.
|
2018-05-22 20:29:33 +02:00
|
|
|
|
|
|
|
### Using Zulip with Amazon RDS as the database
|
|
|
|
|
2019-12-12 10:50:04 +01:00
|
|
|
You can use DBaaS services like Amazon RDS for the Zulip database.
|
|
|
|
The experience is slightly degraded, in that most DBaaS provides don't
|
|
|
|
include useful dictionary files in their installations and don't
|
|
|
|
provide a way to provide them yourself, resulting in a degraded
|
|
|
|
[full-text search](../subsystems/full-text-search.md) experience
|
|
|
|
around issues dictionary files are relevant (e.g. stemming).
|
|
|
|
|
|
|
|
You also need to pass some extra options to the Zulip installer in
|
|
|
|
order to avoid it throwing an error when Zulip attempts to configure
|
|
|
|
the database's dictionary files for full-text search; the details are
|
|
|
|
below.
|
|
|
|
|
2020-08-11 01:47:54 +02:00
|
|
|
#### Step 1: Set up Zulip
|
2019-12-12 10:50:04 +01:00
|
|
|
|
2022-02-24 00:17:21 +01:00
|
|
|
Follow the [standard instructions](install.md), with one
|
2021-08-20 21:53:28 +02:00
|
|
|
change. When running the installer, pass the `--no-init-db`
|
2019-12-12 10:50:04 +01:00
|
|
|
flag, e.g.:
|
|
|
|
|
2021-08-20 07:09:04 +02:00
|
|
|
```bash
|
2019-12-12 10:50:04 +01:00
|
|
|
sudo -s # If not already root
|
|
|
|
./zulip-server-*/scripts/setup/install --certbot \
|
|
|
|
--email=YOUR_EMAIL --hostname=YOUR_HOSTNAME \
|
2020-10-26 22:35:47 +01:00
|
|
|
--no-init-db --postgresql-missing-dictionaries
|
2019-12-12 10:50:04 +01:00
|
|
|
```
|
|
|
|
|
2020-10-26 22:27:53 +01:00
|
|
|
The script also installs and starts PostgreSQL on the server by
|
2019-12-12 10:50:04 +01:00
|
|
|
default. We don't need it, so run the following command to
|
2020-10-26 22:27:53 +01:00
|
|
|
stop and disable the local PostgreSQL server.
|
2019-12-12 10:50:04 +01:00
|
|
|
|
2021-08-20 07:09:04 +02:00
|
|
|
```bash
|
2019-12-12 10:50:04 +01:00
|
|
|
sudo service postgresql stop
|
|
|
|
sudo update-rc.d postgresql disable
|
|
|
|
```
|
|
|
|
|
|
|
|
This complication will be removed in a future version.
|
|
|
|
|
2020-10-26 22:27:53 +01:00
|
|
|
#### Step 2: Create the PostgreSQL database
|
2019-12-12 10:50:04 +01:00
|
|
|
|
2020-10-26 22:27:53 +01:00
|
|
|
Access an administrative `psql` shell on your PostgreSQL database, and
|
2019-12-12 10:50:04 +01:00
|
|
|
run the commands in `scripts/setup/create-db.sql` to:
|
|
|
|
|
2021-08-20 21:45:39 +02:00
|
|
|
- Create a database called `zulip`.
|
|
|
|
- Create a user called `zulip`.
|
|
|
|
- Now log in with the `zulip` user to create a schema called
|
2019-12-12 10:50:04 +01:00
|
|
|
`zulip` in the `zulip` database. You might have to grant `create`
|
|
|
|
privileges first for the `zulip` user to do this.
|
|
|
|
|
2020-10-26 22:27:53 +01:00
|
|
|
Depending on how authentication works for your PostgreSQL installation,
|
2019-12-12 10:50:04 +01:00
|
|
|
you may also need to set a password for the Zulip user, generate a
|
|
|
|
client certificate, or similar; consult the documentation for your
|
|
|
|
database provider for the available options.
|
|
|
|
|
2020-10-26 22:27:53 +01:00
|
|
|
#### Step 3: Configure Zulip to use the PostgreSQL database
|
2019-12-12 10:50:04 +01:00
|
|
|
|
|
|
|
In `/etc/zulip/settings.py` on your Zulip server, configure the
|
2020-10-26 22:27:53 +01:00
|
|
|
following settings with details for how to connect to your PostgreSQL
|
2021-08-20 21:53:28 +02:00
|
|
|
server. Your database provider should provide these details.
|
2019-12-12 10:50:04 +01:00
|
|
|
|
2021-08-20 21:45:39 +02:00
|
|
|
- `REMOTE_POSTGRES_HOST`: Name or IP address of the PostgreSQL server.
|
|
|
|
- `REMOTE_POSTGRES_PORT`: Port on the PostgreSQL server.
|
|
|
|
- `REMOTE_POSTGRES_SSLMODE`: SSL Mode used to connect to the server.
|
2019-12-12 10:50:04 +01:00
|
|
|
|
|
|
|
If you're using password authentication, you should specify the
|
|
|
|
password of the `zulip` user in /etc/zulip/zulip-secrets.conf as
|
|
|
|
follows:
|
|
|
|
|
2021-08-20 07:09:04 +02:00
|
|
|
```ini
|
2019-12-12 10:50:04 +01:00
|
|
|
postgres_password = abcd1234
|
|
|
|
```
|
|
|
|
|
2019-12-13 08:16:55 +01:00
|
|
|
Now complete the installation by running the following commands.
|
2019-12-12 10:50:04 +01:00
|
|
|
|
2021-08-20 07:09:04 +02:00
|
|
|
```bash
|
2020-10-26 22:27:53 +01:00
|
|
|
# Ask Zulip installer to initialize the PostgreSQL database.
|
2019-12-13 08:16:55 +01:00
|
|
|
su zulip -c '/home/zulip/deployments/current/scripts/setup/initialize-database'
|
2019-12-12 10:50:04 +01:00
|
|
|
|
|
|
|
# And then generate a realm creation link:
|
|
|
|
su zulip -c '/home/zulip/deployments/current/manage.py generate_realm_creation_link'
|
|
|
|
```
|
2018-05-22 20:29:33 +02:00
|
|
|
|
2019-06-17 21:16:34 +02:00
|
|
|
## Using an alternate port
|
|
|
|
|
|
|
|
If you'd like your Zulip server to use an HTTPS port other than 443, you can
|
|
|
|
configure that as follows:
|
|
|
|
|
|
|
|
1. Edit `EXTERNAL_HOST` in `/etc/zulip/settings.py`, which controls how
|
|
|
|
the Zulip server reports its own URL, and restart the Zulip server
|
|
|
|
with `/home/zulip/deployments/current/scripts/restart-server`.
|
|
|
|
1. Add the following block to `/etc/zulip/zulip.conf`:
|
|
|
|
|
2021-08-20 22:54:08 +02:00
|
|
|
```ini
|
|
|
|
[application_server]
|
|
|
|
nginx_listen_port = 12345
|
|
|
|
```
|
2019-06-17 21:16:34 +02:00
|
|
|
|
|
|
|
1. As root, run
|
2021-08-20 22:54:08 +02:00
|
|
|
`/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This
|
|
|
|
will convert Zulip's main `nginx` configuration file to use your new
|
|
|
|
port.
|
2019-06-17 21:16:34 +02:00
|
|
|
|
|
|
|
We also have documentation for a Zulip server [using HTTP][using-http] for use
|
|
|
|
behind reverse proxies.
|
|
|
|
|
2022-02-24 23:25:36 +01:00
|
|
|
[using-http]: #configuring-zulip-to-allow-http
|
2019-06-17 21:16:34 +02:00
|
|
|
|
2021-11-17 22:17:56 +01:00
|
|
|
## Customizing the outgoing HTTP proxy
|
2020-10-15 11:43:44 +02:00
|
|
|
|
2021-11-17 22:17:56 +01:00
|
|
|
To protect against [SSRF][ssrf], Zulip 4.8 and above default to
|
|
|
|
routing all outgoing HTTP and HTTPS traffic through
|
|
|
|
[Smokescreen][smokescreen], an HTTP `CONNECT` proxy; this includes
|
|
|
|
outgoing webhooks, website previews, and mobile push notifications.
|
2022-01-08 00:19:31 +01:00
|
|
|
By default, the Camo image proxy will be automatically configured to
|
|
|
|
use a custom outgoing proxy, but does not use Smokescreen by default
|
|
|
|
because Camo includes similar logic to deny access to private
|
|
|
|
subnets. You can [override][proxy.enable_for_camo] this default
|
|
|
|
configuration if desired.
|
2020-10-15 11:43:44 +02:00
|
|
|
|
2021-11-17 22:17:56 +01:00
|
|
|
To use a custom outgoing proxy:
|
2021-05-13 20:03:57 +02:00
|
|
|
|
2020-10-15 11:43:44 +02:00
|
|
|
1. Add the following block to `/etc/zulip/zulip.conf`, substituting in
|
|
|
|
your proxy's hostname/IP and port:
|
|
|
|
|
2021-08-20 22:54:08 +02:00
|
|
|
```ini
|
|
|
|
[http_proxy]
|
|
|
|
host = 127.0.0.1
|
|
|
|
port = 4750
|
|
|
|
```
|
2020-10-15 11:43:44 +02:00
|
|
|
|
|
|
|
1. As root, run
|
2021-08-20 21:53:28 +02:00
|
|
|
`/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This
|
2021-11-17 22:17:56 +01:00
|
|
|
will reconfigure and restart Zulip.
|
|
|
|
|
|
|
|
If you have a deployment with multiple frontend servers, or wish to
|
|
|
|
install Smokescreen on a separate host, you can apply the
|
|
|
|
`zulip::profile::smokescreen` Puppet class on that host, and follow
|
|
|
|
the above steps, setting the `[http_proxy]` block to point to that
|
|
|
|
host.
|
|
|
|
|
|
|
|
If you wish to disable the outgoing proxy entirely, follow the above
|
|
|
|
steps, configuring an empty `host` value.
|
|
|
|
|
|
|
|
Optionally, you can also configure the [Smokescreen ACL
|
|
|
|
list][smokescreen-acls]. By default, Smokescreen denies access to all
|
|
|
|
[non-public IP
|
|
|
|
addresses](https://en.wikipedia.org/wiki/Private_network), including
|
|
|
|
127.0.0.1, but allows traffic to all public Internet hosts.
|
2021-02-26 23:40:18 +01:00
|
|
|
|
2021-11-17 22:17:56 +01:00
|
|
|
In Zulip 4.7 and older, to enable SSRF protection via Smokescreen, you
|
|
|
|
will need to explicitly add the `zulip::profile::smokescreen` Puppet
|
|
|
|
class, and configure the `[http_proxy]` block as above.
|
2020-10-15 11:43:44 +02:00
|
|
|
|
2022-02-16 01:39:15 +01:00
|
|
|
[proxy.enable_for_camo]: #enable_for_camo
|
2020-10-15 11:43:44 +02:00
|
|
|
[smokescreen]: https://github.com/stripe/smokescreen
|
2021-05-13 20:03:57 +02:00
|
|
|
[smokescreen-acls]: https://github.com/stripe/smokescreen#acls
|
2021-02-26 23:40:18 +01:00
|
|
|
[ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
|
2020-10-15 11:43:44 +02:00
|
|
|
|
2022-03-23 21:47:53 +01:00
|
|
|
### S3 file storage requests and outgoing proxies
|
|
|
|
|
|
|
|
By default, the [S3 file storage backend][s3] bypasses the Smokescreen
|
|
|
|
proxy, because when running on EC2 it may require metadata from the
|
|
|
|
IMDS metadata endpoint, which resides on the internal IP address
|
|
|
|
169.254.169.254 and would thus be blocked by Smokescreen.
|
|
|
|
|
|
|
|
If your S3-compatible storage backend requires use of Smokescreen or
|
|
|
|
some other proxy, you can override this default by setting
|
|
|
|
`S3_SKIP_PROXY = False` in `/etc/zulip/settings.py`.
|
|
|
|
|
|
|
|
[s3]: upload-backends.md#s3-backend-configuration
|
|
|
|
|
2018-05-22 20:29:33 +02:00
|
|
|
## Putting the Zulip application behind a reverse proxy
|
|
|
|
|
2018-08-02 17:35:45 +02:00
|
|
|
Zulip is designed to support being run behind a reverse proxy server.
|
2018-11-16 07:26:59 +01:00
|
|
|
This section contains notes on the configuration required with
|
|
|
|
variable reverse proxy implementations.
|
|
|
|
|
|
|
|
### Installer options
|
|
|
|
|
|
|
|
If your Zulip server will not be on the public Internet, we recommend,
|
|
|
|
installing with the `--self-signed-cert` option (rather than the
|
2022-02-08 00:13:33 +01:00
|
|
|
`--certbot` option), since Certbot requires the server to be on the
|
2018-11-16 07:26:59 +01:00
|
|
|
public Internet.
|
|
|
|
|
|
|
|
#### Configuring Zulip to allow HTTP
|
|
|
|
|
2022-11-10 02:05:15 +01:00
|
|
|
Zulip requires clients to connect to Zulip servers over the secure
|
|
|
|
HTTPS protocol; the insecure HTTP protocol is not supported. However,
|
|
|
|
we do support using a reverse proxy that speaks HTTPS to clients and
|
|
|
|
connects to the Zulip server over HTTP; this can be secure when the
|
|
|
|
Zulip server is not directly exposed to the public Internet.
|
|
|
|
|
|
|
|
After installing the Zulip server as [described
|
|
|
|
above](#installer-options), you can configure Zulip to accept HTTP
|
|
|
|
requests from a reverse proxy as follows:
|
2018-11-16 07:26:59 +01:00
|
|
|
|
|
|
|
1. Add the following block to `/etc/zulip/zulip.conf`:
|
|
|
|
|
2021-08-20 22:54:08 +02:00
|
|
|
```ini
|
|
|
|
[application_server]
|
|
|
|
http_only = true
|
|
|
|
```
|
2018-11-16 07:26:59 +01:00
|
|
|
|
|
|
|
1. As root, run
|
2021-08-20 22:54:08 +02:00
|
|
|
`/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This
|
|
|
|
will convert Zulip's main `nginx` configuration file to allow HTTP
|
|
|
|
instead of HTTPS.
|
2018-11-16 07:26:59 +01:00
|
|
|
|
|
|
|
1. Finally, restart the Zulip server, using
|
2021-08-20 22:54:08 +02:00
|
|
|
`/home/zulip/deployments/current/scripts/restart-server`.
|
2018-11-16 07:26:59 +01:00
|
|
|
|
docs: Clarify that trust of X-Fowarded-Proto is also necessary.
Previously, `X-Forwarded-Proto` did not need to be set, and failure to
set `loadbalancer.ips` would merely result in bad IP-address
rate-limiting and incorrect access logs; after 0935d388f053, however,
failure to do either of those, if Zulip is deployed with `http_only`,
will lead to infinite redirect loops after login. These are
accompanied by a misleading error, from Tornado, of:
Forbidden (Origin checking failed - https://zulip.example.com does not match any trusted origins.): /json/events
This is most common with Docker deployments, where deployments use
another docker container, such as nginx or Traefik, to do SSL
termination. See zulip/docker-zulip#403.
Update the documentation to reinforce that `loadbalancer.ips` also
controls trust of `X-Forwarded-Proto`, and that failure to set it will
cause the application to not function correctly.
2023-06-14 04:01:53 +02:00
|
|
|
Note that Zulip must be able to accurately determine if its connection to the
|
|
|
|
client was over HTTPS or not; if you enable `http_only`, it is very important
|
|
|
|
that you correctly configure Zulip to trust the `X-Forwarded-Proto` header from
|
|
|
|
its proxy (see the next section), or clients may see infinite redirects.
|
|
|
|
|
2021-11-18 02:31:09 +01:00
|
|
|
#### Configuring Zulip to trust proxies
|
|
|
|
|
docs: Clarify that trust of X-Fowarded-Proto is also necessary.
Previously, `X-Forwarded-Proto` did not need to be set, and failure to
set `loadbalancer.ips` would merely result in bad IP-address
rate-limiting and incorrect access logs; after 0935d388f053, however,
failure to do either of those, if Zulip is deployed with `http_only`,
will lead to infinite redirect loops after login. These are
accompanied by a misleading error, from Tornado, of:
Forbidden (Origin checking failed - https://zulip.example.com does not match any trusted origins.): /json/events
This is most common with Docker deployments, where deployments use
another docker container, such as nginx or Traefik, to do SSL
termination. See zulip/docker-zulip#403.
Update the documentation to reinforce that `loadbalancer.ips` also
controls trust of `X-Forwarded-Proto`, and that failure to set it will
cause the application to not function correctly.
2023-06-14 04:01:53 +02:00
|
|
|
Before placing Zulip behind a reverse proxy, it needs to be configured to trust
|
|
|
|
the client IP addresses that the proxy reports via the `X-Forwarded-For` header,
|
|
|
|
and the protocol reported by the `X-Forwarded-Proto` header. This is important
|
|
|
|
to have accurate IP addresses in server logs, as well as in notification emails
|
|
|
|
which are sent to end users. Zulip doesn't default to trusting all
|
|
|
|
`X-Forwarded-*` headers, because doing so would allow clients to spoof any IP
|
|
|
|
address, and claim connections were over a secure connection when they were not;
|
|
|
|
we specify which IP addresses are the Zulip server's incoming proxies, so we
|
|
|
|
know which `X-Forwarded-*` headers to trust.
|
2021-11-18 02:31:09 +01:00
|
|
|
|
|
|
|
1. Determine the IP addresses of all reverse proxies you are setting up, as seen
|
|
|
|
from the Zulip host. Depending on your network setup, these may not be the
|
2022-06-27 20:00:31 +02:00
|
|
|
same as the public IP addresses of the reverse proxies. These can also be IP
|
|
|
|
address ranges, as expressed in CIDR notation.
|
2021-11-18 02:31:09 +01:00
|
|
|
|
|
|
|
1. Add the following block to `/etc/zulip/zulip.conf`.
|
|
|
|
|
|
|
|
```ini
|
|
|
|
[loadbalancer]
|
|
|
|
# Use the IP addresses you determined above, separated by commas.
|
|
|
|
ips = 192.168.0.100
|
|
|
|
```
|
|
|
|
|
|
|
|
1. Reconfigure Zulip with these settings. As root, run
|
|
|
|
`/home/zulip/deployments/current/scripts/zulip-puppet-apply`. This will
|
|
|
|
adjust Zulip's `nginx` configuration file to accept the `X-Forwarded-For`
|
|
|
|
header when it is sent from one of the reverse proxy IPs.
|
|
|
|
|
|
|
|
1. Finally, restart the Zulip server, using
|
|
|
|
`/home/zulip/deployments/current/scripts/restart-server`.
|
|
|
|
|
2018-11-16 07:26:59 +01:00
|
|
|
### nginx configuration
|
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
Below is a working example of a full nginx configuration. It assumes
|
|
|
|
that your Zulip server sits at `https://10.10.10.10:443`; see
|
|
|
|
[above](#configuring-zulip-to-allow-http) to switch to HTTP.
|
|
|
|
|
|
|
|
1. Follow the instructions to [configure Zulip to trust
|
|
|
|
proxies](#configuring-zulip-to-trust-proxies).
|
|
|
|
|
|
|
|
1. Configure the root `nginx.conf` file. We recommend using
|
|
|
|
`/etc/nginx/nginx.conf` from your Zulip server for our recommended
|
|
|
|
settings. E.g. if you don't set `client_max_body_size`, it won't be
|
|
|
|
possible to upload large files to your Zulip server.
|
|
|
|
|
|
|
|
1. Configure the `nginx` site-specific configuration (in
|
|
|
|
`/etc/nginx/sites-available`) for the Zulip app. The following
|
|
|
|
example is a good starting point:
|
|
|
|
|
|
|
|
```nginx
|
|
|
|
server {
|
|
|
|
listen 80;
|
|
|
|
listen [::]:80;
|
|
|
|
location / {
|
|
|
|
return 301 https://$host$request_uri;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
server {
|
|
|
|
listen 443 ssl http2;
|
|
|
|
listen [::]:443 ssl http2;
|
|
|
|
server_name zulip.example.com;
|
|
|
|
|
|
|
|
ssl_certificate /etc/letsencrypt/live/zulip.example.com/fullchain.pem;
|
|
|
|
ssl_certificate_key /etc/letsencrypt/live/zulip.example.com/privkey.pem;
|
|
|
|
|
|
|
|
location / {
|
|
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
2023-03-06 20:18:09 +01:00
|
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
2023-07-25 07:31:50 +02:00
|
|
|
proxy_set_header Host $host;
|
2022-04-09 00:00:27 +02:00
|
|
|
proxy_http_version 1.1;
|
|
|
|
proxy_buffering off;
|
|
|
|
proxy_read_timeout 20m;
|
|
|
|
proxy_pass https://10.10.10.10:443;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
2018-05-22 20:29:33 +02:00
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
Don't forget to update `server_name`, `ssl_certificate`,
|
|
|
|
`ssl_certificate_key` and `proxy_pass` with the appropriate values
|
|
|
|
for your deployment.
|
2021-11-18 02:31:09 +01:00
|
|
|
|
2021-09-01 00:15:31 +02:00
|
|
|
[nginx-proxy-longpolling-config]: https://github.com/zulip/zulip/blob/main/puppet/zulip/files/nginx/zulip-include-common/proxy_longpolling
|
|
|
|
[standalone.pp]: https://github.com/zulip/zulip/blob/main/puppet/zulip/manifests/profile/standalone.pp
|
2024-02-06 21:40:19 +01:00
|
|
|
[zulipchat-puppet]: https://github.com/zulip/zulip/tree/main/puppet/kandra/manifests
|
2018-05-22 20:29:33 +02:00
|
|
|
|
2020-06-14 04:18:07 +02:00
|
|
|
### Apache2 configuration
|
|
|
|
|
|
|
|
Below is a working example of a full Apache2 configuration. It assumes
|
2022-04-09 00:00:27 +02:00
|
|
|
that your Zulip server sits at `https://internal.zulip.hostname:443`.
|
|
|
|
Note that if you wish to use SSL to connect to the Zulip server,
|
|
|
|
Apache requires you use the hostname, not the IP address; see
|
|
|
|
[above](#configuring-zulip-to-allow-http) to switch to HTTP.
|
2020-06-14 04:18:07 +02:00
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
1. Follow the instructions to [configure Zulip to trust
|
|
|
|
proxies](#configuring-zulip-to-trust-proxies).
|
2020-06-14 04:18:07 +02:00
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
1. Set `USE_X_FORWARDED_HOST = True` in `/etc/zulip/settings.py` and
|
|
|
|
restart Zulip.
|
2020-06-14 04:18:07 +02:00
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
1. Enable some required Apache modules:
|
2020-06-14 04:18:07 +02:00
|
|
|
|
2023-05-31 01:18:00 +02:00
|
|
|
```bash
|
2022-04-09 00:00:27 +02:00
|
|
|
a2enmod ssl proxy proxy_http headers rewrite
|
|
|
|
```
|
2021-11-18 02:31:09 +01:00
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
1. Create an Apache2 virtual host configuration file, similar to the
|
2021-08-20 21:53:28 +02:00
|
|
|
following. Place it the appropriate path for your Apache2
|
2020-06-14 04:18:07 +02:00
|
|
|
installation and enable it (E.g. if you use Debian or Ubuntu, then
|
|
|
|
place it in `/etc/apache2/sites-available/zulip.example.com.conf`
|
2021-09-08 00:23:24 +02:00
|
|
|
and then run
|
|
|
|
`a2ensite zulip.example.com && systemctl reload apache2`):
|
2020-06-14 04:18:07 +02:00
|
|
|
|
2021-08-20 22:54:08 +02:00
|
|
|
```apache
|
|
|
|
<VirtualHost *:80>
|
|
|
|
ServerName zulip.example.com
|
|
|
|
RewriteEngine On
|
|
|
|
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
|
|
|
</VirtualHost>
|
|
|
|
|
|
|
|
<VirtualHost *:443>
|
|
|
|
ServerName zulip.example.com
|
|
|
|
|
|
|
|
RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
|
|
|
|
|
|
|
|
RewriteEngine On
|
2022-04-09 00:00:27 +02:00
|
|
|
RewriteRule /(.*) https://internal.zulip.hostname:443/$1 [P,L]
|
2021-08-20 22:54:08 +02:00
|
|
|
|
|
|
|
<Location />
|
|
|
|
Require all granted
|
2022-04-09 00:00:27 +02:00
|
|
|
ProxyPass https://internal.zulip.hostname:443/ timeout=1200
|
2021-08-20 22:54:08 +02:00
|
|
|
</Location>
|
|
|
|
|
|
|
|
SSLEngine on
|
|
|
|
SSLProxyEngine on
|
|
|
|
SSLCertificateFile /etc/letsencrypt/live/zulip.example.com/fullchain.pem
|
|
|
|
SSLCertificateKeyFile /etc/letsencrypt/live/zulip.example.com/privkey.pem
|
2022-04-09 00:00:27 +02:00
|
|
|
# This file can be found in ~zulip/deployments/current/puppet/zulip/files/nginx/dhparam.pem
|
2021-08-20 22:54:08 +02:00
|
|
|
SSLOpenSSLConfCmd DHParameters "/etc/nginx/dhparam.pem"
|
|
|
|
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
|
|
|
|
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
|
|
|
SSLHonorCipherOrder off
|
|
|
|
SSLSessionTickets off
|
|
|
|
Header set Strict-Transport-Security "max-age=31536000"
|
|
|
|
</VirtualHost>
|
|
|
|
```
|
2020-06-14 04:18:07 +02:00
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
Don't forget to update `ServerName`, `RewriteRule`, `ProxyPass`,
|
|
|
|
`SSLCertificateFile`, and `SSLCertificateKeyFile` as are
|
|
|
|
appropriate for your deployment.
|
|
|
|
|
2018-11-16 07:26:59 +01:00
|
|
|
### HAProxy configuration
|
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
Below is a working example of a HAProxy configuration. It assumes that
|
2023-12-14 08:28:24 +01:00
|
|
|
your Zulip server sits at `https://10.10.10.10:443`; see
|
2022-04-09 00:00:27 +02:00
|
|
|
[above](#configuring-zulip-to-allow-http) to switch to HTTP.
|
2018-11-16 07:26:59 +01:00
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
1. Follow the instructions to [configure Zulip to trust
|
|
|
|
proxies](#configuring-zulip-to-trust-proxies).
|
2018-11-16 07:26:59 +01:00
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
1. Configure HAProxy. The below is a minimal `frontend` and `backend`
|
|
|
|
configuration:
|
|
|
|
|
|
|
|
```text
|
|
|
|
frontend zulip
|
|
|
|
mode http
|
|
|
|
bind *:80
|
|
|
|
bind *:443 ssl crt /etc/ssl/private/zulip-combined.crt
|
|
|
|
http-request redirect scheme https code 301 unless { ssl_fc }
|
2023-03-06 20:18:09 +01:00
|
|
|
http-request set-header X-Forwarded-Proto http unless { ssl_fc }
|
|
|
|
http-request set-header X-Forwarded-Proto https if { ssl_fc }
|
2022-04-09 00:00:27 +02:00
|
|
|
default_backend zulip
|
|
|
|
|
|
|
|
backend zulip
|
|
|
|
mode http
|
|
|
|
timeout server 20m
|
|
|
|
server zulip 10.10.10.10:443 check ssl ca-file /etc/ssl/certs/ca-certificates.crt
|
|
|
|
```
|
|
|
|
|
|
|
|
Don't forget to update `bind *:443 ssl crt` and `server` as is
|
|
|
|
appropriate for your deployment.
|
2018-11-29 00:53:54 +01:00
|
|
|
|
|
|
|
### Other proxies
|
|
|
|
|
|
|
|
If you're using another reverse proxy implementation, there are few
|
|
|
|
things you need to be careful about when configuring it:
|
|
|
|
|
|
|
|
1. Configure your reverse proxy (or proxies) to correctly maintain the
|
2021-08-20 22:54:08 +02:00
|
|
|
`X-Forwarded-For` HTTP header, which is supposed to contain the series
|
2021-11-18 02:31:09 +01:00
|
|
|
of IP addresses the request was forwarded through. Additionally,
|
|
|
|
[configure Zulip to respect the addresses sent by your reverse
|
|
|
|
proxies](#configuring-zulip-to-trust-proxies). You can verify
|
2021-08-20 22:54:08 +02:00
|
|
|
your work by looking at `/var/log/zulip/server.log` and checking it
|
|
|
|
has the actual IP addresses of clients, not the IP address of the
|
|
|
|
proxy server.
|
2018-11-29 00:53:54 +01:00
|
|
|
|
2023-03-06 20:18:09 +01:00
|
|
|
1. Configure your reverse proxy (or proxies) to correctly maintain the
|
|
|
|
`X-Forwarded-Proto` HTTP header, which is supposed to contain either `https`
|
|
|
|
or `http` depending on the connection between your browser and your
|
|
|
|
proxy. This will be used by Django to perform CSRF checks regardless of your
|
|
|
|
connection mechanism from your proxy to Zulip. Note that the proxies _must_
|
|
|
|
set the header, overriding any existing values, not add a new header.
|
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
1. Configure your proxy to pass along the `Host:` header as was sent
|
|
|
|
from the client, not the internal hostname as seen by the proxy.
|
|
|
|
If this is not possible, you can set `USE_X_FORWARDED_HOST = True`
|
|
|
|
in `/etc/zulip/settings.py`, and pass the client's `Host` header to
|
|
|
|
Zulip in an `X-Forwarded-Host` header.
|
|
|
|
|
|
|
|
1. Ensure your proxy doesn't interfere with Zulip's use of
|
2021-08-20 22:54:08 +02:00
|
|
|
long-polling for real-time push from the server to your users'
|
|
|
|
browsers. This [nginx code snippet][nginx-proxy-longpolling-config]
|
|
|
|
does this.
|
2018-11-29 00:53:54 +01:00
|
|
|
|
2022-02-24 23:58:29 +01:00
|
|
|
The key configuration options are, for the `/json/events` and
|
|
|
|
`/api/1/events` endpoints:
|
2018-11-29 00:53:54 +01:00
|
|
|
|
2022-02-24 23:58:29 +01:00
|
|
|
- `proxy_read_timeout 1200;`. It's critical that this be
|
|
|
|
significantly above 60s, but the precise value isn't important.
|
|
|
|
- `proxy_buffering off`. If you don't do this, your `nginx` proxy may
|
|
|
|
return occasional 502 errors to clients using Zulip's events API.
|
2018-11-29 00:53:54 +01:00
|
|
|
|
2022-04-09 00:00:27 +02:00
|
|
|
1. The other tricky failure mode we've seen with `nginx` reverse
|
2021-08-20 22:54:08 +02:00
|
|
|
proxies is that they can load-balance between the IPv4 and IPv6
|
|
|
|
addresses for a given hostname. This can result in mysterious errors
|
|
|
|
that can be quite difficult to debug. Be sure to declare your
|
|
|
|
`upstreams` equivalent in a way that won't do load-balancing
|
|
|
|
unexpectedly (e.g. pointing to a DNS name that you haven't configured
|
|
|
|
with multiple IPs for your Zulip machine; sometimes this happens with
|
|
|
|
IPv6 configuration).
|
2020-10-20 01:53:08 +02:00
|
|
|
|
postgresql: Support replication on PostgreSQL >= 11, document.
PostgreSQL 11 and below used a configuration file names
`recovery.conf` to manage replicas and standbys; support for this was
removed in PostgreSQL 12[1], and the configuration parameters were
moved into the main `postgresql.conf`.
Add `zulip.conf` settings for the primary server hostname and
replication username, so that the complete `postgresql.conf`
configuration on PostgreSQL 14 can continue to be managed, even when
replication is enabled. For consistency, also begin writing out the
`recovery.conf` for PostgreSQL 11 and below.
In PostgreSQL 12 configuration and later, the `wal_level =
hot_standby` setting is removed, as `hot_standby` is equivalent to
`replica`, which is the default value[2]. Similarly, the
`hot_standby = on` setting is also the default[3].
Documentation is added for these features, and the commentary on the
"Export and Import" page referencing files under `puppet/zulip_ops/`
is removed, as those files no longer have any replication-specific
configuration.
[1]: https://www.postgresql.org/docs/current/recovery-config.html
[2]: https://www.postgresql.org/docs/12/runtime-config-wal.html#GUC-WAL-LEVEL
[3]: https://www.postgresql.org/docs/12/runtime-config-replication.html#GUC-HOT-STANDBY
2021-11-20 00:33:41 +01:00
|
|
|
## PostgreSQL warm standby
|
|
|
|
|
|
|
|
Zulip's configuration allows for [warm standby database
|
|
|
|
replicas][warm-standby] as a disaster recovery solution; see the
|
|
|
|
linked PostgreSQL documentation for details on this type of
|
2022-03-11 03:39:34 +01:00
|
|
|
deployment. Zulip's configuration builds on top of `wal-g`, our
|
2022-05-07 02:21:44 +02:00
|
|
|
[streaming database backup solution][wal-g], and thus requires that it
|
|
|
|
be configured for the primary and all secondary warm standby replicas.
|
postgresql: Support replication on PostgreSQL >= 11, document.
PostgreSQL 11 and below used a configuration file names
`recovery.conf` to manage replicas and standbys; support for this was
removed in PostgreSQL 12[1], and the configuration parameters were
moved into the main `postgresql.conf`.
Add `zulip.conf` settings for the primary server hostname and
replication username, so that the complete `postgresql.conf`
configuration on PostgreSQL 14 can continue to be managed, even when
replication is enabled. For consistency, also begin writing out the
`recovery.conf` for PostgreSQL 11 and below.
In PostgreSQL 12 configuration and later, the `wal_level =
hot_standby` setting is removed, as `hot_standby` is equivalent to
`replica`, which is the default value[2]. Similarly, the
`hot_standby = on` setting is also the default[3].
Documentation is added for these features, and the commentary on the
"Export and Import" page referencing files under `puppet/zulip_ops/`
is removed, as those files no longer have any replication-specific
configuration.
[1]: https://www.postgresql.org/docs/current/recovery-config.html
[2]: https://www.postgresql.org/docs/12/runtime-config-wal.html#GUC-WAL-LEVEL
[3]: https://www.postgresql.org/docs/12/runtime-config-replication.html#GUC-HOT-STANDBY
2021-11-20 00:33:41 +01:00
|
|
|
|
2022-03-17 00:04:52 +01:00
|
|
|
In addition to having `wal-g` backups configured, warm standby
|
|
|
|
replicas should configure the hostname of their primary replica, and
|
|
|
|
username to use for replication, in `/etc/zulip/zulip.conf`:
|
postgresql: Support replication on PostgreSQL >= 11, document.
PostgreSQL 11 and below used a configuration file names
`recovery.conf` to manage replicas and standbys; support for this was
removed in PostgreSQL 12[1], and the configuration parameters were
moved into the main `postgresql.conf`.
Add `zulip.conf` settings for the primary server hostname and
replication username, so that the complete `postgresql.conf`
configuration on PostgreSQL 14 can continue to be managed, even when
replication is enabled. For consistency, also begin writing out the
`recovery.conf` for PostgreSQL 11 and below.
In PostgreSQL 12 configuration and later, the `wal_level =
hot_standby` setting is removed, as `hot_standby` is equivalent to
`replica`, which is the default value[2]. Similarly, the
`hot_standby = on` setting is also the default[3].
Documentation is added for these features, and the commentary on the
"Export and Import" page referencing files under `puppet/zulip_ops/`
is removed, as those files no longer have any replication-specific
configuration.
[1]: https://www.postgresql.org/docs/current/recovery-config.html
[2]: https://www.postgresql.org/docs/12/runtime-config-wal.html#GUC-WAL-LEVEL
[3]: https://www.postgresql.org/docs/12/runtime-config-replication.html#GUC-HOT-STANDBY
2021-11-20 00:33:41 +01:00
|
|
|
|
|
|
|
```ini
|
|
|
|
[postgresql]
|
|
|
|
replication_user = replicator
|
|
|
|
replication_primary = hostname-of-primary.example.com
|
|
|
|
```
|
|
|
|
|
|
|
|
The `postgres` user on the replica will need to be able to
|
2022-03-17 00:04:52 +01:00
|
|
|
authenticate as the `replication_user` user, which may require further
|
2022-03-10 20:18:30 +01:00
|
|
|
configuration of `pg_hba.conf` and client certificates on the replica.
|
|
|
|
If you are using password authentication, you can set a
|
|
|
|
`postgresql_replication_password` secret in
|
|
|
|
`/etc/zulip/zulip-secrets.conf`.
|
postgresql: Support replication on PostgreSQL >= 11, document.
PostgreSQL 11 and below used a configuration file names
`recovery.conf` to manage replicas and standbys; support for this was
removed in PostgreSQL 12[1], and the configuration parameters were
moved into the main `postgresql.conf`.
Add `zulip.conf` settings for the primary server hostname and
replication username, so that the complete `postgresql.conf`
configuration on PostgreSQL 14 can continue to be managed, even when
replication is enabled. For consistency, also begin writing out the
`recovery.conf` for PostgreSQL 11 and below.
In PostgreSQL 12 configuration and later, the `wal_level =
hot_standby` setting is removed, as `hot_standby` is equivalent to
`replica`, which is the default value[2]. Similarly, the
`hot_standby = on` setting is also the default[3].
Documentation is added for these features, and the commentary on the
"Export and Import" page referencing files under `puppet/zulip_ops/`
is removed, as those files no longer have any replication-specific
configuration.
[1]: https://www.postgresql.org/docs/current/recovery-config.html
[2]: https://www.postgresql.org/docs/12/runtime-config-wal.html#GUC-WAL-LEVEL
[3]: https://www.postgresql.org/docs/12/runtime-config-replication.html#GUC-HOT-STANDBY
2021-11-20 00:33:41 +01:00
|
|
|
|
|
|
|
[warm-standby]: https://www.postgresql.org/docs/current/warm-standby.html
|
2022-03-17 00:04:22 +01:00
|
|
|
[wal-g]: export-and-import.md#database-only-backup-tools
|
postgresql: Support replication on PostgreSQL >= 11, document.
PostgreSQL 11 and below used a configuration file names
`recovery.conf` to manage replicas and standbys; support for this was
removed in PostgreSQL 12[1], and the configuration parameters were
moved into the main `postgresql.conf`.
Add `zulip.conf` settings for the primary server hostname and
replication username, so that the complete `postgresql.conf`
configuration on PostgreSQL 14 can continue to be managed, even when
replication is enabled. For consistency, also begin writing out the
`recovery.conf` for PostgreSQL 11 and below.
In PostgreSQL 12 configuration and later, the `wal_level =
hot_standby` setting is removed, as `hot_standby` is equivalent to
`replica`, which is the default value[2]. Similarly, the
`hot_standby = on` setting is also the default[3].
Documentation is added for these features, and the commentary on the
"Export and Import" page referencing files under `puppet/zulip_ops/`
is removed, as those files no longer have any replication-specific
configuration.
[1]: https://www.postgresql.org/docs/current/recovery-config.html
[2]: https://www.postgresql.org/docs/12/runtime-config-wal.html#GUC-WAL-LEVEL
[3]: https://www.postgresql.org/docs/12/runtime-config-replication.html#GUC-HOT-STANDBY
2021-11-20 00:33:41 +01:00
|
|
|
|
2020-10-20 01:53:08 +02:00
|
|
|
## System and deployment configuration
|
|
|
|
|
|
|
|
The file `/etc/zulip/zulip.conf` is used to configure properties of
|
|
|
|
the system and deployment; `/etc/zulip/settings.py` is used to
|
2023-01-17 04:53:46 +01:00
|
|
|
[configure the application itself](settings.md). The `zulip.conf`
|
|
|
|
sections and settings are described below.
|
2020-10-20 01:53:08 +02:00
|
|
|
|
2022-01-05 19:45:12 +01:00
|
|
|
When a setting refers to "set to true" or "set to false", the values
|
|
|
|
`true` and `false` are canonical, but any of the following values will
|
|
|
|
be considered "true", case-insensitively:
|
|
|
|
|
|
|
|
- 1
|
|
|
|
- y
|
|
|
|
- t
|
|
|
|
- yes
|
|
|
|
- true
|
|
|
|
- enable
|
|
|
|
- enabled
|
|
|
|
|
|
|
|
Any other value (including the empty string) is considered false.
|
|
|
|
|
2020-10-20 01:53:08 +02:00
|
|
|
### `[machine]`
|
|
|
|
|
|
|
|
#### `puppet_classes`
|
|
|
|
|
|
|
|
A comma-separated list of the Puppet classes to install on the server.
|
|
|
|
The most common is **`zulip::profile::standalone`**, used for a
|
|
|
|
stand-alone single-host deployment.
|
2022-02-16 01:39:15 +01:00
|
|
|
[Components](../overview/architecture-overview.md#components) of
|
2020-10-20 01:53:08 +02:00
|
|
|
that include:
|
2021-08-20 22:54:08 +02:00
|
|
|
|
|
|
|
- **`zulip::profile::app_frontend`**
|
|
|
|
- **`zulip::profile::memcached`**
|
|
|
|
- **`zulip::profile::postgresql`**
|
|
|
|
- **`zulip::profile::redis`**
|
|
|
|
- **`zulip::profile::rabbitmq`**
|
2020-10-20 01:53:08 +02:00
|
|
|
|
|
|
|
If you are using a [Apache as a single-sign-on
|
2022-02-16 01:39:15 +01:00
|
|
|
authenticator](authentication-methods.md#apache-based-sso-with-remote_user),
|
2020-10-20 01:53:08 +02:00
|
|
|
you will need to add **`zulip::apache_sso`** to the list.
|
|
|
|
|
|
|
|
#### `pgroonga`
|
|
|
|
|
2022-01-05 19:45:12 +01:00
|
|
|
Set to true if enabling the [multi-language PGroonga search
|
2022-02-16 01:39:15 +01:00
|
|
|
extension](../subsystems/full-text-search.md#multi-language-full-text-search).
|
2020-10-20 01:53:08 +02:00
|
|
|
|
2023-01-31 17:24:16 +01:00
|
|
|
#### `timesync`
|
|
|
|
|
|
|
|
What time synchronization daemon to use; defaults to `chrony`, but also supports
|
|
|
|
`ntpd` and `none`. Installations should not adjust this unless they are aligning
|
|
|
|
with a fleet-wide standard of `ntpd`. `none` is only reasonable in containers
|
|
|
|
like LXC which do not allow adjustment of the clock; a Zulip server will not
|
|
|
|
function correctly without an accurate clock.
|
|
|
|
|
2020-10-20 01:53:08 +02:00
|
|
|
### `[deployment]`
|
|
|
|
|
|
|
|
#### `deploy_options`
|
|
|
|
|
|
|
|
Options passed by `upgrade-zulip` and `upgrade-zulip-from-git` into
|
2021-08-20 21:53:28 +02:00
|
|
|
`upgrade-zulip-stage-2`. These might be any of:
|
2020-10-20 01:53:08 +02:00
|
|
|
|
2021-08-20 22:54:08 +02:00
|
|
|
- **`--skip-puppet`** skips doing Puppet/apt upgrades. The user will need
|
|
|
|
to run `zulip-puppet-apply` manually after the upgrade.
|
|
|
|
- **`--skip-migrations`** skips running database migrations. The
|
|
|
|
user will need to run `./manage.py migrate` manually after the upgrade.
|
|
|
|
- **`--skip-purge-old-deployments`** skips purging old deployments;
|
|
|
|
without it, only deployments with the last two weeks are kept.
|
2020-10-20 01:53:08 +02:00
|
|
|
|
|
|
|
Generally installations will not want to set any of these options; the
|
|
|
|
`--skip-*` options are primarily useful for reducing upgrade downtime
|
|
|
|
for servers that are upgraded frequently by core Zulip developers.
|
|
|
|
|
|
|
|
#### `git_repo_url`
|
|
|
|
|
|
|
|
Default repository URL used when [upgrading from a Git
|
2023-01-17 04:33:42 +01:00
|
|
|
repository](upgrade.md#upgrading-from-a-git-repository).
|
2020-10-20 01:53:08 +02:00
|
|
|
|
|
|
|
### `[application_server]`
|
|
|
|
|
|
|
|
#### `http_only`
|
|
|
|
|
2022-01-05 19:45:12 +01:00
|
|
|
If set to true, [configures Zulip to allow HTTP access][using-http];
|
|
|
|
use if Zulip is deployed behind a reverse proxy that is handling
|
|
|
|
SSL/TLS termination.
|
2020-10-20 01:53:08 +02:00
|
|
|
|
|
|
|
#### `nginx_listen_port`
|
|
|
|
|
|
|
|
Set to the port number if you [prefer to listen on a port other than
|
|
|
|
443](#using-an-alternate-port).
|
|
|
|
|
2023-03-23 22:39:15 +01:00
|
|
|
#### `nginx_worker_connections`
|
|
|
|
|
|
|
|
Adjust the [`worker_connections`][nginx_worker_connections] setting in
|
|
|
|
the nginx server. This defaults to 10000; increasing it allows more
|
|
|
|
concurrent connections per CPU core, at the cost of more memory
|
|
|
|
consumed by NGINX. This number, times the number of CPU cores, should
|
|
|
|
be more than twice the concurrent number of users.
|
|
|
|
|
|
|
|
[nginx_worker_connections]: http://nginx.org/en/docs/ngx_core_module.html#worker_connections
|
|
|
|
|
2021-04-20 00:11:07 +02:00
|
|
|
#### `queue_workers_multiprocess`
|
|
|
|
|
|
|
|
By default, Zulip automatically detects whether the system has enough
|
|
|
|
memory to run Zulip queue processors in the higher-throughput but more
|
|
|
|
multiprocess mode (or to save 1.5GiB of RAM with the multithreaded
|
|
|
|
mode). The calculation is based on whether the system has enough
|
|
|
|
memory (currently 3.5GiB) to run a single-server Zulip installation in
|
|
|
|
the multiprocess mode.
|
|
|
|
|
2022-01-05 19:45:12 +01:00
|
|
|
Set explicitly to true or false to override the automatic
|
|
|
|
calculation. This override is useful both Docker systems (where the
|
|
|
|
above algorithm might see the host's memory, not the container's)
|
|
|
|
and/or when using remote servers for postgres, memcached, redis, and
|
|
|
|
RabbitMQ.
|
puppet: Use lazy-apps and uwsgi control sockets for rolling reloads.
Restarting the uwsgi processes by way of supervisor opens a window
during which nginx 502's all responses. uwsgi has a configuration
called "chain reloading" which allows for rolling restart of the uwsgi
processes, such that only one process at once in unavailable; see
uwsgi documentation ([1]).
The tradeoff is that this requires that the uwsgi processes load the
libraries after forking, rather than before ("lazy apps"); in theory
this can lead to larger memory footprints, since they are not shared.
In practice, as Django defers much of the loading, this is not as much
of an issue. In a very basic test of memory consumption (measured by
total memory - free - caches - buffers; 6 uwsgi workers), both
immediately after restarting Django, and after requesting `/` 60 times
with 6 concurrent requests:
| Non-lazy | Lazy app | Difference
------------------+------------+------------+-------------
Fresh | 2,827,216 | 2,870,480 | +43,264
After 60 requests | 3,332,284 | 3,409,608 | +77,324
..................|............|............|.............
Difference | +505,068 | +539,128 | +34,060
That is, "lazy app" loading increased the footprint pre-requests by
43MB, and after 60 requests grew the memory footprint by 539MB, as
opposed to non-lazy loading, which grew it by 505MB. Using wsgi "lazy
app" loading does increase the memory footprint, but not by a large
percentage.
The other effect is that processes may be served by either old or new
code during the restart window. This may cause transient failures
when new frontend code talks to old backend code.
Enable chain-reloading during graceful, puppetless restarts, but only
if enabled via a zulip.conf configuration flag.
Fixes #2559.
[1]: https://uwsgi-docs.readthedocs.io/en/latest/articles/TheArtOfGracefulReloading.html#chain-reloading-lazy-apps
2022-01-01 05:20:49 +01:00
|
|
|
|
|
|
|
#### `rolling_restart`
|
|
|
|
|
2022-01-20 05:16:52 +01:00
|
|
|
If set to true, when using `./scripts/restart-server` to restart
|
|
|
|
Zulip, restart the uwsgi processes one-at-a-time, instead of all at
|
|
|
|
once. This decreases the number of 502's served to clients, at the
|
|
|
|
cost of slightly increased memory usage, and the possibility that
|
puppet: Use lazy-apps and uwsgi control sockets for rolling reloads.
Restarting the uwsgi processes by way of supervisor opens a window
during which nginx 502's all responses. uwsgi has a configuration
called "chain reloading" which allows for rolling restart of the uwsgi
processes, such that only one process at once in unavailable; see
uwsgi documentation ([1]).
The tradeoff is that this requires that the uwsgi processes load the
libraries after forking, rather than before ("lazy apps"); in theory
this can lead to larger memory footprints, since they are not shared.
In practice, as Django defers much of the loading, this is not as much
of an issue. In a very basic test of memory consumption (measured by
total memory - free - caches - buffers; 6 uwsgi workers), both
immediately after restarting Django, and after requesting `/` 60 times
with 6 concurrent requests:
| Non-lazy | Lazy app | Difference
------------------+------------+------------+-------------
Fresh | 2,827,216 | 2,870,480 | +43,264
After 60 requests | 3,332,284 | 3,409,608 | +77,324
..................|............|............|.............
Difference | +505,068 | +539,128 | +34,060
That is, "lazy app" loading increased the footprint pre-requests by
43MB, and after 60 requests grew the memory footprint by 539MB, as
opposed to non-lazy loading, which grew it by 505MB. Using wsgi "lazy
app" loading does increase the memory footprint, but not by a large
percentage.
The other effect is that processes may be served by either old or new
code during the restart window. This may cause transient failures
when new frontend code talks to old backend code.
Enable chain-reloading during graceful, puppetless restarts, but only
if enabled via a zulip.conf configuration flag.
Fixes #2559.
[1]: https://uwsgi-docs.readthedocs.io/en/latest/articles/TheArtOfGracefulReloading.html#chain-reloading-lazy-apps
2022-01-01 05:20:49 +01:00
|
|
|
different requests will be served by different versions of the code.
|
2021-04-20 00:11:07 +02:00
|
|
|
|
2023-05-17 17:06:03 +02:00
|
|
|
#### `service_file_descriptor_limit`
|
|
|
|
|
|
|
|
The number of file descriptors which [Supervisor is configured to allow
|
2023-05-31 00:28:07 +02:00
|
|
|
processes to use][supervisor-minfds]; defaults to 40000. If your Zulip deployment
|
2023-05-17 17:06:03 +02:00
|
|
|
is very large (hundreds of thousands of concurrent users), your Django processes
|
|
|
|
hit this limit and refuse connections to clients. Raising it above this default
|
|
|
|
may require changing system-level limits, particularly if you are using a
|
|
|
|
virtualized environment (e.g. Docker, or Proxmox LXC).
|
|
|
|
|
|
|
|
[supervisor-minfds]: http://supervisord.org/configuration.html?highlight=minfds#supervisord-section-values
|
|
|
|
|
uploads: Serve S3 uploads directly from nginx.
When file uploads are stored in S3, this means that Zulip serves as a
302 to S3. Because browsers do not cache redirects, this means that
no image contents can be cached -- and upon every page load or reload,
every recently-posted image must be re-fetched. This incurs extra
load on the Zulip server, as well as potentially excessive bandwidth
usage from S3, and on the client's connection.
Switch to fetching the content from S3 in nginx, and serving the
content from nginx. These have `Cache-control: private, immutable`
headers set on the response, allowing browsers to cache them locally.
Because nginx fetching from S3 can be slow, and requests for uploads
will generally be bunched around when a message containing them are
first posted, we instruct nginx to cache the contents locally. This
is safe because uploaded file contents are immutable; access control
is still mediated by Django. The nginx cache key is the URL without
query parameters, as those parameters include a time-limited signed
authentication parameter which lets nginx fetch the non-public file.
This adds a number of nginx-level configuration parameters to control
the caching which nginx performs, including the amount of in-memory
index for he cache, the maximum storage of the cache on disk, and how
long data is retained in the cache. The currently-chosen figures are
reasonable for small to medium deployments.
The most notable effect of this change is in allowing browsers to
cache uploaded image content; however, while there will be many fewer
requests, it also has an improvement on request latency. The
following tests were done with a non-AWS client in SFO, a server and
S3 storage in us-east-1, and with 100 requests after 10 requests of
warm-up (to fill the nginx cache). The mean and standard deviation
are shown.
| | Redirect to S3 | Caching proxy, hot | Caching proxy, cold |
| ----------------- | ------------------- | ------------------- | ------------------- |
| Time in Django | 263.0 ms ± 28.3 ms | 258.0 ms ± 12.3 ms | 258.0 ms ± 12.3 ms |
| Small file (842b) | 586.1 ms ± 21.1 ms | 266.1 ms ± 67.4 ms | 288.6 ms ± 17.7 ms |
| Large file (660k) | 959.6 ms ± 137.9 ms | 609.5 ms ± 13.0 ms | 648.1 ms ± 43.2 ms |
The hot-cache performance is faster for both large and small files,
since it saves the client the time having to make a second request to
a separate host. This performance improvement remains at least 100ms
even if the client is on the same coast as the server.
Cold nginx caches are only slightly slower than hot caches, because
VPC access to S3 endpoints is extremely fast (assuming it is in the
same region as the host), and nginx can pool connections to S3 and
reuse them.
However, all of the 648ms taken to serve a cold-cache large file is
occupied in nginx, as opposed to the only 263ms which was spent in
nginx when using redirects to S3. This means that to overall spend
less time responding to uploaded-file requests in nginx, clients will
need to find files in their local cache, and skip making an
uploaded-file request, at least 60% of the time. Modeling shows a
reduction in the number of client requests by about 70% - 80%.
The `Content-Disposition` header logic can now also be entirely shared
with the local-file codepath, as can the `url_only` path used by
mobile clients. While we could provide the direct-to-S3 temporary
signed URL to mobile clients, we choose to provide the
served-from-Zulip signed URL, to better control caching headers on it,
and greater consistency. In doing so, we adjust the salt used for the
URL; since these URLs are only valid for 60s, the effect of this salt
change is minimal.
2022-11-22 20:41:35 +01:00
|
|
|
#### `s3_memory_cache_size`
|
|
|
|
|
|
|
|
Used only when the [S3 storage backend][s3-backend] is in use.
|
|
|
|
Controls the in-memory size of the cache _index_; the default is 1MB,
|
|
|
|
which is enough to store about 8 thousand entries.
|
|
|
|
|
|
|
|
#### `s3_disk_cache_size`
|
|
|
|
|
|
|
|
Used only when the [S3 storage backend][s3-backend] is in use.
|
|
|
|
Controls the on-disk size of the cache _contents_; the default is
|
|
|
|
200MB.
|
|
|
|
|
|
|
|
#### `s3_cache_inactive_time`
|
|
|
|
|
|
|
|
Used only when the [S3 storage backend][s3-backend] is in use.
|
|
|
|
Controls the longest amount of time an entry will be cached since last
|
|
|
|
use; the default is 30 days. Since the contents of the cache are
|
|
|
|
immutable, this serves only as a potential additional limit on the
|
|
|
|
size of the contents on disk; `s3_disk_cache_size` is expected to be
|
|
|
|
the primary control for cache sizing.
|
|
|
|
|
puppet: Read resolver from /etc/resolv.conf.
04cf68b45ebb make nginx responsible for downloading (and caching)
files from S3. As noted in that commit, nginx implements its own
non-blocking DNS resolver, since the base syscall is blocking, so
requires an explicit nameserver configuration. That commit used
127.0.0.53, which is provided by systemd-resolved, as the resolver.
However, that service may not always be enabled and running, and may
in fact not even be installed (e.g. on Docker). Switch to parsing
`/etc/resolv.conf` and using the first-provided nameserver. In many
deployments, this will still be `127.0.0.53`, but for others it will
provide a working DNS server which is external to the host.
In the event that a server is misconfigured and has no resolvers in
`/etc/resolv.conf`, it will error out:
```console
Error: Evaluation Error: Error while evaluating a Function Call, No nameservers found in /etc/resolv.conf! Configure one by setting application_server.nameserver in /etc/zulip/zulip.conf (file: /home/zulip/deployments/current/puppet/zulip/manifests/app_frontend_base.pp, line: 76, column: 70) on node example.zulipdev.org
```
2023-06-08 21:30:41 +02:00
|
|
|
#### `nameserver`
|
|
|
|
|
|
|
|
When the [S3 storage backend][s3-backend] is in use, downloads from S3 are
|
|
|
|
proxied from nginx, whose configuration requires an explicit value of a DNS
|
|
|
|
nameserver to resolve the S3 server's hostname. Zulip defaults to using the
|
|
|
|
resolver found in `/etc/resolv.conf`; this setting overrides any value found
|
|
|
|
there.
|
|
|
|
|
uploads: Serve S3 uploads directly from nginx.
When file uploads are stored in S3, this means that Zulip serves as a
302 to S3. Because browsers do not cache redirects, this means that
no image contents can be cached -- and upon every page load or reload,
every recently-posted image must be re-fetched. This incurs extra
load on the Zulip server, as well as potentially excessive bandwidth
usage from S3, and on the client's connection.
Switch to fetching the content from S3 in nginx, and serving the
content from nginx. These have `Cache-control: private, immutable`
headers set on the response, allowing browsers to cache them locally.
Because nginx fetching from S3 can be slow, and requests for uploads
will generally be bunched around when a message containing them are
first posted, we instruct nginx to cache the contents locally. This
is safe because uploaded file contents are immutable; access control
is still mediated by Django. The nginx cache key is the URL without
query parameters, as those parameters include a time-limited signed
authentication parameter which lets nginx fetch the non-public file.
This adds a number of nginx-level configuration parameters to control
the caching which nginx performs, including the amount of in-memory
index for he cache, the maximum storage of the cache on disk, and how
long data is retained in the cache. The currently-chosen figures are
reasonable for small to medium deployments.
The most notable effect of this change is in allowing browsers to
cache uploaded image content; however, while there will be many fewer
requests, it also has an improvement on request latency. The
following tests were done with a non-AWS client in SFO, a server and
S3 storage in us-east-1, and with 100 requests after 10 requests of
warm-up (to fill the nginx cache). The mean and standard deviation
are shown.
| | Redirect to S3 | Caching proxy, hot | Caching proxy, cold |
| ----------------- | ------------------- | ------------------- | ------------------- |
| Time in Django | 263.0 ms ± 28.3 ms | 258.0 ms ± 12.3 ms | 258.0 ms ± 12.3 ms |
| Small file (842b) | 586.1 ms ± 21.1 ms | 266.1 ms ± 67.4 ms | 288.6 ms ± 17.7 ms |
| Large file (660k) | 959.6 ms ± 137.9 ms | 609.5 ms ± 13.0 ms | 648.1 ms ± 43.2 ms |
The hot-cache performance is faster for both large and small files,
since it saves the client the time having to make a second request to
a separate host. This performance improvement remains at least 100ms
even if the client is on the same coast as the server.
Cold nginx caches are only slightly slower than hot caches, because
VPC access to S3 endpoints is extremely fast (assuming it is in the
same region as the host), and nginx can pool connections to S3 and
reuse them.
However, all of the 648ms taken to serve a cold-cache large file is
occupied in nginx, as opposed to the only 263ms which was spent in
nginx when using redirects to S3. This means that to overall spend
less time responding to uploaded-file requests in nginx, clients will
need to find files in their local cache, and skip making an
uploaded-file request, at least 60% of the time. Modeling shows a
reduction in the number of client requests by about 70% - 80%.
The `Content-Disposition` header logic can now also be entirely shared
with the local-file codepath, as can the `url_only` path used by
mobile clients. While we could provide the direct-to-S3 temporary
signed URL to mobile clients, we choose to provide the
served-from-Zulip signed URL, to better control caching headers on it,
and greater consistency. In doing so, we adjust the salt used for the
URL; since these URLs are only valid for 60s, the effect of this salt
change is minimal.
2022-11-22 20:41:35 +01:00
|
|
|
[s3-backend]: upload-backends.md
|
|
|
|
|
2020-10-20 01:53:08 +02:00
|
|
|
#### `uwsgi_listen_backlog_limit`
|
|
|
|
|
|
|
|
Override the default uwsgi backlog of 128 connections.
|
|
|
|
|
|
|
|
#### `uwsgi_processes`
|
|
|
|
|
|
|
|
Override the default `uwsgi` (Django) process count of 6 on hosts with
|
|
|
|
more than 3.5GiB of RAM, 4 on hosts with less.
|
|
|
|
|
2022-04-29 01:23:27 +02:00
|
|
|
#### `access_log_retention_days`
|
|
|
|
|
|
|
|
Number of days of access logs to keep, for both nginx and the application.
|
|
|
|
Defaults to 14 days.
|
|
|
|
|
2020-10-20 01:53:08 +02:00
|
|
|
### `[postfix]`
|
|
|
|
|
|
|
|
#### `mailname`
|
|
|
|
|
|
|
|
The hostname that [Postfix should be configured to receive mail
|
2023-01-17 21:22:44 +01:00
|
|
|
at](email-gateway.md#local-delivery-setup), as well as identify itself as for
|
|
|
|
outgoing email.
|
2020-10-20 01:53:08 +02:00
|
|
|
|
|
|
|
### `[postgresql]`
|
|
|
|
|
|
|
|
#### `effective_io_concurrency`
|
|
|
|
|
|
|
|
Override PostgreSQL's [`effective_io_concurrency`
|
|
|
|
setting](https://www.postgresql.org/docs/current/runtime-config-resource.html#GUC-EFFECTIVE-IO-CONCURRENCY).
|
|
|
|
|
|
|
|
#### `listen_addresses`
|
|
|
|
|
|
|
|
Override PostgreSQL's [`listen_addresses`
|
|
|
|
setting](https://www.postgresql.org/docs/current/runtime-config-connection.html#GUC-LISTEN-ADDRESSES).
|
|
|
|
|
|
|
|
#### `random_page_cost`
|
|
|
|
|
|
|
|
Override PostgreSQL's [`random_page_cost`
|
|
|
|
setting](https://www.postgresql.org/docs/current/runtime-config-query.html#GUC-RANDOM-PAGE-COST)
|
|
|
|
|
postgresql: Support replication on PostgreSQL >= 11, document.
PostgreSQL 11 and below used a configuration file names
`recovery.conf` to manage replicas and standbys; support for this was
removed in PostgreSQL 12[1], and the configuration parameters were
moved into the main `postgresql.conf`.
Add `zulip.conf` settings for the primary server hostname and
replication username, so that the complete `postgresql.conf`
configuration on PostgreSQL 14 can continue to be managed, even when
replication is enabled. For consistency, also begin writing out the
`recovery.conf` for PostgreSQL 11 and below.
In PostgreSQL 12 configuration and later, the `wal_level =
hot_standby` setting is removed, as `hot_standby` is equivalent to
`replica`, which is the default value[2]. Similarly, the
`hot_standby = on` setting is also the default[3].
Documentation is added for these features, and the commentary on the
"Export and Import" page referencing files under `puppet/zulip_ops/`
is removed, as those files no longer have any replication-specific
configuration.
[1]: https://www.postgresql.org/docs/current/recovery-config.html
[2]: https://www.postgresql.org/docs/12/runtime-config-wal.html#GUC-WAL-LEVEL
[3]: https://www.postgresql.org/docs/12/runtime-config-replication.html#GUC-HOT-STANDBY
2021-11-20 00:33:41 +01:00
|
|
|
#### `replication_primary`
|
|
|
|
|
|
|
|
On the [warm standby replicas](#postgresql-warm-standby), set to the
|
|
|
|
hostname of the primary PostgreSQL server that streaming replication
|
|
|
|
should be done from.
|
|
|
|
|
|
|
|
#### `replication_user`
|
|
|
|
|
|
|
|
On the [warm standby replicas](#postgresql-warm-standby), set to the
|
|
|
|
username that the host should authenticate to the primary PostgreSQL
|
2022-03-10 20:18:30 +01:00
|
|
|
server as, for streaming replication. Authentication will be done
|
|
|
|
based on the `pg_hba.conf` file; if you are using password
|
|
|
|
authentication, you can set a `postgresql_replication_password` secret
|
|
|
|
for authentication.
|
2020-10-20 01:53:08 +02:00
|
|
|
|
2023-04-26 03:21:36 +02:00
|
|
|
#### `skip_backups`
|
|
|
|
|
|
|
|
If set to as true value, inhibits the nightly [`wal-g` backups][wal-g] which
|
|
|
|
would be taken on all non-replicated hosts and [all warm standby
|
|
|
|
replicas](#postgresql-warm-standby). This is generally only set if you have
|
|
|
|
multiple warm standby replicas, in order to avoid taking multiple backups, one
|
|
|
|
per replica.
|
|
|
|
|
2023-04-26 16:49:47 +02:00
|
|
|
#### `backups_disk_concurrency`
|
|
|
|
|
|
|
|
Number of concurrent disk reads to use when taking backups. Defaults to 1; you
|
|
|
|
may wish to increase this if you are taking backups on a replica, so can afford
|
|
|
|
to affect other disk I/O, and have an SSD which is good at parallel random
|
|
|
|
reads.
|
|
|
|
|
2023-07-19 02:27:04 +02:00
|
|
|
#### `backups_storage_class`
|
|
|
|
|
|
|
|
What [storage class](https://aws.amazon.com/s3/storage-classes/) to use when
|
|
|
|
uploading database backups. Defaults to `STANDARD`, meaning "[S3
|
|
|
|
standard][s3-standard]", but many deployments will have overall lower costs if
|
|
|
|
"[S3 Standard - Infrequent Access][s3-ia]" is used, via the `STANDARD_IA`
|
|
|
|
value. Also supported is "[S3 Reduced Redundancy][s3-rr]", by setting
|
|
|
|
`REDUCED_REDUNDANCY`, but this is not suggested for production use.
|
|
|
|
|
|
|
|
[s3-standard]: https://aws.amazon.com/s3/storage-classes/#General_purpose
|
|
|
|
[s3-ia]: https://aws.amazon.com/s3/storage-classes/#Infrequent_access
|
|
|
|
[s3-rr]: https://aws.amazon.com/s3/reduced-redundancy/
|
|
|
|
|
2023-05-31 00:48:01 +02:00
|
|
|
#### `missing_dictionaries`
|
|
|
|
|
|
|
|
If set to a true value during initial database creation, uses PostgreSQL's
|
|
|
|
standard `pg_catalog.english` text search configuration, rather than Zulip's
|
|
|
|
improved set of stopwords. Has no effect after initial database construction.
|
|
|
|
|
2020-10-20 01:53:08 +02:00
|
|
|
#### `ssl_ca_file`
|
|
|
|
|
|
|
|
Set to the path to the PEM-encoded certificate authority used to
|
|
|
|
authenticate client connections.
|
|
|
|
|
|
|
|
#### `ssl_cert_file`
|
|
|
|
|
|
|
|
Set to the path to the PEM-encoded public certificate used to secure
|
|
|
|
client connections.
|
|
|
|
|
|
|
|
#### `ssl_key_file`
|
|
|
|
|
|
|
|
Set to the path to the PEM-encoded private key used to secure client
|
|
|
|
connections.
|
|
|
|
|
2022-03-10 21:07:50 +01:00
|
|
|
#### `ssl_mode`
|
|
|
|
|
|
|
|
The mode that should be used to verify the server certificate. The
|
|
|
|
PostgreSQL default is `prefer`, which provides no security benefit; we
|
|
|
|
strongly suggest setting this to `require` or better if you are using
|
|
|
|
certificate authentication. See the [PostgreSQL
|
|
|
|
documentation](https://www.postgresql.org/docs/current/libpq-ssl.html#LIBPQ-SSL-SSLMODE-STATEMENTS)
|
|
|
|
for potential values.
|
|
|
|
|
2020-10-20 01:53:08 +02:00
|
|
|
#### `version`
|
|
|
|
|
2021-08-20 21:53:28 +02:00
|
|
|
The version of PostgreSQL that is in use. Do not set by hand; use the
|
2023-01-17 04:33:42 +01:00
|
|
|
[PostgreSQL upgrade tool](upgrade.md#upgrading-postgresql).
|
2020-10-20 01:53:08 +02:00
|
|
|
|
|
|
|
### `[memcached]`
|
|
|
|
|
|
|
|
#### `memory`
|
|
|
|
|
|
|
|
Override the number of megabytes of memory that memcached should be
|
|
|
|
configured to consume; defaults to 1/8th of the total server memory.
|
|
|
|
|
2023-02-09 20:24:10 +01:00
|
|
|
#### `max_item_size`
|
|
|
|
|
|
|
|
Override the maximum size that an item in memcached can store. This defaults to
|
|
|
|
1m; adjusting it should only be necessary if your Zulip server has organizations
|
|
|
|
which have more than 20k users.
|
|
|
|
|
2020-10-20 01:53:08 +02:00
|
|
|
### `[loadbalancer]`
|
|
|
|
|
|
|
|
#### `ips`
|
|
|
|
|
2022-06-27 20:00:31 +02:00
|
|
|
Comma-separated list of IP addresses or netmasks of external load balancers
|
docs: Clarify that trust of X-Fowarded-Proto is also necessary.
Previously, `X-Forwarded-Proto` did not need to be set, and failure to
set `loadbalancer.ips` would merely result in bad IP-address
rate-limiting and incorrect access logs; after 0935d388f053, however,
failure to do either of those, if Zulip is deployed with `http_only`,
will lead to infinite redirect loops after login. These are
accompanied by a misleading error, from Tornado, of:
Forbidden (Origin checking failed - https://zulip.example.com does not match any trusted origins.): /json/events
This is most common with Docker deployments, where deployments use
another docker container, such as nginx or Traefik, to do SSL
termination. See zulip/docker-zulip#403.
Update the documentation to reinforce that `loadbalancer.ips` also
controls trust of `X-Forwarded-Proto`, and that failure to set it will
cause the application to not function correctly.
2023-06-14 04:01:53 +02:00
|
|
|
whose `X-Forwarded-For` and `X-Forwarded-Proto` should be respected. These can
|
|
|
|
be individual IP addresses, or CIDR IP address ranges.
|
2020-10-15 11:43:44 +02:00
|
|
|
|
|
|
|
### `[http_proxy]`
|
|
|
|
|
|
|
|
#### `host`
|
|
|
|
|
|
|
|
The hostname or IP address of an [outgoing HTTP `CONNECT`
|
2021-11-17 22:17:56 +01:00
|
|
|
proxy](#customizing-the-outgoing-http-proxy). Defaults to `localhost`
|
|
|
|
if unspecified.
|
2020-10-15 11:43:44 +02:00
|
|
|
|
|
|
|
#### `port`
|
|
|
|
|
|
|
|
The TCP port of the HTTP `CONNECT` proxy on the host specified above.
|
2021-11-17 22:17:56 +01:00
|
|
|
Defaults to `4750` if unspecified.
|
2021-07-14 23:54:15 +02:00
|
|
|
|
|
|
|
#### `listen_address`
|
|
|
|
|
|
|
|
The IP address that Smokescreen should bind to and listen on.
|
|
|
|
Defaults to `127.0.0.1`.
|
2022-01-05 20:04:21 +01:00
|
|
|
|
|
|
|
#### `enable_for_camo`
|
|
|
|
|
|
|
|
Because Camo includes logic to deny access to private subnets, routing
|
|
|
|
its requests through Smokescreen is generally not necessary. Set to
|
|
|
|
true or false to override the default, which uses the proxy only if
|
|
|
|
it is not the default of Smokescreen on a local host.
|
2023-01-28 02:00:15 +01:00
|
|
|
|
|
|
|
### `[sentry]`
|
|
|
|
|
|
|
|
#### `organization`
|
|
|
|
|
|
|
|
The Sentry organization used for the [Sentry deploy hook](#sentry-deploy-hook).
|
|
|
|
|
|
|
|
#### `project`
|
|
|
|
|
|
|
|
The Sentry project used for the [Sentry deploy hook](#sentry-deploy-hook).
|