diff --git a/lemmy/configmap.yaml b/lemmy/configmap.yaml new file mode 100644 index 0000000..946a044 --- /dev/null +++ b/lemmy/configmap.yaml @@ -0,0 +1,36 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: lemmy-config + namespace: {{ .namespace }} +data: + lemmy.hjson: | + { + hostname: "{{ .domain }}" + bind: "0.0.0.0" + port: {{ .backendPort }} + tls_enabled: false + + database: { + uri: "postgresql://{{ .dbUser }}:DBPASSWORD@{{ .dbHost }}:{{ .dbPort }}/{{ .dbName }}" + } + + pictrs: { + url: "http://lemmy-pictrs:{{ .pictrsPort }}/" + api_key: "PICTRS_API_KEY" + } + + email: { + smtp_server: "{{ .smtp.host }}:{{ .smtp.port }}" + smtp_login: "{{ .smtp.user }}" + smtp_password: "SMTP_PASSWORD" + smtp_from_address: "{{ .smtp.from }}" + tls_type: "{{ if eq .smtp.tls "true" }}tls{{ else }}none{{ end }}" + } + + setup: { + admin_username: "admin" + admin_password: "ADMIN_PASSWORD" + site_name: "Lemmy" + } + } diff --git a/lemmy/db-init-job.yaml b/lemmy/db-init-job.yaml new file mode 100644 index 0000000..8ec9e54 --- /dev/null +++ b/lemmy/db-init-job.yaml @@ -0,0 +1,76 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: lemmy-db-init + namespace: {{ .namespace }} +spec: + template: + metadata: + labels: + component: db-init + spec: + restartPolicy: OnFailure + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + seccompProfile: + type: RuntimeDefault + containers: + - name: db-init + image: postgres:16-alpine + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + env: + - name: PGHOST + value: "{{ .dbHost }}" + - name: PGPORT + value: "{{ .dbPort }}" + - name: PGUSER + value: postgres + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: lemmy-secrets + key: postgres.password + - name: DB_NAME + value: "{{ .dbName }}" + - name: DB_USER + value: "{{ .dbUser }}" + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: lemmy-secrets + key: dbPassword + command: + - sh + - -c + - | + set -e + echo "Waiting for PostgreSQL to be ready..." + until pg_isready -h $PGHOST -p $PGPORT -U $PGUSER; do + echo "Waiting for database connection..." + sleep 2 + done + + echo "Creating database and user..." + psql -v ON_ERROR_STOP=1 <<-EOSQL + SELECT 'CREATE DATABASE ${DB_NAME}' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${DB_NAME}')\gexec + DO \$\$ + BEGIN + IF NOT EXISTS (SELECT FROM pg_user WHERE usename = '${DB_USER}') THEN + CREATE USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}'; + ELSE + ALTER USER ${DB_USER} WITH PASSWORD '${DB_PASSWORD}'; + END IF; + END + \$\$; + GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${DB_USER}; + \c ${DB_NAME} + GRANT ALL ON SCHEMA public TO ${DB_USER}; + EOSQL + + echo "Database initialization completed successfully" diff --git a/lemmy/deployment-backend.yaml b/lemmy/deployment-backend.yaml new file mode 100644 index 0000000..7aa5c89 --- /dev/null +++ b/lemmy/deployment-backend.yaml @@ -0,0 +1,102 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lemmy-backend + namespace: {{ .namespace }} +spec: + replicas: {{ .backendReplicas }} + selector: + matchLabels: + component: backend + template: + metadata: + labels: + component: backend + spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + initContainers: + - name: config-prep + image: busybox:stable + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: true + command: + - sh + - -c + - | + cp /config-template/lemmy.hjson /config/lemmy.hjson + sed -i "s|DBPASSWORD|${DB_PASSWORD}|g" /config/lemmy.hjson + sed -i "s|PICTRS_API_KEY|${PICTRS_API_KEY}|g" /config/lemmy.hjson + sed -i "s|SMTP_PASSWORD|${SMTP_PASSWORD}|g" /config/lemmy.hjson + sed -i "s|ADMIN_PASSWORD|${ADMIN_PASSWORD}|g" /config/lemmy.hjson + env: + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: lemmy-secrets + key: dbPassword + - name: PICTRS_API_KEY + valueFrom: + secretKeyRef: + name: lemmy-secrets + key: jwtSecret + - name: SMTP_PASSWORD + valueFrom: + secretKeyRef: + name: lemmy-secrets + key: smtpPassword + - name: ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: lemmy-secrets + key: adminPassword + volumeMounts: + - name: config-template + mountPath: /config-template + - name: config + mountPath: /config + containers: + - name: backend + image: {{ .backendImage }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + env: + - name: LEMMY_CONFIG_LOCATION + value: /config/lemmy.hjson + - name: TZ + value: "{{ .timezone }}" + ports: + - containerPort: {{ .backendPort }} + name: http + volumeMounts: + - name: config + mountPath: /config + livenessProbe: + httpGet: + path: /api/v3/site + port: {{ .backendPort }} + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /api/v3/site + port: {{ .backendPort }} + initialDelaySeconds: 10 + periodSeconds: 5 + volumes: + - name: config-template + configMap: + name: lemmy-config + - name: config + emptyDir: {} diff --git a/lemmy/deployment-pictrs.yaml b/lemmy/deployment-pictrs.yaml new file mode 100644 index 0000000..5d21e75 --- /dev/null +++ b/lemmy/deployment-pictrs.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lemmy-pictrs + namespace: {{ .namespace }} +spec: + replicas: {{ .pictrsReplicas }} + selector: + matchLabels: + component: pictrs + template: + metadata: + labels: + component: pictrs + spec: + securityContext: + runAsNonRoot: true + runAsUser: 991 + runAsGroup: 991 + fsGroup: 991 + seccompProfile: + type: RuntimeDefault + containers: + - name: pictrs + image: {{ .pictrsImage }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + env: + - name: PICTRS__SERVER__BIND + value: "0.0.0.0:{{ .pictrsPort }}" + - name: PICTRS__MEDIA__VIDEO_CODEC + value: vp9 + - name: PICTRS__MEDIA__GIF__MAX_WIDTH + value: "256" + - name: PICTRS__MEDIA__GIF__MAX_HEIGHT + value: "256" + - name: PICTRS__MEDIA__GIF__MAX_AREA + value: "65536" + - name: PICTRS__MEDIA__GIF__MAX_FRAME_COUNT + value: "400" + - name: RUST_LOG + value: debug + - name: RUST_BACKTRACE + value: full + - name: PICTRS__REPO__TYPE + value: sled + - name: PICTRS__REPO__PATH + value: /mnt/sled-repo + - name: PICTRS__STORE__TYPE + value: filesystem + - name: PICTRS__STORE__PATH + value: /mnt/files + ports: + - containerPort: {{ .pictrsPort }} + name: http + volumeMounts: + - name: storage + mountPath: /mnt + livenessProbe: + httpGet: + path: /healthz + port: {{ .pictrsPort }} + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /healthz + port: {{ .pictrsPort }} + initialDelaySeconds: 10 + periodSeconds: 5 + volumes: + - name: storage + persistentVolumeClaim: + claimName: lemmy-pictrs-storage diff --git a/lemmy/deployment-ui.yaml b/lemmy/deployment-ui.yaml new file mode 100644 index 0000000..25cd41e --- /dev/null +++ b/lemmy/deployment-ui.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: lemmy-ui + namespace: {{ .namespace }} +spec: + replicas: {{ .uiReplicas }} + selector: + matchLabels: + component: ui + template: + metadata: + labels: + component: ui + spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + seccompProfile: + type: RuntimeDefault + containers: + - name: ui + image: {{ .uiImage }} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: [ALL] + readOnlyRootFilesystem: false + env: + - name: LEMMY_UI_LEMMY_INTERNAL_HOST + value: "lemmy-backend:{{ .backendPort }}" + - name: LEMMY_UI_LEMMY_EXTERNAL_HOST + value: "{{ .domain }}" + - name: LEMMY_UI_HTTPS + value: "true" + ports: + - containerPort: {{ .uiPort }} + name: http + livenessProbe: + httpGet: + path: / + port: {{ .uiPort }} + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: {{ .uiPort }} + initialDelaySeconds: 10 + periodSeconds: 5 diff --git a/lemmy/ingress.yaml b/lemmy/ingress.yaml new file mode 100644 index 0000000..b9be5d7 --- /dev/null +++ b/lemmy/ingress.yaml @@ -0,0 +1,42 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: lemmy-ingress + namespace: {{ .namespace }} + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }} + external-dns.alpha.kubernetes.io/cloudflare-proxied: "false" +spec: + ingressClassName: traefik + tls: + - hosts: + - {{ .domain }} + secretName: {{ .tlsSecretName }} + rules: + - host: {{ .domain }} + http: + paths: + - path: /api + pathType: Prefix + backend: + service: + name: lemmy-backend + port: + number: {{ .backendPort }} + - path: /pictrs + pathType: Prefix + backend: + service: + name: lemmy-pictrs + port: + number: {{ .pictrsPort }} + - path: / + pathType: Prefix + backend: + service: + name: lemmy-ui + port: + number: {{ .uiPort }} diff --git a/lemmy/kustomization.yaml b/lemmy/kustomization.yaml new file mode 100644 index 0000000..d8c7d12 --- /dev/null +++ b/lemmy/kustomization.yaml @@ -0,0 +1,21 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: lemmy +labels: + - includeSelectors: true + pairs: + app: lemmy + managedBy: kustomize + partOf: wild-cloud +resources: + - namespace.yaml + - configmap.yaml + - pvc-pictrs.yaml + - db-init-job.yaml + - deployment-pictrs.yaml + - service-pictrs.yaml + - deployment-backend.yaml + - service-backend.yaml + - deployment-ui.yaml + - service-ui.yaml + - ingress.yaml diff --git a/lemmy/manifest.yaml b/lemmy/manifest.yaml new file mode 100644 index 0000000..f429429 --- /dev/null +++ b/lemmy/manifest.yaml @@ -0,0 +1,41 @@ +name: lemmy +is: lemmy +description: Lemmy is a selfhosted social link aggregation and discussion platform. It is an open source alternative to Reddit, designed for the fediverse. +version: 0.19.15 +icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/lemmy.svg +requires: + - name: postgres +defaultConfig: + namespace: lemmy + backendImage: dessalines/lemmy:0.19.15 + uiImage: dessalines/lemmy-ui:0.19.15 + pictrsImage: asonix/pictrs:0.5.5 + backendPort: 8536 + uiPort: 1234 + pictrsPort: 8080 + backendReplicas: 1 + uiReplicas: 1 + pictrsReplicas: 1 + storage: 10Gi + pictrsStorage: 50Gi + timezone: UTC + domain: lemmy.{{ .cloud.domain }} + externalDnsDomain: lemmy.{{ .cloud.baseDomain }} + tlsSecretName: lemmy-tls + dbName: lemmy + dbUser: lemmy + dbHost: postgres.postgres.svc.cluster.local + dbPort: 5432 + smtp: + host: "{{ .cloud.smtp.host }}" + port: "{{ .cloud.smtp.port }}" + user: "{{ .cloud.smtp.user }}" + from: "noreply@{{ .cloud.baseDomain }}" + tls: "{{ .cloud.smtp.tls }}" +defaultSecrets: + - key: dbPassword + - key: adminPassword + - key: jwtSecret + - key: smtpPassword +requiredSecrets: + - postgres.password diff --git a/lemmy/namespace.yaml b/lemmy/namespace.yaml new file mode 100644 index 0000000..054927e --- /dev/null +++ b/lemmy/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .namespace }} diff --git a/lemmy/pvc-pictrs.yaml b/lemmy/pvc-pictrs.yaml new file mode 100644 index 0000000..3748f33 --- /dev/null +++ b/lemmy/pvc-pictrs.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: lemmy-pictrs-storage + namespace: {{ .namespace }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .pictrsStorage }} diff --git a/lemmy/service-backend.yaml b/lemmy/service-backend.yaml new file mode 100644 index 0000000..74f1529 --- /dev/null +++ b/lemmy/service-backend.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: lemmy-backend + namespace: {{ .namespace }} +spec: + type: ClusterIP + selector: + component: backend + ports: + - name: http + port: {{ .backendPort }} + targetPort: {{ .backendPort }} diff --git a/lemmy/service-pictrs.yaml b/lemmy/service-pictrs.yaml new file mode 100644 index 0000000..4f52324 --- /dev/null +++ b/lemmy/service-pictrs.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: lemmy-pictrs + namespace: {{ .namespace }} +spec: + type: ClusterIP + selector: + component: pictrs + ports: + - name: http + port: {{ .pictrsPort }} + targetPort: {{ .pictrsPort }} diff --git a/lemmy/service-ui.yaml b/lemmy/service-ui.yaml new file mode 100644 index 0000000..0c12c81 --- /dev/null +++ b/lemmy/service-ui.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: lemmy-ui + namespace: {{ .namespace }} +spec: + type: ClusterIP + selector: + component: ui + ports: + - name: http + port: {{ .uiPort }} + targetPort: {{ .uiPort }}