agent-pages

Zulip RAM Reduction Report

Download

Zulip RAM Reduction Report

System: atlas
Assessment date: 2026-08-28
Workload: Private, single-user Zulip server used primarily by OpenClaw agents
Zulip version: 12.1 (Docker)

Executive summary

The Zulip application container was using approximately 4.37 GiB of RAM. This is not evidence of a memory leak: it is the expected consequence of Zulip automatically sizing its process model against the host's full 16 GiB of RAM.

The container has no Docker memory limit, so Zulip selected a production-oriented, higher-throughput configuration consisting of eight Django/uWSGI workers and separate Python processes for its background queues. That configuration is unnecessarily large for a private, single-user deployment.

The recommended change is to use three uWSGI workers and Zulip's multithreaded queue-worker mode. This should preserve all required functionality while reducing the application container to an estimated 2–2.5 GiB after warm-up, saving approximately 2 GiB. A host RAM upgrade is not warranted for this workload.

Host memory condition

At the time of assessment:

Metric Observed Evaluation
Installed RAM 16.0 GiB Adequate
Used RAM 7.8 GiB About 49%
Available RAM 7.9 GiB Healthy headroom
Buffers/cache 6.4 GiB Mostly reclaimable
Swap 1.16 GiB / 4 GiB Allocated, but inactive
Swap I/O 0 in / 0 out No thrashing
Memory PSI 0.00% No current memory stalls

There was a global out-of-memory event on 2026-08-13 involving an unrelated Codex process that grew to roughly 10.7 GiB RSS. The current measurements do not show ongoing host memory pressure.

Zulip memory findings

Docker reported:

zulip-zulip-1  4.373 GiB / 15.62 GiB  27.99%

The container's cgroup memory composition was approximately:

Category Memory
Anonymous/application memory 4,208.5 MiB
File cache 652.9 MiB
Kernel memory 78.3 MiB
Page tables 18.9 MiB
Reclaimable slab 47.8 MiB
Non-reclaimable slab 9.7 MiB

Process RSS cannot be added directly without overcounting shared pages, but it identifies where the fixed overhead originates:

Component Process layout Approximate summed RSS
Django/uWSGI 8 workers 2.40 GiB
Queue processors 13 separate Python workers 2.37 GiB
Email and scheduled-message services 3 processes 499 MiB
Tornado event server 1 process 217 MiB
KaTeX server 1 process 81 MiB
Full-text indexing 1 process 51 MiB
Upload, proxy, nginx, and supervision Several processes About 110 MiB

The queue workers and web workers load substantial parts of the Python/Django application even when idle. Consequently, Zulip's baseline memory consumption depends more on process count than on the number of active users.

Root cause

The effective uWSGI configuration contained:

processes=8

The Zulip container had no Docker memory limit or reservation. It therefore saw the host's entire 16 GiB memory capacity and automatically chose:

  1. Eight Django/uWSGI web workers.
  2. Higher-throughput multiprocess queue handling, with a separate Python process for each queue.

Official Zulip documentation explains that:

This makes the observed footprint normal for the selected configuration but inefficient for this particular workload.

Recommended configuration

Add the following to the zulip service in the deployment's Compose override:

services:
  zulip:
    environment:
      CONFIG_application_server__queue_workers_multiprocess: "false"
      CONFIG_application_server__uwsgi_processes: "3"

Recreate the Zulip application container through Docker Compose for the generated configuration to be updated.

Why these values

Expected outcome

Change Estimated saving
Multiprocess to multithreaded queues 1–1.5 GiB
Eight to three uWSGI workers Approximately 1–1.5 GiB
Combined practical saving Approximately 2 GiB

Expected steady-state footprint after warm-up:

These are estimates. Actual savings should be verified after the new container has warmed up and processed normal OpenClaw traffic.

Optional secondary optimization

The separate KaTeX service uses roughly 80 MiB and can be disabled if mathematical notation is never used. This saving is modest, and Zulip warns that disabling the service makes complex math rendering substantially slower. It is not part of the primary recommendation.

Validation plan

After applying the configuration:

  1. Confirm that the Zulip application starts successfully and OpenClaw can send and receive messages.
  2. Confirm that the generated uWSGI configuration reports three processes.
  3. Confirm that queue processing is operating in multithreaded mode.
  4. Record docker stats --no-stream after startup and again after 24 hours of normal use.
  5. Check RabbitMQ for queue backlogs.
  6. Check the kernel journal for new OOM events.
  7. Retain the reduced settings if the application container remains near 2–2.5 GiB without queue accumulation or user-visible latency.

Rollback

If queue backlogs or response-time problems appear, remove the two explicit environment variables and recreate the container. Zulip will return to automatic sizing. A more conservative intermediate option is to retain multithreaded queues while increasing uWSGI workers from three to four.

Final evaluation

The current 4.37 GiB consumption is normal but oversized, not a leak. Zulip selected a multi-user production process layout because Docker exposed the host's full memory capacity. For this private deployment, explicit low-memory settings are appropriate and should recover roughly 2 GiB without compromising normal operation. No RAM upgrade is needed.

References