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
319 lines
11 KiB
Puppet
319 lines
11 KiB
Puppet
# Minimal configuration to run a Zulip application server.
|
|
# Default nginx configuration is included in extension app_frontend.pp.
|
|
class zulip::app_frontend_base {
|
|
include zulip::nginx
|
|
include zulip::tusd
|
|
include zulip::sasl_modules
|
|
include zulip::supervisor
|
|
include zulip::tornado_sharding
|
|
include zulip::hooks::base
|
|
|
|
if $facts['os']['family'] == 'Debian' {
|
|
# Upgrade and other tooling wants to be able to get a database
|
|
# shell. This is not necessary on CentOS because the PostgreSQL
|
|
# package already includes the client.
|
|
include zulip::postgresql_client
|
|
}
|
|
zulip::safepackage {
|
|
[
|
|
# For `manage.py compilemessages` when upgrading from Git.
|
|
'gettext',
|
|
# For Slack import.
|
|
'unzip',
|
|
# Ensures `/etc/ldap/ldap.conf` exists; the default
|
|
# `TLS_CACERTDIR` specified there is necessary for LDAP
|
|
# authentication to work. This package is "Recommended" by
|
|
# `libldap` where is required by postgres, so has been on most
|
|
# Zulip servers by default; adding it here explicitly ensures it
|
|
# is available on those that don't include the database server.
|
|
'libldap-common',
|
|
]:
|
|
ensure => installed,
|
|
}
|
|
|
|
file { '/etc/nginx/zulip-include/app':
|
|
require => Package[$zulip::common::nginx],
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
source => 'puppet:///modules/zulip/nginx/zulip-include-frontend/app',
|
|
notify => Service['nginx'],
|
|
}
|
|
file { '/etc/nginx/zulip-include/uploads.types':
|
|
require => Package[$zulip::common::nginx],
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
source => 'puppet:///modules/zulip/nginx/zulip-include-frontend/uploads.types',
|
|
notify => Service['nginx'],
|
|
}
|
|
file { '/etc/nginx/zulip-include/app.d/':
|
|
ensure => directory,
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0755',
|
|
}
|
|
file { '/etc/nginx/zulip-include/localhost.d/':
|
|
ensure => directory,
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0755',
|
|
}
|
|
|
|
$loadbalancers = split(zulipconf('loadbalancer', 'ips', ''), ',')
|
|
if $loadbalancers != [] {
|
|
file { '/etc/nginx/zulip-include/app.d/keepalive-loadbalancer.conf':
|
|
require => File['/etc/nginx/zulip-include/app.d'],
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
source => 'puppet:///modules/zulip/nginx/zulip-include-app.d/keepalive-loadbalancer.conf',
|
|
notify => Service['nginx'],
|
|
}
|
|
} else {
|
|
file { '/etc/nginx/zulip-include/app.d/keepalive-loadbalancer.conf':
|
|
ensure => absent,
|
|
notify => Service['nginx'],
|
|
}
|
|
}
|
|
file { '/etc/nginx/zulip-include/app.d/accept-loadbalancer.conf':
|
|
# This moved to /etc/nginx/zulip-include/trusted-ip, via
|
|
# nginx.pp. This block can be removed once direct Zulip upgrades
|
|
# from Zulip 11 are no longer supported.
|
|
ensure => absent,
|
|
notify => Service['nginx'],
|
|
}
|
|
file { '/etc/nginx/zulip-include/app.d/healthcheck.conf':
|
|
require => File['/etc/nginx/zulip-include/app.d'],
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
content => template('zulip/nginx/healthcheck.conf.template.erb'),
|
|
notify => Service['nginx'],
|
|
}
|
|
|
|
file { '/etc/nginx/zulip-include/upstreams':
|
|
require => Package[$zulip::common::nginx],
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
source => 'puppet:///modules/zulip/nginx/zulip-include-frontend/upstreams',
|
|
notify => Service['nginx'],
|
|
}
|
|
|
|
$s3_memory_cache_size = zulipconf('application_server', 's3_memory_cache_size', '1M')
|
|
$s3_disk_cache_size = zulipconf('application_server', 's3_disk_cache_size', '200M')
|
|
$s3_cache_inactive_time = zulipconf('application_server', 's3_cache_inactive_time', '30d')
|
|
$configured_nginx_resolver = zulipconf('application_server', 'nameserver', '')
|
|
if $configured_nginx_resolver == '' {
|
|
# This may fail in the unlikely change that there is no configured
|
|
# resolver in /etc/resolv.conf, so only call it is unset in zulip.conf
|
|
$nginx_resolver_ip = resolver_ip()
|
|
} elsif (':' in $configured_nginx_resolver) and ! ('.' in $configured_nginx_resolver) and ! ('[' in $configured_nginx_resolver) {
|
|
# Assume this is IPv6, which needs square brackets.
|
|
$nginx_resolver_ip = "[${configured_nginx_resolver}]"
|
|
} else {
|
|
$nginx_resolver_ip = $configured_nginx_resolver
|
|
}
|
|
file { '/etc/nginx/zulip-include/s3-cache':
|
|
require => [
|
|
Package[$zulip::common::nginx],
|
|
File['/srv/zulip-uploaded-files-cache'],
|
|
],
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
content => template('zulip/nginx/s3-cache.template.erb'),
|
|
notify => Service['nginx'],
|
|
}
|
|
|
|
file { '/etc/nginx/zulip-include/app.d/uploads-internal.conf':
|
|
ensure => file,
|
|
require => Package[$zulip::common::nginx],
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
notify => Service['nginx'],
|
|
source => 'puppet:///modules/zulip/nginx/zulip-include-frontend/uploads-internal.conf',
|
|
}
|
|
|
|
# This determines whether we run queue processors multithreaded or
|
|
# multiprocess. Multiprocess scales much better, but requires more
|
|
# RAM; we just auto-detect based on available system RAM.
|
|
#
|
|
# Because Zulip can run in the multiprocess mode with 4GB of memory,
|
|
# and it's a common instance size, we aim for that to be the cutoff
|
|
# for this higher-performance mode.
|
|
#
|
|
# We use a cutoff less than 4000 here to detect systems advertised
|
|
# as "4GB"; some may have as little as 4 x 1000^3 / 1024^2 ≈ 3815 MiB
|
|
# of memory.
|
|
$queues_multiprocess_default = $zulip::common::total_memory_mb > 3800
|
|
$queues_multiprocess = zulipconf('application_server', 'queue_workers_multiprocess', $queues_multiprocess_default)
|
|
$base_queues = [
|
|
'deferred_work',
|
|
'digest_emails',
|
|
'email_mirror',
|
|
'embed_links',
|
|
'embedded_bots',
|
|
'email_senders',
|
|
'deferred_email_senders',
|
|
'missedmessage_emails',
|
|
'missedmessage_mobile_notifications',
|
|
'outgoing_webhooks',
|
|
'thumbnail',
|
|
'user_activity',
|
|
'user_activity_interval',
|
|
]
|
|
# Soft reactivations normally share the deferred_work queue; larger
|
|
# servers can opt into a dedicated queue (and worker process) for them.
|
|
$queues = zulipconf('application_server', 'dedicated_soft_reactivation_queue', false) ? {
|
|
true => $base_queues + ['soft_reactivation'],
|
|
default => $base_queues,
|
|
}
|
|
|
|
if $zulip::common::total_memory_mb > 24000 {
|
|
$uwsgi_default_processes = 16
|
|
} elsif $zulip::common::total_memory_mb > 12000 {
|
|
$uwsgi_default_processes = 8
|
|
} elsif $zulip::common::total_memory_mb > 6000 {
|
|
$uwsgi_default_processes = 6
|
|
} elsif $zulip::common::total_memory_mb > 3000 {
|
|
$uwsgi_default_processes = 4
|
|
} else {
|
|
$uwsgi_default_processes = 3
|
|
}
|
|
|
|
# Not the different naming scheme for sharded workers, where each gets its own queue,
|
|
# vs when multiple workers service the same queue.
|
|
$worker_counts = Hash(zulipconf_keys('application_server').filter |$key| {
|
|
$key =~ /_workers$/
|
|
}.map |$key| {
|
|
[regsubst($key, '_workers$', ''), Integer(zulipconf('application_server', $key, 1))]
|
|
})
|
|
$mobile_notification_shards = Integer(zulipconf('application_server', 'mobile_notification_shards', 1))
|
|
$user_activity_shards = Integer(zulipconf('application_server', 'user_activity_shards', 1))
|
|
$tornado_ports = $zulip::tornado_sharding::tornado_ports
|
|
|
|
$proxy_host = zulipconf('http_proxy', 'host', 'localhost')
|
|
$proxy_port = zulipconf('http_proxy', 'port', '4750')
|
|
|
|
if ($proxy_host in ['localhost', '127.0.0.1', '::1']) and ($proxy_port == '4750') {
|
|
include zulip::smokescreen
|
|
}
|
|
|
|
$katex_server = zulipconf('application_server', 'katex_server', true)
|
|
$katex_server_port = zulipconf('application_server', 'katex_server_port', '9700')
|
|
|
|
$tusd_server_listen = zulipconf('application_server', 'tusd_server_listen', '127.0.0.1')
|
|
|
|
# Supervisor sets this as HTTP_proxy/HTTPS_proxy in every process's
|
|
# environment, so that all outgoing requests go through Smokescreen;
|
|
# including those made directly by libraries rather than through
|
|
# OutgoingSession.
|
|
if $proxy_host != '' and $proxy_port != '' {
|
|
$proxy = "http://${proxy_host}:${proxy_port}"
|
|
} else {
|
|
$proxy = ''
|
|
}
|
|
$custom_ca_path = zulipconf('application_server','custom_ca_path', '')
|
|
if $custom_ca_path != '' {
|
|
file { '/usr/local/share/ca-certificates/custom-zulip-ca.crt':
|
|
ensure => file,
|
|
source => $custom_ca_path,
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
notify => Exec['update-ca-certificates'],
|
|
}
|
|
exec { 'update-ca-certificates':
|
|
command => '/usr/sbin/update-ca-certificates',
|
|
require => Package['ca-certificates'],
|
|
before => File["${zulip::common::supervisor_conf_dir}/zulip.conf"],
|
|
refreshonly => true,
|
|
}
|
|
$ca_bundle=',REQUESTS_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt"'
|
|
} else {
|
|
$ca_bundle=''
|
|
}
|
|
file { "${zulip::common::supervisor_conf_dir}/zulip.conf":
|
|
ensure => file,
|
|
require => [Package[supervisor], Exec['stage_updated_sharding']],
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
content => template('zulip/supervisor/zulip.conf.template.erb'),
|
|
notify => Service[$zulip::common::supervisor_service],
|
|
}
|
|
|
|
$uwsgi_rolling_restart = zulipconf('application_server', 'rolling_restart', false)
|
|
$uwsgi_listen_backlog_limit = zulipconf('application_server', 'uwsgi_listen_backlog_limit', 128)
|
|
$uwsgi_processes = zulipconf('application_server', 'uwsgi_processes', $uwsgi_default_processes)
|
|
$somaxconn = 2 * Integer($uwsgi_listen_backlog_limit)
|
|
file { '/etc/zulip/uwsgi.ini':
|
|
ensure => file,
|
|
require => Package[supervisor],
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
content => template('zulip/uwsgi.ini.template.erb'),
|
|
notify => Service[$zulip::common::supervisor_service],
|
|
}
|
|
zulip::sysctl { 'uwsgi':
|
|
comment => 'Allow larger listen backlog',
|
|
key => 'net.core.somaxconn',
|
|
value => $somaxconn,
|
|
}
|
|
|
|
file { [
|
|
'/home/zulip/tornado',
|
|
'/home/zulip/prod-static',
|
|
'/home/zulip/deployments',
|
|
'/srv/zulip-locks',
|
|
'/srv/zulip-emoji-cache',
|
|
'/srv/zulip-uploaded-files-cache',
|
|
]:
|
|
ensure => directory,
|
|
owner => 'zulip',
|
|
group => 'zulip',
|
|
mode => '0755',
|
|
}
|
|
# /home/zulip/uploads is documented as something administrators may
|
|
# replace with a symlink to a different storage location, so we use
|
|
# an exec with a `test -d` guard (which follows symlinks) rather
|
|
# than a file resource that would replace the symlink.
|
|
exec { 'create-uploads-dir':
|
|
command => 'install -d -o zulip -g zulip -m 0755 /home/zulip/uploads',
|
|
unless => 'test -d /home/zulip/uploads',
|
|
path => '/usr/bin:/bin',
|
|
}
|
|
file { [
|
|
'/var/log/zulip/queue_error',
|
|
'/var/log/zulip/queue_stats',
|
|
]:
|
|
ensure => directory,
|
|
owner => 'zulip',
|
|
group => 'zulip',
|
|
mode => '0750',
|
|
}
|
|
$access_log_retention_days = zulipconf('application_server','access_log_retention_days', 14)
|
|
file { '/etc/logrotate.d/zulip':
|
|
ensure => file,
|
|
owner => 'root',
|
|
group => 'root',
|
|
mode => '0644',
|
|
content => template('zulip/logrotate/zulip.template.erb'),
|
|
}
|
|
|
|
zulip::nagios_plugins {'zulip_app_frontend': }
|
|
|
|
# This cron job does nothing unless RATE_LIMIT_TOR_TOGETHER is enabled.
|
|
zulip::cron { 'fetch-tor-exit-nodes':
|
|
minute => '17',
|
|
}
|
|
# This was originally added with a typo in the name.
|
|
file { '/etc/cron.d/fetch-for-exit-nodes':
|
|
ensure => absent,
|
|
}
|
|
}
|