Adds zulip app.

This commit is contained in:
2026-06-19 21:33:33 +00:00
parent cb5f3d66ff
commit 4d4b48cf72
16 changed files with 501 additions and 0 deletions

5
zulip/app.yaml Normal file
View File

@@ -0,0 +1,5 @@
name: zulip
is: zulip
description: Zulip is an open-source team chat application with threaded conversations, making it easy to catch up on important discussions without information overload.
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/zulip.svg
latest: "9"

View File

@@ -0,0 +1,42 @@
# Zulip
Zulip is an open-source team messaging platform with a unique topic-based threading model.
## Dependencies
- **SMTP** - For sending invitations and notifications
- **Redis** - Used for caching and real-time event queuing
Zulip bundles its own **PostgreSQL**, **RabbitMQ**, and **Memcached** instances — these are deployed alongside the main Zulip pod and are not shared with other apps.
## Configuration
Key settings in `config.yaml`:
- **domain** - Where Zulip will be accessible
- **adminEmail** - Admin account email (defaults to your operator email)
- **smtp** - Email settings inherited from your Wild Cloud SMTP service
## First-Time Setup
1. Add and deploy the app:
```bash
wild app add zulip
wild app deploy zulip
```
2. **First start takes 510 minutes** as Zulip runs hundreds of Django database migrations. Wait until the pod shows `1/1 Running`.
3. The admin account is pre-configured with the `adminEmail` and `adminPassword` from `secrets.yaml`. Visit the app URL and log in.
4. Complete the realm (organization) setup wizard on first login.
## Inviting Users
Send invitation emails from **Settings → Organization → Invitations**, or use pre-generated invite links.
## Notes
- The `secretKey`, `postgresPassword`, and `rabbitmqPassword` are all auto-generated in `secrets.yaml`
- Zulip's topic-based threading keeps conversations organized — each stream (channel) has multiple topics
- Mobile and desktop apps are available and connect to self-hosted instances

View File

@@ -0,0 +1,46 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zulip-memcached
namespace: {{ .namespace }}
spec:
replicas: 1
selector:
matchLabels:
component: memcached
template:
metadata:
labels:
component: memcached
spec:
securityContext:
runAsNonRoot: true
runAsUser: 11211
seccompProfile:
type: RuntimeDefault
containers:
- name: memcached
image: memcached:1.6.32-alpine
args:
- -m
- "64m"
- -c
- "1024"
- -p
- "11211"
ports:
- containerPort: 11211
name: memcached
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: "200m"
memory: 128Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true

View File

@@ -0,0 +1,56 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zulip-postgres
namespace: {{ .namespace }}
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
component: postgres
template:
metadata:
labels:
component: postgres
spec:
securityContext:
runAsNonRoot: false
runAsUser: 0
seccompProfile:
type: RuntimeDefault
containers:
- name: postgres
image: zulip/zulip-postgresql:14
ports:
- containerPort: 5432
name: postgres
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
- name: POSTGRES_DB
value: "{{ .db.name }}"
- name: POSTGRES_USER
value: "{{ .db.user }}"
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: zulip-secrets
key: postgresPassword
resources:
requests:
cpu: 50m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
securityContext:
readOnlyRootFilesystem: false
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: zulip-postgres-data

View File

@@ -0,0 +1,50 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zulip-rabbitmq
namespace: {{ .namespace }}
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
component: rabbitmq
template:
metadata:
labels:
component: rabbitmq
spec:
securityContext:
runAsNonRoot: true
runAsUser: 999
runAsGroup: 999
seccompProfile:
type: RuntimeDefault
containers:
- name: rabbitmq
image: rabbitmq:3.13-alpine
ports:
- containerPort: 5672
name: amqp
env:
- name: RABBITMQ_DEFAULT_USER
value: "{{ .rabbitmq.user }}"
- name: RABBITMQ_DEFAULT_PASS
valueFrom:
secretKeyRef:
name: zulip-secrets
key: rabbitmqPassword
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: "1"
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false

View File

@@ -0,0 +1,142 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zulip
namespace: {{ .namespace }}
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
component: web
template:
metadata:
labels:
component: web
spec:
securityContext:
runAsNonRoot: false
runAsUser: 0
seccompProfile:
type: RuntimeDefault
containers:
- name: zulip
image: zulip/docker-zulip:9.4-0
ports:
- name: http
containerPort: 80
protocol: TCP
env:
- name: SETTING_ZULIP_ADMINISTRATOR
value: "{{ .adminEmail }}"
- name: ZULIP_PASSWORD
valueFrom:
secretKeyRef:
name: zulip-secrets
key: adminPassword
- name: SETTING_EXTERNAL_HOST
value: "{{ .domain }}"
- name: SSL_CERTIFICATE_GENERATION
value: "self-signed"
- name: SECRETS_secret_key
valueFrom:
secretKeyRef:
name: zulip-secrets
key: secretKey
# Database
- name: DB_HOST
value: "{{ .db.host }}"
- name: DB_HOST_PORT
value: "{{ .db.port }}"
- name: DB_NAME
value: "{{ .db.name }}"
- name: DB_USER
value: "{{ .db.user }}"
- name: SECRETS_postgres_password
valueFrom:
secretKeyRef:
name: zulip-secrets
key: postgresPassword
# RabbitMQ
- name: SETTING_RABBITMQ_HOST
value: "{{ .rabbitmq.host }}"
- name: SETTING_RABBITMQ_USER
value: "{{ .rabbitmq.user }}"
- name: SECRETS_rabbitmq_password
valueFrom:
secretKeyRef:
name: zulip-secrets
key: rabbitmqPassword
# Redis
- name: SETTING_REDIS_HOST
value: "{{ .redis.host }}"
- name: SECRETS_redis_password
valueFrom:
secretKeyRef:
name: zulip-secrets
key: redis.password
# Memcached
- name: SETTING_MEMCACHED_LOCATION
value: "{{ .memcached.host }}:11211"
# Email
- name: SETTING_EMAIL_HOST
value: "{{ .smtp.host }}"
- name: SETTING_EMAIL_HOST_USER
value: "{{ .smtp.user }}"
- name: SETTING_EMAIL_PORT
value: "{{ .smtp.port }}"
- name: SETTING_EMAIL_USE_TLS
value: "True"
- name: SETTING_EMAIL_BACKEND
value: "django.core.mail.backends.smtp.EmailBackend"
- name: SETTING_DEFAULT_FROM_EMAIL
value: "{{ .smtp.from }}"
- name: SECRETS_email_password
valueFrom:
secretKeyRef:
name: zulip-secrets
key: smtp.password
resources:
limits:
cpu: "2"
ephemeral-storage: 1Gi
memory: 3Gi
requests:
cpu: 50m
ephemeral-storage: 100Mi
memory: 512Mi
volumeMounts:
- name: zulip-data
mountPath: /data
livenessProbe:
httpGet:
path: /accounts/home/
port: 443
scheme: HTTPS
httpHeaders:
- name: Host
value: "{{ .domain }}"
initialDelaySeconds: 180
timeoutSeconds: 30
periodSeconds: 30
failureThreshold: 6
readinessProbe:
httpGet:
path: /accounts/home/
port: 443
scheme: HTTPS
httpHeaders:
- name: Host
value: "{{ .domain }}"
initialDelaySeconds: 120
timeoutSeconds: 30
periodSeconds: 15
failureThreshold: 6
securityContext:
readOnlyRootFilesystem: false
volumes:
- name: zulip-data
persistentVolumeClaim:
claimName: zulip-data
restartPolicy: Always

View File

@@ -0,0 +1,26 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: zulip
namespace: {{ .namespace }}
annotations:
external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }}
external-dns.alpha.kubernetes.io/cloudflare-proxied: "false"
external-dns.alpha.kubernetes.io/ttl: "60"
spec:
ingressClassName: traefik
rules:
- host: {{ .domain }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: zulip
port:
number: 80
tls:
- hosts:
- {{ .domain }}
secretName: {{ .tlsSecretName }}

View File

@@ -0,0 +1,22 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: {{ .namespace }}
labels:
- includeSelectors: true
pairs:
app: zulip
managedBy: kustomize
partOf: wild-cloud
resources:
- namespace.yaml
- deployment.yaml
- service.yaml
- ingress.yaml
- pvc.yaml
- deployment-postgres.yaml
- service-postgres.yaml
- pvc-postgres.yaml
- deployment-rabbitmq.yaml
- service-rabbitmq.yaml
- deployment-memcached.yaml
- service-memcached.yaml

View File

@@ -0,0 +1,37 @@
version: 9.4-3
requires:
- name: smtp
- name: redis
defaultConfig:
namespace: zulip
domain: "zulip.{{ .cloud.domain }}"
externalDnsDomain: "{{ .cloud.domain }}"
tlsSecretName: wildcard-wild-cloud-tls
storage: 2Gi
adminEmail: "{{ .operator.email }}"
db:
host: zulip-postgres.zulip.svc.cluster.local
port: "5432"
name: zulip
user: zulip
storage: 2Gi
rabbitmq:
host: zulip-rabbitmq.zulip.svc.cluster.local
user: zulip
redis:
host: "{{ .apps.redis.host }}"
memcached:
host: zulip-memcached.zulip.svc.cluster.local
smtp:
host: "{{ .apps.smtp.host }}"
port: "{{ .apps.smtp.port }}"
user: "{{ .apps.smtp.user }}"
from: "{{ .apps.smtp.from }}"
defaultSecrets:
- key: adminPassword
- key: secretKey
- key: postgresPassword
- key: rabbitmqPassword
requiredSecrets:
- smtp.password
- redis.password

View File

@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: {{ .namespace }}

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: zulip-postgres-data
namespace: {{ .namespace }}
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .db.storage }}

11
zulip/versions/9/pvc.yaml Normal file
View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: zulip-data
namespace: {{ .namespace }}
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .storage }}

View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: zulip-memcached
namespace: {{ .namespace }}
spec:
selector:
component: memcached
ports:
- port: 11211
targetPort: 11211
name: memcached

View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: zulip-postgres
namespace: {{ .namespace }}
spec:
selector:
component: postgres
ports:
- port: 5432
targetPort: 5432
name: postgres

View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: zulip-rabbitmq
namespace: {{ .namespace }}
spec:
selector:
component: rabbitmq
ports:
- port: 5672
targetPort: 5672
name: amqp

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: zulip
namespace: {{ .namespace }}
spec:
selector:
component: web
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP