Why Self-Host Zulip on RamNode?
Self-hosting Zulip gives you full control over your data, no per-seat pricing, and the ability to customize the platform to fit your team's workflow. RamNode's SSD VPS plans are a cost-effective fit with NVMe-backed storage, generous bandwidth, and straightforward pricing starting at $4/month.
Small Teams
$12/month plan (2 vCPU / 2GB RAM)
Larger Organizations
4GB RAM tier for sustained load
Prerequisites
- • A RamNode VPS running Ubuntu 22.04 LTS (recommended by the Zulip project)
- • A domain name pointed to your VPS IP (e.g.,
chat.yourdomain.com) - • Root or sudo access to your server
- • A working email address for the initial admin account
Important: Zulip's installer requires a fresh OS install and will configure Nginx, PostgreSQL, Redis, and RabbitMQ automatically. Do not run this on a server with existing web services.
Provision Your RamNode VPS
Log into your RamNode control panel and deploy a new VPS:
- Select Ubuntu 22.04 LTS as your OS
- Choose at minimum 2GB RAM (4GB recommended for production)
- Select your preferred datacenter location
- Set a strong root password or add your SSH public key
Once provisioned, note your server's IP address.
Point Your Domain to the VPS
In your DNS provider, create an A record pointing your desired subdomain to your VPS IP:
Type: A
Name: chat
Value: YOUR_VPS_IP
TTL: 300Allow a few minutes for DNS propagation before proceeding. Zulip's installer will attempt to verify DNS and provision a Let's Encrypt TLS certificate automatically.
Initial Server Setup
ssh root@YOUR_VPS_IP
apt update && apt upgrade -y
apt install -y curl wget gitSet Hostname
hostnamectl set-hostname chat.yourdomain.com
echo "127.0.1.1 chat.yourdomain.com chat" >> /etc/hostsDownload the Zulip Installer
Download the latest Zulip Server release. Always check zulip.com/server for the latest version before installing.
cd /root
wget https://download.zulip.com/server/zulip-server-latest.tar.gz
tar -xf zulip-server-latest.tar.gz
cd zulip-server-*/Run the Zulip Installer
The installer handles the entire stack configuration. Run it with your domain and admin email:
./scripts/setup/install --certbot-email=you@yourdomain.com \
--hostname=chat.yourdomain.comWhat this does:
- • Installs and configures Nginx as a reverse proxy with TLS via Certbot
- • Sets up PostgreSQL for the Zulip database
- • Configures Redis for caching and session storage
- • Installs RabbitMQ for background job processing
- • Deploys the Zulip Django application with Supervisor
The installation typically takes 5 to 10 minutes.
💡 Note: If DNS has not fully propagated yet, Let's Encrypt certificate issuance may fail. You can skip TLS during setup with --no-init-db and configure it manually afterward, or wait and re-run the installer once DNS resolves.
Create Your Organization
Once the installer completes, generate a one-time organization creation link:
su zulip -c '/home/zulip/deployments/current/manage.py generate_realm_creation_link'Open the output URL in your browser. You will be prompted to:
- Set your organization name
- Create the admin user account
- Choose your organization type (company, community, education, etc.)
Configure Email (SMTP)
Zulip requires a working SMTP connection for invitations, password resets, and notifications. Edit the configuration file:
nano /etc/zulip/settings.pyExample using a generic SMTP provider (Mailgun, Postmark, or Amazon SES recommended):
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.yourprovider.com'
EMAIL_HOST_USER = 'you@yourdomain.com'
EMAIL_HOST_PASSWORD = 'your-smtp-password'
EMAIL_PORT = 587
EMAIL_USE_TLS = TrueRestart Zulip to apply changes:
su zulip -c '/home/zulip/deployments/current/scripts/restart-server'Test email delivery from the Zulip admin panel under Server settings → Email.
Configure the Firewall
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status verboseZulip's Nginx configuration already handles HTTP to HTTPS redirection. Port 80 only needs to be open for Certbot's certificate renewal challenge.
Enable Automatic Certificate Renewal
Certbot installs a systemd timer for automatic renewal. Verify it is active:
systemctl status certbot.timerDo a dry run to confirm renewal will work:
certbot renew --dry-runSet Up Automated Backups
Zulip includes a built-in backup utility. Create a full backup:
/home/zulip/deployments/current/manage.py backup --output=/root/zulip-backup.tar.gzSchedule Daily Backups
crontab -eAdd the following line to run a backup every day at 2:00 AM:
0 2 * * * /home/zulip/deployments/current/manage.py backup --output=/root/backups/zulip-$(date +\%F).tar.gzTip: For off-server backup storage, sync your backup directory to an S3-compatible bucket using rclone or aws-cli. Keeping backups only on the same VPS is not a disaster recovery strategy.
Keeping Zulip Updated
# Download the latest release
wget https://download.zulip.com/server/zulip-server-latest.tar.gz -O /tmp/zulip.tar.gz
# Run the upgrade script
/home/zulip/deployments/current/scripts/upgrade-zulip /tmp/zulip.tar.gzThe upgrade script handles database migrations and service restarts automatically. Always read the Zulip changelog before major version upgrades.
Performance Tuning for RamNode VPS
PostgreSQL (2GB RAM VPS)
shared_buffers = 256MB
work_mem = 4MB
maintenance_work_mem = 64MB
effective_cache_size = 768MBsystemctl restart postgresqlAdd Swap Space
Zulip can spike in memory usage during heavy indexing. Adding swap provides a safety buffer on lower-RAM plans:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstabArchitecture Summary
| Component | Handled By |
|---|---|
| Web server / TLS | Nginx + Certbot |
| Application server | Django + Supervisor |
| Database | PostgreSQL |
| Message queue | RabbitMQ |
| Cache / sessions | Redis |
| Backups | Zulip built-in CLI |
Troubleshooting
Installer fails at Let's Encrypt step
DNS has not propagated yet. Wait a few minutes and re-run the installer, or use --no-init-db to skip TLS and configure Certbot manually afterward.
Zulip services are not starting
Check supervisor status for individual service errors:
supervisorctl status
supervisorctl tail -f zulip-djangoEmail is not being delivered
Verify SMTP credentials in /etc/zulip/settings.py and use the admin panel's built-in email test. Check that port 587 is not blocked at the network level.
High memory usage
Zulip runs several worker processes. On a 2GB VPS, reduce Tornado workers in /etc/zulip/zulip.conf under [application_server] by setting tornado_processes = 1.
