Installing Zulip a Groupware Service
About
Zulip is an open-source team chat platform that combines the real-time speed of Slack with the organization of email. Its standout feature is topic-based threading, which keeps conversations categorized so you can focus on what matters without getting buried in a single, messy chat stream.
It is built for developers and fast-moving teams who need to balance personal messaging, file sharing, and deep discussions in a way that's easy to search and follow later.
Installation
Context
The software is to be installed in a LXC-container (not LXD) behind SSL-proxy on the host of the container which has * acme.sh* installed.
Download & Extract
Download the lastest using the following.
# Change in to temporary directory.
cd $(mktemp -d)
wget https://download.zulip.com/server/zulip-server-latest.tar.gz
Extract the tar file in the current temporary directory.
Install
Run the installer as root.
./zulip-server-*/scripts/setup/install --hostname=your.domain.com --email=admin@your.domain.com --self-signed-cert
--self-signed-cert: This option is even required, even when not using SSL at all.--no-init-db: This option instructs the installer to not do any database initialization. This should be used when you already have a Zulip database.
--no-overwrite-settings: This option preserves existing/etc/zulipconfiguration files.
Configure Zulip for Reverse Proxy
Edit /etc/zulip/settings.py:
# Tell Zulip it's behind a reverse proxy
EXTERNAL_HOST = 'your.domain.com'
ZULIP_ADMINISTRATOR = 'admin@your.domain.com'
# Trust the proxy's forwarded headers
USING_PGROONGA = False # unless you need it
Edit /etc/zulip/zulip.conf and add:
[application_server]
# Zulip listens on HTTP, proxy handles HTTPS.
http_only = true
[loadbalancer]
# IP must be the host's IP as seen from the container (typically its bridge interface IP).
ips = 10.0.3.1
Setting http_only = true makes Zulip's nginx listen on port 80 only (no internal TLS),
since Apache handles encryption.
Run the next scripts to apply the changes made in the ini-file.
sudo /home/zulip/deployments/current/scripts/zulip-puppet-apply
sudo /home/zulip/deployments/current/scripts/restart-server
Database Initialization
Initialize the Database
SMTP-Server
Edit the settings file /etc/zulip/settings.py and add the following lines:
EMAIL_HOST = "smtp.example.com"
EMAIL_HOST_USER = "username"
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_PASSWORD = "the-password"
For Strato:
EMAIL_HOST = "smtp.strato.com"
EMAIL_HOST_USER = "zulip@example.com"
EMAIL_USE_TLS = True
EMAIL_PORT = 587
Edit /etc/zulip/zulip-secrets.conf and add:
Restart Zulip inside the container:
Test the SMTP-Server:
Host Proxy Configuration
Apache Reverse Proxy
<VirtualHost *:443>
ServerName your.domain.com
# ... your existing SSL config ...
# WebSocket support (critical for Zulip)
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://127.0.0.1:80/$1" [P,L]
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:80/
ProxyPassReverse / http://127.0.0.1:80/
# Pass real IP to Zulip
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
</VirtualHost>
Nginx Reverse Proxy
server {
listen 80;
server_name your.domain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name your.domain.com;
# ... your existing SSL config ...
# Proxy to Zulip in the LXC container
location / {
proxy_pass http://192.168.100.10:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
# WebSocket support (critical for Zulip)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Timeouts — Zulip uses long-polling
proxy_read_timeout 1200s;
proxy_send_timeout 1200s;
}
}
The long proxy_read_timeout and proxy_send_timeout values are important — Zulip uses
long-polling for real-time updates, and default Nginx timeouts of 60s will cause constant unnecessary disconnections.
Required: Upgrade Map Block
This needs to be in your nginx.conf (in the http {} block), not in the vhost: