# docker-zulip: Running the Zulip Server as Containers

> docker-zulip is the Apache-2.0 licensed official container image for a production Zulip server, with a compose file, a Helm chart and a two-stage build that produces a release tarball from a chosen Git ref. It suits organisations that deploy Zulip through containers or Kubernetes rather than through the traditional installer.

**zulip/docker-zulip** — Container configurations, images, and examples for Zulip.

- Repository: https://github.com/zulip/docker-zulip
- Website: https://zulip.com/
- Stars: 765 · Forks: 323
- Language: Shell
- License: Apache-2.0
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/zulip-docker-zulip

## The official image, and what it replaced

This repository holds container configurations, images and examples for Zulip, and the readme describes it as the official Docker container image for running a Zulip server in production, with built images available from ghcr.io. The pull command carries the server version in the tag.

```console
$ docker pull ghcr.io/zulip/zulip-server:12.3-0
```

The current Zulip version is 12.3 and the current Docker image version is 12.3-0, so the image version carries an extra component for the packaging itself. The note about a previous packaging matters for anyone with an existing deployment: an earlier packaging of Zulip for Docker still exists on Docker Hub as zulip/docker-zulip, and that version will continue to be supported through the end of the Zulip Server 11.x series, with upgrade steps and separate documentation for the older packaging. So there are two supported lines at once, and the older one has a stated end date.

## Docker or Kubernetes, and what the prerequisites exclude

To use the image you need an installation of Docker and Docker Compose, or a Kubernetes runtime engine. The memory recommendation is at least 2GB of available RAM for a production Zulip server, and 4GB if you are building the container rather than using the pre-built images. The documentation is candid about the lighter case, saying that for testing, or when you are not expecting many users or messages, you can get away with significantly less, especially for the postgres, memcached and similar containers, because Docker makes it easy to sharply limit the RAM allocated to the services Zulip depends on, at the cost of potential performance issues. One exclusion is absolute: the project does not support docker-rootless or uDocker, because Zulip needs root access to set properties like the maximum number of open file descriptions via ulimit, which matters for handling thousands of connected clients.

## The compose file and how it treats secrets

The compose file is where the deployment shape becomes clear, and the secret handling is the first thing to notice. The database service runs the zulip/zulip-postgresql:14 image with a restart policy of unless-stopped, and the password arrives through a Docker secret rather than an environment variable, with POSTGRES_PASSWORD_FILE pointing at the secret path while the database name and user are plain values. The memcached service takes the same approach, reading its password from a file to build the SASL password database at startup. RabbitMQ does the same thing, reading the password from its secret file and writing the default user and password into its configuration before execing the entrypoint. Redis follows the same pattern. Every backing service is set to restart unless-stopped and to attach false, so they run as plain background dependencies rather than as attached streams.

## Building the image from a chosen Git ref

The Dockerfile is a two-stage build and the comments explain why. The first stage builds a Zulip development environment image and uses tools/build-release-tarball to generate a production release tarball from the provided Git ref. The base is ubuntu:24.04 with the locales, python3, sudo and tzdata packages installed and a zulip user created with uid 1000. The ref is a build argument, so you can point the build at a branch or a tag.

```dockerfile
ARG ZULIP_GIT_URL=https://github.com/zulip/zulip.git
ARG ZULIP_GIT_REF=12.3
```

The provisioning step then runs the release tarball build, using uv to run the tool without re-syncing the environment, and moves the resulting tarball to a fixed path. The image also carries the standard OpenContainers labels for source, licence, title, version and documentation, with the version label wired to the Git ref, so an image built from a given ref is identifiable from its metadata.

## The Helm chart, the custom files hook and the release notes

The repository is not only a compose file. There is a helm/ directory with a Zulip chart and its own changelog at helm/zulip/CHANGELOG.md, separate from the image changelog in CHANGELOG.md at the root, so an operator can tell whether a change affects the image or the chart. There is a custom_zulip_files/ directory, which is the mechanism for site-specific customisations that have to survive a container upgrade, and a compose.override.yaml.example alongside compose.yaml, which is how you add a local override without editing the tracked file. There is a zulip-certbot-setup directory and an upgrade-postgresql directory, both of which point at the operational work a major version bump can bring. The documentation is published separately in the Zulip Docker deployment documentation, and the readme links a docker topic in the Zulip production-help chat for questions.

## The trade-off the project states plainly

The readme does not oversell the container route, and the sentence is worth repeating because it is unusual in project documentation. It recommends using the Docker image if your organisation has a preference for deploying services using Docker, and then says that deploying with Docker moderately increases the effort required to install, maintain and upgrade a Zulip installation compared with the standard Zulip installer. That is the honest framing. The standard installer knows the shape of a Zulip deployment, and the container route trades that for portability, so the work moves from the installer to your own orchestration. What you gain is the standard container story: images on a registry, secrets as files, resource limits per service, and a Helm chart for Kubernetes. The topics on the repository reflect that range with docker, helm, kubernetes and zulip, and the package manifest is a single dev dependency of prettier, which tells you the repository itself is not a JavaScript project.

## Release cadence and what to check before migrating

The releases follow the server version, with tags 12.1-0 on 2026-06-26, 12.2-0 on 2026-08-10 and 12.3-0 on 2026-09-21, and the last push to the repository was on 2026-10-01. The version numbers in the tags are therefore the server release plus the packaging revision, and a deployment pins the image tag to get a specific combination of the two. Two things are worth checking before you move an existing installation. The first is the upgrade path from the legacy Docker Hub packaging, for which there is a dedicated document on composing an upgrade from legacy, and the second is the supported end of the 11.x series for that older image. A third is more practical than documented: whether your host can give the container the file descriptor limits Zulip wants, since that root requirement is the one thing the project cannot work around.

## Conclusion

docker-zulip fits an organisation that already runs its services as containers and wants Zulip deployed the same way, with a Helm chart for Kubernetes and prebuilt images on ghcr.io. It is the wrong choice if you want the least effort, since the documentation says plainly that Docker moderately increases the effort required to install, maintain and upgrade compared with the standard installer, and it is the wrong choice on a rootless Docker setup, which the project does not support. Before you plan the migration from the older Docker Hub packaging, read the documented upgrade steps, because that old image is only supported through the end of the 11.x series.

## FAQ

### How do I pull the official Zulip server image?

Built images are available from ghcr.io, so docker pull ghcr.io/zulip/zulip-server:12.3-0 gets the image for the current Zulip version 12.3, with the image tag carrying an extra packaging revision on top of the server version.

### Does the Zulip Docker image support rootless Docker?

No. The project does not support docker-rootless or uDocker, because Zulip needs root access to set properties such as the maximum number of open file descriptions via ulimit, which matters for handling thousands of connected clients.

### How much memory does running Zulip in Docker need?

The documentation recommends at least 2GB of available RAM for a production server, and 4GB if you are building the container rather than using the pre-built images. For testing you can get away with significantly less, since Docker makes it easy to limit the RAM of the Postgres, memcached and Redis containers, at the cost of potential performance issues.

### What happened to the old Zulip Docker image on Docker Hub?

A previous packaging of Zulip for Docker still exists on Docker Hub as zulip/docker-zulip, and it will continue to be supported through the end of the Zulip Server 11.x series. The upgrade steps and the documentation for the older packaging are both linked from the readme.

### How are database passwords passed to the Zulip containers?

Through Docker secrets rather than environment variables. The compose file mounts secrets for the Postgres, memcached and RabbitMQ passwords, and each service reads its password from a file such as POSTGRES_PASSWORD_FILE at startup.

## Sources

- [License: Apache-2.0](https://github.com/zulip/docker-zulip/blob/main/LICENSE)
- [Project website](https://zulip.com/)
- [README](https://github.com/zulip/docker-zulip/blob/main/README.md)
- [Releases](https://github.com/zulip/docker-zulip/releases)
- [zulip/docker-zulip on GitHub](https://github.com/zulip/docker-zulip)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zulip-docker-zulip
