DEV Community

Cover image for From Ghost to Zulip: Setting Up Zulip Locally on WSL2 (and Fixing Tricky Python Issues)
Abdul Talha
Abdul Talha

Posted on

From Ghost to Zulip: Setting Up Zulip Locally on WSL2 (and Fixing Tricky Python Issues)

Introduction

After contributing to Ghost, I wanted to push myself further by working on a larger and more complex backend system.

That curiosity led me to Zulip, an open-source team collaboration tool known for its topic-based threading and production-grade architecture.

Zulip is built with:

  • Django
  • Tornado
  • PostgreSQL
  • RabbitMQ

Setting up Zulip locally isn’t hard, but it’s very different from a typical Django project. I ran into a few unexpected 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 activate_this.py problem

If you’re new to Zulip or moving into larger backend systems, this guide should help you avoid common setup blockers.

Who This Post Is For

This post is for:

  • Developers contributing to Zulip for the first time
  • Open-source contributors moving beyond small projects
  • Anyone using WSL2 on Windows
  • Developers stuck with Python 3.12 or activate_this.py errors

Prerequisites

Before starting, make sure you have:

General

  • 2GB+ RAM
  • Stable internet connection
  • GitHub account

Windows

  • Windows 10/11 (64-bit)
  • Virtualisation enabled (VT-x / AMD-V)
  • Admin access

Git and GitHub Setup

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

Generate an SSH key:

ssh-keygen -t ed25519 -C "your_email@example.com"
Enter fullscreen mode Exit fullscreen mode

Copy the public key:

cat ~/.ssh/id_ed25519.pub
Enter fullscreen mode Exit fullscreen mode

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

Setting Up WSL2 for Zulip

If you already use native Ubuntu or WSL2, feel free to skip ahead.

Enable Virtualisation

Make sure VT-x / AMD-V is enabled in BIOS.

Install WSL2

wsl --install
Enter fullscreen mode Exit fullscreen mode

This installs Ubuntu automatically.

Enable systemd (Required)

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

Edit:

sudo nano /etc/wsl.conf
Enter fullscreen mode Exit fullscreen mode

Add:

[boot]
systemd=true
Enter fullscreen mode Exit fullscreen mode

Restart WSL:

wsl --shutdown
Enter fullscreen mode Exit fullscreen mode

Install Required Services

Update your system:

sudo apt update && sudo apt upgrade
Enter fullscreen mode Exit fullscreen mode

Install services:

sudo apt install rabbitmq-server memcached redis-server postgresql
Enter fullscreen mode Exit fullscreen mode

RabbitMQ Configuration

sudo nano /etc/rabbitmq/rabbitmq-env.conf
Enter fullscreen mode Exit fullscreen mode

Add:

NODE_IP_ADDRESS=127.0.0.1
NODE_PORT=5672
Enter fullscreen mode Exit fullscreen mode

Cloning the Zulip Repository

Fork Zulip from GitHub, then 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
Enter fullscreen mode Exit fullscreen mode

Running Zulip Locally

Install dependencies:

./tools/provision
Enter fullscreen mode Exit fullscreen mode

Activate the virtual environment:

source .venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

Start the dev server:

./tools/run-dev
Enter fullscreen mode Exit fullscreen mode

Visit:

http://localhost:9991
Enter fullscreen mode Exit fullscreen mode

Common Issues I Faced (and How I Fixed Them)

Issue 1: Missing activate_this.py

Error

FileNotFoundError: No such file or directory ... activate_this.py
Enter fullscreen mode Exit fullscreen mode

Why this happens

Zulip now uses uv, which doesn’t automatically create activate_this.py. Some scripts still expect it.

Fix

Manually create the file:

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
Enter fullscreen mode Exit fullscreen mode

Issue 2: Django Not Found (Python 3.12)

Error

ModuleNotFoundError: No module named 'django'
Enter fullscreen mode Exit fullscreen mode

Cause

Older scripts used:

sys.version[:3]
Enter fullscreen mode Exit fullscreen mode

This breaks with Python 3.12.

Fix

Use:

'{}.{}'.format(*sys.version_info[:2])
Enter fullscreen mode Exit fullscreen mode

Then rerun:

./tools/provision
Enter fullscreen mode Exit fullscreen mode

What I Learned

Technical

  • Modern Python tooling like uv changes old assumptions
  • Understanding virtual environments helps with debugging
  • WSL2 + systemd setup is critical for backend services

Non-Technical

  • Large codebases require patience
  • Error messages usually point to the real issue
  • Open-source communities are incredibly helpful

Final Thoughts

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

Zulip’s setup may feel overwhelming at first, but once the environment is running, contributing becomes much smoother. If you’re stuck on setup issues like activate_this.py, you’re definitely not alone.

I hope this post helps you get unblocked.

Useful Links

Top comments (2)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.