snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-15 15:05:30 +05:00
commit 64898b2c79
8999 changed files with 1757358 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
# Default configuration for a Zulip app frontend
class zulip::profile::app_frontend {
include zulip::profile::base
include zulip::app_frontend_base
include zulip::app_frontend_once
$nginx_http_only = zulipconf('application_server', 'http_only', false)
if $nginx_http_only {
$nginx_listen_port = zulipconf('application_server', 'nginx_listen_port', 80)
} else {
$nginx_listen_port = zulipconf('application_server', 'nginx_listen_port', 443)
}
$nginx_server_name = zulipconf('application_server', 'nginx_server_name', '')
$ssl_dir = $facts['os']['family'] ? {
'Debian' => '/etc/ssl',
'RedHat' => '/etc/pki/tls',
}
file { '/etc/nginx/sites-available/zulip-enterprise':
ensure => file,
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/nginx/zulip-enterprise.template.erb'),
notify => Service['nginx'],
}
file { '/etc/nginx/sites-enabled/zulip-enterprise':
ensure => link,
require => Package[$zulip::common::nginx],
target => '/etc/nginx/sites-available/zulip-enterprise',
notify => Service['nginx'],
}
# Reload nginx after deploying a new cert.
file { '/etc/letsencrypt/renewal-hooks/deploy/001-nginx.sh':
# This was renumbered
ensure => absent,
}
file { '/etc/letsencrypt/renewal-hooks/deploy/020-symlink.sh':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/letsencrypt/020-symlink.sh',
require => [
Package[certbot],
File['/etc/letsencrypt/renewal-hooks/deploy'],
]
}
file { '/etc/letsencrypt/renewal-hooks/deploy/050-nginx.sh':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/letsencrypt/050-nginx.sh',
}
# Restart the server regularly to avoid potential memory leak problems.
zulip::cron { 'restart-zulip':
hour => '6',
minute => '0',
dow => '0',
command => '/home/zulip/deployments/current/scripts/restart-server --fill-cache --skip-client-reloads',
}
}

View File

@@ -0,0 +1,142 @@
# @summary Included only by classes that can be deployed.
#
# This class should only be included by classes that are intended to
# be able to be deployed on their own host.
class zulip::profile::base {
include zulip::timesync
include zulip::common
case $facts['os']['family'] {
'Debian': {
include zulip::apt_repository
}
'RedHat': {
include zulip::yum_repository
}
default: {
fail('osfamily not supported')
}
}
case $facts['os']['family'] {
'Debian': {
$base_packages = [
# Basics
'python3',
'python3-yaml',
'puppet',
'git',
'curl',
'jq',
'procps',
# Used to read /etc/zulip/zulip.conf for `zulipconf` Puppet function
'crudini',
# Used for tools like sponge
'moreutils',
# Nagios monitoring plugins
$zulip::common::nagios_plugins,
# Required for using HTTPS in apt repositories.
'apt-transport-https',
# Needed for the cron jobs installed by Puppet
'cron',
# Applies log rotation config installed by Puppet
'logrotate',
]
}
'RedHat': {
$base_packages = [
'python3',
'python3-pyyaml',
'puppet',
'git',
'curl',
'jq',
'crudini',
'moreutils',
'nmap-ncat',
'nagios-plugins', # there is no dummy package on CentOS 7
'cronie',
]
}
default: {
fail('osfamily not supported')
}
}
package { $base_packages: ensure => installed }
group { 'zulip':
ensure => present,
}
user { 'zulip':
ensure => present,
require => Group['zulip'],
gid => 'zulip',
shell => '/bin/bash',
home => '/home/zulip',
managehome => true,
}
file { '/etc/zulip':
ensure => directory,
mode => '0755',
owner => 'zulip',
group => 'zulip',
links => follow,
}
file { ['/etc/zulip/zulip.conf', '/etc/zulip/settings.py']:
ensure => present,
require => File['/etc/zulip'],
mode => '0644',
owner => 'zulip',
group => 'zulip',
}
file { '/etc/zulip/zulip-secrets.conf':
ensure => present,
require => File['/etc/zulip'],
mode => '0640',
owner => 'zulip',
group => 'zulip',
}
file { '/etc/security/limits.d/zulip.conf':
ensure => file,
mode => '0640',
owner => 'root',
group => 'root',
source => 'puppet:///modules/zulip/limits.d/zulip.conf',
}
file { '/etc/systemd/system.conf.d/':
ensure => directory,
mode => '0755',
owner => 'root',
group => 'root',
}
file { '/etc/systemd/system.conf.d/limits.conf':
ensure => file,
mode => '0644',
owner => 'root',
group => 'root',
source => 'puppet:///modules/zulip/systemd/system.conf.d/limits.conf',
}
service { 'puppet':
ensure => stopped,
enable => false,
require => Package['puppet'],
}
# This directory is written to by cron jobs for reading by Nagios
file { '/var/lib/nagios_state/':
ensure => directory,
group => 'zulip',
mode => '0775',
}
file { '/var/log/zulip':
ensure => directory,
owner => 'zulip',
group => 'zulip',
mode => '0750',
}
zulip::nagios_plugins { 'zulip_base': }
}

View File

@@ -0,0 +1,29 @@
# This class includes all the modules you need to install/run a Zulip installation
# in a single container (without the database, memcached, Redis services).
# The database, memcached, Redis services need to be run in separate containers.
# Through this split of services, it is easier to scale the services to the needs.
class zulip::profile::docker {
include zulip::profile::base
include zulip::profile::app_frontend
include zulip::localhost_camo
include zulip::local_mailserver
include zulip::supervisor
include zulip::process_fts_updates
file { "${zulip::common::supervisor_conf_dir}/cron.conf":
ensure => file,
require => Package[supervisor],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/supervisor/conf.d/cron.conf',
}
file { "${zulip::common::supervisor_conf_dir}/nginx.conf":
ensure => file,
require => Package[supervisor],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/supervisor/conf.d/nginx.conf',
}
}

View File

@@ -0,0 +1,97 @@
class zulip::profile::memcached {
include zulip::profile::base
include zulip::sasl_modules
case $facts['os']['family'] {
'Debian': {
$memcached_packages = [ 'memcached', 'sasl2-bin' ]
$memcached_user = 'memcache'
}
'RedHat': {
$memcached_packages = [ 'memcached', 'cyrus-sasl' ]
$memcached_user = 'memcached'
}
default: {
fail('osfamily not supported')
}
}
package { $memcached_packages: ensure => installed }
$memcached_max_item_size = zulipconf('memcached', 'max_item_size', '1m')
$memcached_size_reporting = zulipconf('memcached', 'size_reporting', false)
$memcached_memory = zulipconf('memcached', 'memory', $zulip::common::total_memory_mb / 8)
file { '/etc/sasl2':
ensure => directory,
}
file { '/etc/sasl2/memcached-zulip-password':
# We cache the password in this file so we can check whether it
# changed and avoid running saslpasswd2 if it didn't.
require => File['/etc/sasl2'],
owner => 'root',
group => 'root',
mode => '0600',
content => zulipsecret('secrets', 'memcached_password', ''),
notify => Exec[generate_memcached_sasldb2],
}
file { '/var/lib/zulip/memcached-sasldb2.stamp':
owner => 'root',
group => 'root',
mode => '0644',
content => '1',
notify => Exec[generate_memcached_sasldb2],
}
exec { 'generate_memcached_sasldb2':
require => [
Package[$memcached_packages],
Package[$zulip::sasl_modules::sasl_module_packages],
],
refreshonly => true,
# Use localhost for the currently recommended MEMCACHED_USERNAME =
# "zulip@localhost" and the hostname for compatibility with
# MEMCACHED_USERNAME = "zulip".
command => "bash -euc '
rm -f /etc/sasl2/memcached-sasldb2
saslpasswd2 -p -f /etc/sasl2/memcached-sasldb2 \
-a memcached -u localhost zulip < /etc/sasl2/memcached-zulip-password
saslpasswd2 -p -f /etc/sasl2/memcached-sasldb2 \
-a memcached -u \"\$HOSTNAME\" zulip < /etc/sasl2/memcached-zulip-password
'",
}
file { '/etc/sasl2/memcached-sasldb2':
require => Exec[generate_memcached_sasldb2],
owner => $memcached_user,
group => $memcached_user,
mode => '0600',
}
file { '/etc/sasl2/memcached.conf':
require => File['/etc/sasl2'],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/sasl2/memcached.conf',
notify => Service[memcached],
}
file { '/etc/memcached.conf':
ensure => file,
require => [
Package[$memcached_packages],
Package[$zulip::sasl_modules::sasl_module_packages]
],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/memcached.conf.template.erb'),
}
file { '/run/memcached':
ensure => directory,
owner => 'memcache',
group => 'memcache',
mode => '0755',
require => Package[$memcached_packages],
}
service { 'memcached':
ensure => running,
subscribe => File['/etc/memcached.conf'],
require => File['/run/memcached'],
}
}

View File

@@ -0,0 +1,116 @@
# @summary Extends postgresql_base by tuning the configuration.
class zulip::profile::postgresql(Boolean $start = true) {
include zulip::profile::base
include zulip::postgresql_base
include zulip::systemd_daemon_reload
$version = $zulip::postgresql_common::version
if defined(Class['zulip::app_frontend_base']) {
$total_postgres_memory_mb = zulipconf('postgresql', 'memory', $zulip::common::total_memory_mb / 2)
} else {
$total_postgres_memory_mb = zulipconf('postgresql', 'memory', $zulip::common::total_memory_mb)
}
$work_mem = zulipconf('postgresql', 'work_mem', sprintf('%dMB', $total_postgres_memory_mb / 256))
$shared_buffers = zulipconf('postgresql', 'shared_buffers', sprintf('%dMB', $total_postgres_memory_mb / 4))
$effective_cache_size = zulipconf('postgresql', 'effective_cache_size', sprintf('%dMB', $total_postgres_memory_mb * 3 / 4))
$maintenance_work_mem = zulipconf('postgresql', 'maintenance_work_mem', sprintf('%dMB', min(2048, $total_postgres_memory_mb / 8)))
$max_worker_processes = zulipconf('postgresql', 'max_worker_processes', undef)
$max_parallel_workers_per_gather = zulipconf('postgresql', 'max_parallel_workers_per_gather', undef)
$max_parallel_workers = zulipconf('postgresql', 'max_parallel_workers', undef)
$max_parallel_maintenance_workers = zulipconf('postgresql', 'max_parallel_maintenance_workers', undef)
$wal_buffers = zulipconf('postgresql', 'wal_buffers', undef)
$min_wal_size = zulipconf('postgresql', 'min_wal_size', undef)
$max_wal_size = zulipconf('postgresql', 'max_wal_size', undef)
$random_page_cost = zulipconf('postgresql', 'random_page_cost', '1.1')
$effective_io_concurrency = zulipconf('postgresql', 'effective_io_concurrency', undef)
$listen_addresses = zulipconf('postgresql', 'listen_addresses', undef)
$s3_backups_bucket = zulipsecret('secrets', 's3_backups_bucket', '')
$replication_primary = zulipconf('postgresql', 'replication_primary', undef)
$replication_user = zulipconf('postgresql', 'replication_user', undef)
$replication_password = zulipsecret('secrets', 'postgresql_replication_password', '')
$ssl_cert_file = zulipconf('postgresql', 'ssl_cert_file', undef)
$ssl_key_file = zulipconf('postgresql', 'ssl_key_file', undef)
$ssl_ca_file = zulipconf('postgresql', 'ssl_ca_file', undef)
$ssl_mode = zulipconf('postgresql', 'ssl_mode', undef)
file { $zulip::postgresql_base::postgresql_confdirs:
ensure => directory,
owner => 'postgres',
group => 'postgres',
}
if $version in ['14'] {
$postgresql_conf_file = "${zulip::postgresql_base::postgresql_confdir}/postgresql.conf"
file { $postgresql_conf_file:
ensure => file,
require => Package[$zulip::postgresql_base::postgresql],
owner => 'postgres',
group => 'postgres',
mode => '0644',
content => template("zulip/postgresql/${version}/postgresql.conf.template.erb"),
}
} elsif $version in ['15', '16', '17', '18'] {
$postgresql_conf_file = "${zulip::postgresql_base::postgresql_confdir}/conf.d/zulip.conf"
file { $postgresql_conf_file:
ensure => file,
require => Package[$zulip::postgresql_base::postgresql],
owner => 'postgres',
group => 'postgres',
mode => '0644',
content => template('zulip/postgresql/zulip.conf.template.erb'),
}
} else {
fail("PostgreSQL ${version} not supported")
}
# PostgreSQL 18's io_method = io_uring requires a larger
# RLIMIT_MEMLOCK than systemd's default on Linux 6.14 and newer
if $facts['os']['family'] == 'Debian' {
file { '/etc/systemd/system/postgresql@.service.d':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
}
file { '/etc/systemd/system/postgresql@.service.d/zulip.conf':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/postgresql/postgresql@.service.d/zulip.conf',
notify => [Exec['reload systemd'], Service['postgresql']],
}
}
if $replication_primary != undef and $replication_user != undef {
# The presence of a standby.signal file triggers replication
file { "${zulip::postgresql_base::postgresql_datadir}/standby.signal":
ensure => file,
require => Package[$zulip::postgresql_base::postgresql],
before => Service['postgresql'],
owner => 'postgres',
group => 'postgres',
mode => '0644',
content => '',
}
}
$backups_s3_bucket = zulipsecret('secrets', 's3_backups_bucket', '')
$backups_directory = zulipconf('postgresql', 'backups_directory', '')
if $backups_s3_bucket != '' or $backups_directory != '' {
$require = [File['/usr/local/bin/env-wal-g'], Package[$zulip::postgresql_base::postgresql]]
} else {
$require = [Package[$zulip::postgresql_base::postgresql]]
}
service { 'postgresql':
ensure => $start,
require => $require + [Exec['reload systemd']],
subscribe => [ File[$postgresql_conf_file] ],
}
}

View File

@@ -0,0 +1,95 @@
class zulip::profile::rabbitmq {
include zulip::profile::base
$erlang = $facts['os']['family'] ? {
'Debian' => 'erlang-base',
'RedHat' => 'erlang',
}
$rabbitmq_packages = [
$erlang,
'rabbitmq-server',
]
group { 'rabbitmq':
ensure => present,
system => true,
}
user { 'rabbitmq':
ensure => present,
comment => 'RabbitMQ messaging server',
gid => 'rabbitmq',
home => '/var/lib/rabbitmq',
shell => '/usr/sbin/nologin',
system => true,
require => Group['rabbitmq'],
}
file { '/etc/rabbitmq':
ensure => directory,
owner => 'rabbitmq',
group => 'rabbitmq',
mode => '0755',
require => User['rabbitmq'],
before => Package['rabbitmq-server'],
}
file { '/etc/rabbitmq/rabbitmq.config':
ensure => file,
owner => 'rabbitmq',
group => 'rabbitmq',
mode => '0644',
source => 'puppet:///modules/zulip/rabbitmq/rabbitmq.config',
# This config file must be installed before the package, so that
# port 25672 is not even briefly open to the Internet world, which
# would be a security risk, due to insecure defaults in the
# RabbitMQ package.
before => Package['rabbitmq-server'],
notify => Service['rabbitmq-server'],
}
exec { 'warn-rabbitmq-nodename-change':
command => "${facts['zulip_scripts_path']}/lib/warn-rabbitmq-nodename-change",
onlyif => '[ -f /etc/rabbitmq/rabbitmq-env.conf ] && ! grep -xq NODENAME=zulip@localhost /etc/rabbitmq/rabbitmq-env.conf',
before => [
File['/etc/rabbitmq/rabbitmq-env.conf'],
Service['rabbitmq-server'],
],
logoutput => true,
loglevel => warning,
}
file { '/etc/rabbitmq/rabbitmq-env.conf':
ensure => file,
owner => 'rabbitmq',
group => 'rabbitmq',
mode => '0644',
source => 'puppet:///modules/zulip/rabbitmq/rabbitmq-env.conf',
before => Package['rabbitmq-server'],
notify => [
Service['rabbitmq-server'],
Exec['configure-rabbitmq'],
],
}
package { $rabbitmq_packages:
ensure => installed,
}
# epmd doesn't have an init script, so we just check if it is
# running, and if it isn't, start it. Even in case of a race, this
# won't leak epmd processes, because epmd checks if one is already
# running and exits if so.
exec { 'epmd':
command => 'epmd -daemon',
unless => 'which pgrep && pgrep -x epmd >/dev/null',
require => Package[$erlang],
path => '/usr/bin/:/bin/',
}
service { 'rabbitmq-server':
ensure => running,
require => [
Exec['epmd'],
Package['rabbitmq-server'],
],
}
exec { 'configure-rabbitmq':
command => "${facts['zulip_scripts_path']}/setup/configure-rabbitmq",
refreshonly => true,
require => Service['rabbitmq-server'],
}
}

View File

@@ -0,0 +1,71 @@
class zulip::profile::redis {
include zulip::profile::base
case $facts['os']['family'] {
'Debian': {
$redis = 'redis-server'
$redis_dir = '/etc/redis'
}
'RedHat': {
$redis = 'redis'
$redis_dir = '/etc'
}
default: {
fail('osfamily not supported')
}
}
$redis_packages = [ # The server itself
$redis,
]
package { $redis_packages: ensure => installed }
$file = "${redis_dir}/redis.conf"
$zulip_redisconf = "${redis_dir}/zulip-redis.conf"
$line = "include ${zulip_redisconf}"
exec { 'update-redis-conf':
unless => "/bin/grep -Fxqe '${line}' '${file}'",
path => '/bin',
command => "bash -c \"(/bin/echo; /bin/echo '# Include Zulip-specific configuration'; /bin/echo '${line}') >> '${file}'\"",
require => [
Package[$redis],
File[$zulip_redisconf],
],
}
$redis_password = zulipsecret('secrets', 'redis_password', '')
file { $zulip_redisconf:
ensure => file,
require => Package[$redis],
owner => 'redis',
group => 'redis',
mode => '0640',
content => template('zulip/zulip-redis.template.erb'),
}
# https://redis.io/docs/management/admin/#linux
zulip::sysctl { 'redis-server':
key => 'vm.overcommit_memory',
value => '1',
}
package { 'sysfsutils': }
file { '/etc/sysfs.d/40-disable-transpatent-hugepages.conf':
require => Package['sysfsutils'],
notify => Service['sysfsutils'],
content => 'kernel/mm/transparent_hugepage/enabled = never',
}
service { 'sysfsutils':
ensure => running,
require => Package['sysfsutils'],
}
service { $redis:
ensure => running,
require => [
Service['sysfsutils'],
Package['redis-server'],
],
subscribe => [
File[$zulip_redisconf],
Exec['update-redis-conf'],
],
}
}

View File

@@ -0,0 +1,6 @@
# @summary Outgoing HTTP CONNECT proxy for HTTP/HTTPS on port 4750.
#
class zulip::profile::smokescreen {
include zulip::profile::base
include zulip::smokescreen
}

View File

@@ -0,0 +1,21 @@
# @summary Complete Zulip server on one server
#
# This class includes all the modules you need to run an entire Zulip
# installation on a single server. If desired, you can split up the
# different `zulip::profile::*` components of a Zulip installation
# onto different servers:
#
# - zulip::profile::app_frontend
# - zulip::profile::memcached
# - zulip::profile::postgresql
# - zulip::profile::rabbitmq
# - zulip::profile::redis
# - zulip::profile::smokescreen
#
# See the corresponding configuration in /etc/zulip/settings.py for
# how to find the various services is also required to make this work.
class zulip::profile::standalone {
include zulip::profile::standalone_nodb
include zulip::profile::postgresql
}

View File

@@ -0,0 +1,16 @@
# @summary Complete Zulip server, except the database server.
#
# This includes all of the parts necessary to run an entire Zulip
# installation on a single server, except the database. It is assumed
# that the PostgreSQL database is either hosted on another server with
# the `zulip::profile::postgresql` class applied, or a cloud-managed
# database is used (e.g. AWS RDS).
#
# @see https://zulip.readthedocs.io/en/latest/production/postgresql.html
class zulip::profile::standalone_nodb {
include zulip::profile::app_frontend
include zulip::profile::memcached
include zulip::profile::rabbitmq
include zulip::profile::redis
include zulip::localhost_camo
}