Skip to main content

Command Palette

Search for a command to run...

From Ghost to Zulip: Setting Up My Local Development Environment

A complete, beginner-friendly guide to setting up a local development environment for contributing to Zulip using WSL2.

Updated
4 min read
From Ghost to Zulip: Setting Up My Local Development Environment

Introduction

Setting up Zulip locally is not a typical Django setup.

After contributing to Ghost, I wanted to challenge myself with a larger backend system, one that reflects real-world complexity. Zulip stood out because of its scale, architecture, and active open-source community.

Zulip is built using:

  • Django

  • Tornado

  • PostgreSQL

  • RabbitMQ

While the setup process is well-documented, I still faced some issues, especially around Python virtual environments and WSL configuration.

In this post, I’ll walk through:

  • How I set up Zulip locally on WSL2

  • The issues I faced during setup

  • How I fixed them, including the tricky activate_this.py problem

If you’re setting up Zulip for the first time, this guide should save you time and effort.

Who This Post Is For

This post is helpful if you are:

  • Contributing to Zulip for the first time

  • Moving from smaller projects to large backend systems

  • Using WSL2 on Windows

  • Facing Python 3.12 or activate_this.py issues during setup

Prerequisites for Zulip Local Setup

Before starting, make sure your system meets these requirements.

General Requirements

  • 2GB or more RAM

  • Stable internet connection

  • GitHub account

Windows Requirements

  • Windows 10 or 11 (64-bit)

  • Virtualisation enabled (VT-x / AMD-V)

  • Administrator access

Git and GitHub Setup

If Git is already configured, you can skip this step.

Generate an SSH key:

ssh-keygen -t ed25519 -C "your_email@example.com"

Copy the public key:

cat ~/.ssh/id_ed25519.pub

Add it to:
GitHub → Settings → SSH and GPG Keys

This allows you to clone Zulip securely.

Setting Up WSL2 for Zulip Development

If you’re already using native Ubuntu or WSL2, you can skip this section.

Enable Virtualization

Enable VT-x / AMD-V in your BIOS.

Install WSL2

wsl --install

This installs Ubuntu automatically.

Use a Fresh WSL Instance

A clean WSL setup helps avoid conflicts with existing Python or Node installations.

Enable systemd (Important)

Zulip relies on system services like PostgreSQL, Redis, and RabbitMQ.

Edit the config file:

sudo nano /etc/wsl.conf

Add:

[boot]
systemd=true

Restart WSL:

wsl --shutdown

Install Required Services

Update your system:

sudo apt update && sudo apt upgrade

Install required services:

sudo apt install rabbitmq-server memcached redis-server postgresql

Configure RabbitMQ

Edit the config file:

sudo nano /etc/rabbitmq/rabbitmq-env.conf

Add:

NODE_IP_ADDRESS=127.0.0.1
NODE_PORT=5672

Important WSL Tips

Use WSL’s Native Disk

Avoid working inside /mnt/c/....

Always work from:

cd ~

Generate SSH Key Inside WSL

ssh-keygen -t ed25519 -C "your_email@example.com"
cat ~/.ssh/id_ed25519.pub

Add this key to GitHub as well.

Cloning the Zulip Repository

Fork Zulip

Go to:
https://github.com/zulip/zulip
Click Fork.

Clone Your Fork

git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git
cd zulip
git remote add -f upstream https://github.com/zulip/zulip.git

Running Zulip Locally

Install Dependencies

This step installs all required dependencies and may take 5–10 minutes.

./tools/provision

Activate the Virtual Environment

source .venv/bin/activate

Start the Development Server

./tools/run-dev

Visit:
http://localhost:9991

Common Zulip Setup Issues (and Fixes)

Issue 1: Missing activate_this.py

Error

FileNotFoundError: No such file or directory ... activate_this.py

Why This Happens

Zulip now uses uv, a modern Python package manager.
Unlike virtualenv, it does not automatically create activate_this.py.

Some scripts still expect this file.

Fix

Manually create it:

cat > .venv/bin/activate_this.py << 'EOF'
import os
import site
import sys

bin_dir = os.path.dirname(os.path.abspath(__file__))
os.environ["PATH"] = os.pathsep.join([bin_dir] + os.environ.get("PATH", "").split(os.pathsep))

base = os.path.dirname(bin_dir)
os.environ["VIRTUAL_ENV"] = base

site_packages = os.path.join(base, 'lib', 'python{}.{}'.format(*sys.version_info[:2]), 'site-packages')

prev = set(sys.path)
site.addsitedir(site_packages)
sys.real_prefix = sys.prefix
sys.prefix = base

new = list(sys.path)
sys.path[:] = [i for i in new if i not in prev] + [i for i in new if i in prev]
EOF

Issue 2: Django Not Found (Python 3.12)

Error

ModuleNotFoundError: No module named 'django'

Root Cause

Older scripts used:

sys.version[:3]

With Python 3.12, this incorrectly returns "3.1".

Fix

Use:

'{}.{}'.format(*sys.version_info[:2])

Then re-run:

./tools/provision

Key Learnings from Setting Up Zulip

Technical Learnings

  • Modern tools like UV change long-standing Python assumptions

  • Understanding virtual environment internals is useful for debugging

  • A proper WSL2 + systemd setup is critical for backend services

Soft Learnings

  • Large codebases require patience

  • Error messages often point to the real issue

  • Zulip’s documentation and community are extremely helpful

Conclusion

Moving from Ghost to Zulip has been a rewarding step in my open-source journey.

Zulip’s development environment is complex but well-architected, making it a great project for anyone who wants to grow in backend engineering.

If you’re stuck on setup, especially around activate_this.py you’re not alone. Once the environment is running, contributing becomes much smoother.

Resources