Skip to content


Repository files navigation

Welcome to docker-zulip!

docker-zulip stream

This is a container image for running Zulip (GitHub) in production. Image available from:

  • Docker Hub (docker pull zulip/docker-zulip:8.4-0)

Current Zulip version: 8.4 Current Docker image version: 8.4-0

Project status: Alpha. While this project works and is used by many sites in production, configuring is substantially more error-prone than the normal Zulip installer (which Just Works). We recommend this project if you want to host Zulip using Docker, but both setting up and maintaining a Zulip server is simpler and less error-prone with the normal installer than with Docker.


This project defines a Docker image for a Zulip server, as well as sample configuration to run that Zulip web/application server with each of the major services that Zulip uses in its own container: redis, postgres, rabbitmq, memcached.

We have configuration and documentation for Docker Compose and Kubernetes; contributions are welcome for documenting other container runtimes and flows.

If you aren't already a Docker expert, we recommend starting by reading our brief overview of how Docker and containers work in the next section.

The Docker data storage model

Docker and other container systems are built around shareable container images. An image is a read-only template with instructions for creating a container. Often, an image is based on another image, with a bit of additional customization. For example, Zulip's zulip-postgresql image extends the standard postgresql image (by installing a couple postgres extensions). And the zulip image is built on top of a standard ubuntu image, adding all the code for a Zulip application/web server.

Every time you boot a container based on a given image, it's like booting off a CD-ROM: you get the exact same image (and anything written to the image's filesystem is lost). To handle persistent state that needs to persist after the Docker equivalent of a reboot or upgrades (like uploaded files or the Zulip database), container systems let you configure certain directories inside the container from the host.

This project's docker-compose.yml configuration file uses Docker managed volumes to store persistent Zulip data. If you use the Docker Compose deployment, you should make sure that Zulip's volumes are backed up, to ensure that Zulip's data is backed up.


To use docker-zulip, you need the following:

  • An installation of Docker and Docker Compose or a Kubernetes runtime engine.
  • We recommend at least 2GB of available RAM for running a production Zulip server; you'll want 4GB if you're building the container (rather than using the prebuilt images). If you're just testing and/or aren't expecting a lot of users/messages, you can get away with significantly less especially for the postgres, memcached, etc. containers, because Docker makes it easy to sharply limit the RAM allocated to the services Zulip depends on, like redis, memcached, and postgresql (at the cost of potential performance issues).
  • This project doesn't support docker-rootless; Zulip needs root access to set properties like the maximum number of open file descriptions via ulimit (which is important for it to handle thousands of connected clients).

Running a Zulip server with docker-compose

To use this project, we recommend starting by cloning the repo (since you'll want to edit the docker-compose.yml file in this project):

git clone
cd docker-zulip
# Edit `docker-compose.yml` to configure; see docs below

If you're in hurry to try Zulip, you can skip to start the Zulip server, but for production use, you'll need to do some configuration.


With docker-compose, it is traditional to configure a service by setting environment variables declared in the zulip -> environment section of the docker-compose.yml file; this image follows that convention.

Mandatory settings. You must configure these settings (more discussion in the main Zulip installation docs):

  • SETTING_EXTERNAL_HOST: The hostname your users will use to connect to your Zulip server. If you're testing on your laptop, the default of localhost.localdomain is great.
  • SETTING_ZULIP_ADMINISTRATOR: The email address to receive error and support emails generated by the Zulip server and its users.

Mandatory settings for serious use. Before you allow production traffic, you need to also set these:

  • POSTGRES_PASSWORD and SECRETS_postgres_password should both be a password for the Zulip container to authenticate to the Postgres container. Since you won't use this directly, you just want a long, randomly generated string. While SECRETS_postgres_password is synced to the Zulip container on every boot, POSTGRES_PASSWORD is only accessed by the postgres container on first boot, so if you later want to change your postgres password after booting the container, you'll need to either do an ALTER ROLE query inside the postgres container or rebuild the postgres database (only if you don't need your data!).
  • RABBITMQ_DEFAULT_PASS and SECRETS_rabbitmq_password are similar, just for the RabbitMQ container.
  • MEMCACHED_PASSWORD and SECRETS_memcached_password are similar, just for the memcached container.
  • REDIS_PASSWORD and SECRETS_redis_password are similar, just for the Redis container.
  • SECRETS_secret_key should be a long (e.g. 50 characters), random string. This value is important to keep secret and constant over time, since it is used to (among other things) sign login cookies (so if you change this, all your users will be forcibly logged out).
  • SETTING_EMAIL_*: Where you configure Zulip's ability to send outgoing email.

Other settings. If an environment variable name doesn't start with SETTING or SECRETS in docker-compose.yml, it is specific to the Docker environment. Standard Zulip server settings are secrets are set using the following syntax:

  • SETTING_MY_SETTING will become MY_SETTING in /etc/zulip/
  • SECRETS_my_secret will become my_secret in /etc/zulip/zulip-secrets.conf.

Reading the comments in the sample Zulip's file is the best way to learn about the full set of Zulip's supported server-level settings.

Most settings in Zulip are just strings, but some are lists (etc.) which you need to encode in the YAML file. For example,

  • For AUTHENTICATION_BACKENDS, you enter ZULIP_AUTH_BACKENDS as a comma-separated list of the backend names (E.g. "EmailAuthBackend,GitHubAuthBackend").

Reducing RAM usage. By default, the Zulip server automatically detect 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). This algorithm might see the host's memory, not the docker container's memory. Set to QUEUE_WORKERS_MULTIPROCESS to true or false to override the automatic calculation.

SSL Certificates. By default, the image will generate a self-signed cert. You can set SSL_CERTIFICATE_GENERATION: "certbot" within docker-compose.yml to enable automatically-renewed Let's Encrypt certificates. By using certbot here, you are agreeing to the Let's Encrypt ToS.

You can also provide an SSL certificate for your Zulip server by putting it in /opt/docker/zulip/zulip/certs/ (by default, the zulip container startup script will generate a self-signed certificate and install it in that directory).

Load balancer. To tell Zulip that it is behind a load balancer, you must set LOADBALANCER_IPS to a comma-separated list of IPs or CIDR ranges. This will tell Zulip to pass the real IP of the client, instead of the IP of the load balancer itself, by setting the IPs under [loadbalancer] in zulip.conf.

Your proxy must provide both X-Forwarded-For and X-Forwarded-Proto headers. See the Zulip documentation for sample nginx, Apache2, and HAProxy configurations, as well as notes for other proxies.

Manual configuration

The way the environment variables configuration process described in the last section works is that the script that runs when the Docker image starts up will generate a Zulip file file based on your settings every time you boot the container. This is convenient, in that you only need to edit the docker-compose.yml file to configure your Zulip server's settings.

An alternative approach is to set MANUAL_CONFIGURATION: "True" and LINK_SETTINGS_TO_DATA: "True" in docker-compose.yml. If you do that, you can provide a file and a zulip-secrets.conf file in /opt/docker/zulip/zulip/settings/etc-zulip/, and the container will use those.

Starting the server

You can boot your Zulip installation with:

docker-compose pull
docker-compose up

This will boot the 5 containers declared in docker-compose.yml. The docker-compose command will print a bunch of output, and then eventually hang once everything is happily booted, usually ending with a bunch of lines like this:

rabbitmq_1   | =INFO REPORT==== 27-May-2018::23:26:58 ===
rabbitmq_1   | accepting AMQP connection <0.534.0> (

You can inspect what containers are running in another shell with docker-compose ps (remember to cd into the docker-zulip directory first).

If you hit Ctrl-C, that will stop your Zulip server cluster. If you'd prefer to have the containers run in the background, you can use docker-compose up -d.

If you want to build the Zulip image yourself, you can do that by running docker-compose build; see also the documentation on building a custom Git version version.

Connecting to your Zulip server

You can now connect to your Zulip server. For example, if you set this up on a laptop with the default port mappings and SETTING_EXTERNAL_HOST, typing http://localhost/ will take you to your server. Note that in this default scenario, (1) you'll have to proceed past a self-signed SSL error, and (2) you won't be able to login until you create an organization, but visiting the URL is a good way to confirm that your networking configuration is working correctly.

You can now follow the normal instructions for how to create a Zulip organization and log in to your new Zulip server (though see the following section for how to run management commands).

Running management commands

From time to time, you'll need to attach a shell to the Zulip container so that you can run commands, check logs, etc. The following are helpful examples:

# Get a (root) shell in the container so you can access logs
docker-compose exec zulip bash
# Create the initial Zulip organization
docker-compose exec -u zulip zulip \
    /home/zulip/deployments/current/ generate_realm_creation_link

Since that process for running management commands is a pain, we recommend using a wrapper script for running management commands.

Using a custom certificate bundle for outgoing HTTP connections

If you are sitting behind a custom CA and want to build the Zulip image yourself, special care is required.

The Zulip build process installs packages via yarn and pip, and these need packages to be configured to use your custom CA certificates. You will need to get your certificate bundle into the docker image, either by adding a COPY somewhere or by replacing the FROMs with a custom ubuntu image that includes your bundle. The recommended way is to have your own base image which has your bundle ready at the default /etc/ssl/certs/ca-certificates.crt.

The next and last step is to set up the CUSTOM_CA_CERTIFICATES argument in docker-compose.yml to point to your CA bundle, e.g. to /etc/ssl/certs/ca-certificates.crt.

At this point you are ready to build Zulip.


See the separate upgrading document for upgrading instructions.

Running a Zulip server with Kubernetes

A Kubernetes pod file is in the kubernetes/ folder; you can run it with kubectl create -f ./kubernetes/.

You should read the docker-compose section above to understand how this works, since it's a very similar setup. You'll want to clone this repository, edit the zulip-rc.yml to configure the image, etc.

Installing minikube for testing

The fastest way to get Kubernetes up and running for testing without signing up for a cloud service is to install Minikube on your system.

Helm charts

Read the Helm Chart README to learn more about installing Zulip on a Kubernetes cluster with Helm.

Feedback is welcome in the [helm-chart-thread]:

Scaling out and high availability

This image is not designed to make it easy to run multiple copies of the zulip application server container (and you need to know a lot about Zulip to do this sort of thing successfully). If you're interested in running a high-availablity Zulip installation, your best bet is to get in touch with the Zulip support team at

Networking and reverse proxy configuration

When running your container in production, you may want to put your Zulip container behind an HTTP proxy. This wiki page documents how to do this correctly with nginx.

See also the Zulip documentation on reverse proxies

By default, Zulip will only interact with user traffic over HTTPS. However, if your networking environment is such that the Zulip server is behind a load balancer and you need the Zulip server to respond over HTTP, you can configure that via setting DISABLE_HTTPS: "True" in the Docker environment (docker-compose.yml).


Common issues include:

  • Invalid configuration resulting in the zulip container not starting; check docker-compose ps to see if it started, and then read the logs for the Zulip container to see why it failed.
  • A new Zulip setting not being passed through the Docker script properly. If you run into this sort of problem you can work around it by specifying a ZULIP_CUSTOM_SETTINGS with one setting per line below, but please report an issue so that we can fix this for everyone else.

Community support

You can get community support and tell the developers about your experiences using this project on #production-help on, the Zulip community server.

In late May 2018, we completed a complete rewrite of this project's documentation, so we'd love any and all feedback!


We love community contributions, and respond quickly to issues and PRs. Some particularly useful ways to contribute right now are:

  • Contribute to this documentation by opening issues about what confused you or submitting pull requests!
  • Reporting bugs or rough edges!


Huge thanks to everyone who has contributed. Special thanks to Alexander Trost, who created docker-zulip and did a huge amount of the early work required to make a high-quality Docker image for Zulip possible.