# yaml-language-server: $schema=https://schema.zeabur.app/template.json
apiVersion: zeabur.com/v1
kind: Template
metadata:
    name: OpenReplay
spec:
    description: |
        Open-source session replay platform. Debug your web app with session recordings, network logs, console errors, and more — self-hosted and privacy-friendly.
    icon: https://avatars.githubusercontent.com/u/66554311?s=200&v=4
    variables:
        - key: PUBLIC_DOMAIN
          type: DOMAIN
          name: OpenReplay Domain
          description: The public domain for your OpenReplay instance (e.g. replay.example.com)
    tags:
        - Analytics
        - Session Replay
        - Developer Tools
        - Open Source
    readme: |
        # OpenReplay

        [OpenReplay](https://openreplay.com) is an open-source session replay suite. It lets you replay everything your users experience — DOM snapshots, network requests, console logs, JS errors — and integrates with your existing stack.

        ## Architecture

        This template deploys the full OpenReplay stack:

        | Layer | Services |
        |-------|----------|
        | Database | PostgreSQL, ClickHouse |
        | Cache | Redis (Valkey) |
        | Object storage | MinIO (S3-compatible) |
        | Application | 17 microservices |
        | Proxy | Nginx |

        ## Resource Requirements

        **Minimum: 8 GB RAM, 4 vCPU.** Each service runs in its own container. Consider a dedicated server plan.

        ## After Deployment

        1. Visit your configured domain
        2. Register your admin account
        3. Copy the tracking snippet from Settings → Projects into your app

        ## Known Limitations

        OpenReplay uses a shared EFS volume in Kubernetes. On Zeabur each service gets its own volume. Session recordings (raw → processed pipeline between `http-openreplay` and `ender-openreplay`) may not work without a shared filesystem. The UI, API, and dashboards will work.

        ## License

        [Elastic License 2.0 (ELv2)](https://github.com/openreplay/openreplay/blob/main/LICENSE) · [GitHub](https://github.com/openreplay/openreplay) · [openreplay.com](https://openreplay.com)
    services:
        - name: postgresql
          icon: https://raw.githubusercontent.com/zeabur/service-icons/main/marketplace/postgresql.svg
          template: PREBUILT_V2
          spec:
            id: postgresql
            source:
                image: postgres:17
                command:
                    - /bin/bash
                    - -c
                    - apt-get update -qq && apt-get install -y -qq curl && exec /usr/local/bin/docker-entrypoint.sh postgres
            ports:
                - id: db
                  port: 5432
                  type: TCP
            volumes:
                - id: data
                  dir: /var/lib/postgresql/data
            env:
                ASSIST_JWT_SECRET:
                    default: ${PASSWORD}
                    expose: true
                ASSIST_KEY:
                    default: ${PASSWORD}
                    expose: true
                JWT_REFRESH_SECRET:
                    default: ${PASSWORD}
                    expose: true
                JWT_SECRET:
                    default: ${PASSWORD}
                    expose: true
                JWT_SPOT_REFRESH_SECRET:
                    default: ${PASSWORD}
                    expose: true
                JWT_SPOT_SECRET:
                    default: ${PASSWORD}
                    expose: true
                POSTGRES_DB:
                    default: postgres
                    expose: true
                POSTGRES_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                POSTGRES_PASSWORD:
                    default: ${PASSWORD}
                    expose: true
                POSTGRES_USER:
                    default: postgres
                    expose: true
                S3_KEY:
                    default: openreplay
                    expose: true
                S3_SECRET:
                    default: ${PASSWORD}
                    expose: true
                TOKEN_SECRET:
                    default: ${PASSWORD}
                    expose: true
            configs:
                - path: /docker-entrypoint-initdb.d/openreplay-schema.sh
                  template: |
                    #!/bin/bash
                    set -e
                    echo "[OpenReplay] Downloading PostgreSQL schema..."
                    curl -sf "https://raw.githubusercontent.com/openreplay/openreplay/main/scripts/schema/db/init_dbs/postgresql/init_schema.sql" -o /tmp/or_schema.sql
                    echo "[OpenReplay] Applying PostgreSQL schema..."
                    psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -f /tmp/or_schema.sql
                    echo "[OpenReplay] Schema applied."
                  permission: 493
                  envsubst: false
            healthCheck:
                type: TCP
                port: db
        - name: clickhouse
          icon: https://raw.githubusercontent.com/zeabur/service-icons/main/marketplace/clickhouse.svg
          template: PREBUILT_V2
          spec:
            id: clickhouse
            source:
                image: clickhouse/clickhouse-server:25.11-alpine
            ports:
                - id: tcp
                  port: 9000
                  type: TCP
                - id: http
                  port: 8123
                  type: TCP
            volumes:
                - id: data
                  dir: /var/lib/clickhouse
            env:
                CH_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT:
                    default: "1"
                CLICKHOUSE_PASSWORD:
                    default: ""
                CLICKHOUSE_USER:
                    default: default
            configs:
                - path: /docker-entrypoint-initdb.d/openreplay-schema.sh
                  template: |
                    #!/bin/sh
                    set -e
                    echo "[OpenReplay] Downloading ClickHouse schema..."
                    wget -qO /tmp/ch_schema.sql "https://raw.githubusercontent.com/openreplay/openreplay/main/scripts/schema/db/init_dbs/clickhouse/create/init_schema.sql"
                    echo "[OpenReplay] Applying ClickHouse schema..."
                    clickhouse-client --multiquery < /tmp/ch_schema.sql || true
                    echo "[OpenReplay] ClickHouse schema applied."
                  permission: 493
                  envsubst: false
        - name: redis
          icon: https://raw.githubusercontent.com/zeabur/service-icons/main/marketplace/redis.svg
          template: PREBUILT_V2
          spec:
            id: redis
            source:
                image: ghcr.io/openreplay/valkey:8
            ports:
                - id: db
                  port: 6379
                  type: TCP
            volumes:
                - id: data
                  dir: /data
            env:
                ALLOW_EMPTY_PASSWORD:
                    default: "yes"
                REDIS_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
        - name: minio
          template: PREBUILT_V2
          spec:
            id: minio
            source:
                image: ghcr.io/openreplay/minio:2025
            ports:
                - id: api
                  port: 9000
                  type: TCP
            volumes:
                - id: data
                  dir: /bitnami/minio/data
            env:
                MINIO_DEFAULT_BUCKETS:
                    default: mobs,sessions-assets,sourcemaps,uxtesting-records,records,spots,ios-images
                MINIO_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                MINIO_ROOT_PASSWORD:
                    default: ${S3_SECRET}
                MINIO_ROOT_USER:
                    default: ${S3_KEY}
        - name: http-openreplay
          template: PREBUILT_V2
          spec:
            id: http-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/http:v1.23.0
            ports:
                - id: web
                  port: 8080
                  type: TCP
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                AWS_ACCESS_KEY_ID:
                    default: ${S3_KEY}
                AWS_ENDPOINT:
                    default: http://${MINIO_HOST}:9000
                AWS_REGION:
                    default: us-east-1
                AWS_SECRET_ACCESS_KEY:
                    default: ${S3_SECRET}
                BUCKET_NAME:
                    default: uxtesting-records
                CACHE_ASSETS:
                    default: "true"
                HTTP_OPENREPLAY_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                JWT_SECRET:
                    default: ${JWT_SECRET}
                JWT_SPOT_SECRET:
                    default: ${JWT_SPOT_SECRET}
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                POSTGRES_STRING:
                    default: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres?sslmode=disable
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
                TOKEN_SECRET:
                    default: ${TOKEN_SECRET}
                pg_password:
                    default: ${POSTGRES_PASSWORD}
        - name: api-openreplay
          template: PREBUILT_V2
          spec:
            id: api-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/api:v1.23.0
            ports:
                - id: web
                  port: 8080
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                API_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                ASSIST_JWT_SECRET:
                    default: ${ASSIST_JWT_SECRET}
                ASSIST_KEY:
                    default: ${ASSIST_KEY}
                ASSIST_URL:
                    default: http://${ASSIST_HOST}:9001/assist/%s
                AWS_ACCESS_KEY_ID:
                    default: ${S3_KEY}
                AWS_ENDPOINT:
                    default: http://${MINIO_HOST}:9000
                AWS_REGION:
                    default: us-east-1
                AWS_SECRET_ACCESS_KEY:
                    default: ${S3_SECRET}
                CLICKHOUSE_HTTP_STRING:
                    default: http://${CH_HOST}:8123
                CLICKHOUSE_STRING:
                    default: tcp://${CH_HOST}:9000
                JWT_REFRESH_SECRET:
                    default: ${JWT_REFRESH_SECRET}
                JWT_SECRET:
                    default: ${JWT_SECRET}
                JWT_SPOT_REFRESH_SECRET:
                    default: ${JWT_SPOT_REFRESH_SECRET}
                JWT_SPOT_SECRET:
                    default: ${JWT_SPOT_SECRET}
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                POSTGRES_STRING:
                    default: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres?sslmode=disable
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
                SITE_URL:
                    default: https://${PUBLIC_DOMAIN}
                TOKEN_SECRET:
                    default: ${TOKEN_SECRET}
                ch_host:
                    default: ${CH_HOST}
                ch_password:
                    default: ""
                ch_port:
                    default: "9000"
                ch_port_http:
                    default: "8123"
                ch_username:
                    default: default
                pg_password:
                    default: ${POSTGRES_PASSWORD}
        - name: sourcemapreader-openreplay
          template: PREBUILT_V2
          spec:
            id: sourcemapreader-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/sourcemapreader:v1.23.0
            ports:
                - id: web
                  port: 9000
                  type: TCP
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                ASSETS_ORIGIN:
                    default: https://${PUBLIC_DOMAIN}/sessions-assets
                AWS_REGION:
                    default: us-east-1
                LICENSE_KEY:
                    default: ""
                S3_HOST:
                    default: http://${MINIO_HOST}:9000
                S3_KEY:
                    default: ${S3_KEY}
                S3_SECRET:
                    default: ${S3_SECRET}
                SMR_HOST:
                    default: 0.0.0.0
                SOURCEMAPREADER_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
        - name: assist-openreplay
          template: PREBUILT_V2
          spec:
            id: assist-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/assist:v1.23.0
            ports:
                - id: web
                  port: 9001
                  type: TCP
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                ASSIST_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                ASSIST_JWT_SECRET:
                    default: ${ASSIST_JWT_SECRET}
                ASSIST_KEY:
                    default: ${ASSIST_KEY}
                AWS_DEFAULT_REGION:
                    default: us-east-1
                CLEAR_SOCKET_TIME:
                    default: "720"
                REDIS_URL:
                    default: ${REDIS_HOST}
                debug:
                    default: "0"
                redis:
                    default: "false"
        - name: chalice-openreplay
          template: PREBUILT_V2
          spec:
            id: chalice-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/chalice:v1.23.0
                command:
                    - /bin/sh
                    - /opt/zeabur-ns-wrapper.sh
            ports:
                - id: web
                  port: 8000
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                ASSIST_JWT_SECRET:
                    default: ${ASSIST_JWT_SECRET}
                ASSIST_KEY:
                    default: ${ASSIST_KEY}
                ASSIST_RECORDS_BUCKET:
                    default: records
                ASSIST_URL:
                    default: http://${ASSIST_HOST}:9001/assist/%s
                AWS_DEFAULT_REGION:
                    default: us-east-1
                CHALICE_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                EMAIL_FROM:
                    default: OpenReplay<do-not-reply@openreplay.com>
                EMAIL_HOST:
                    default: ""
                EMAIL_PASSWORD:
                    default: ""
                EMAIL_PORT:
                    default: "587"
                EMAIL_USE_SSL:
                    default: "false"
                EMAIL_USE_TLS:
                    default: "true"
                EMAIL_USER:
                    default: ""
                IOS_VIDEO_BUCKET:
                    default: mobs
                JWT_EXPIRATION:
                    default: "86400"
                JWT_REFRESH_SECRET:
                    default: ${JWT_REFRESH_SECRET}
                JWT_SECRET:
                    default: ${JWT_SECRET}
                JWT_SPOT_REFRESH_SECRET:
                    default: ${JWT_SPOT_REFRESH_SECRET}
                JWT_SPOT_SECRET:
                    default: ${JWT_SPOT_SECRET}
                KAFKA_SERVERS:
                    default: kafka:9092
                LICENSE_KEY:
                    default: ""
                LOGLEVEL:
                    default: INFO
                PYTHONUNBUFFERED:
                    default: "0"
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
                S3_HOST:
                    default: http://${MINIO_HOST}:9000
                S3_KEY:
                    default: ${S3_KEY}
                S3_SECRET:
                    default: ${S3_SECRET}
                SCIM_ACCESS_SECRET_KEY:
                    default: ${PASSWORD}
                SCIM_ACCESS_TOKEN_EXPIRE_SECONDS:
                    default: "86400"
                SCIM_REFRESH_SECRET_KEY:
                    default: ${PASSWORD}
                SITE_URL:
                    default: ${OR_SITE_URL}
                ch_host:
                    default: ${CH_HOST}
                ch_password:
                    default: ""
                ch_port:
                    default: "9000"
                ch_port_http:
                    default: "8123"
                ch_username:
                    default: default
                js_cache_bucket:
                    default: sessions-assets
                pg_dbname:
                    default: postgres
                pg_host:
                    default: ${POSTGRES_HOST}
                pg_password:
                    default: ${POSTGRES_PASSWORD}
                pg_port:
                    default: "5432"
                pg_user:
                    default: postgres
                root_path:
                    default: /api
                sessions_bucket:
                    default: mobs
                sessions_region:
                    default: us-east-1
                sourcemaps_bucket:
                    default: sourcemaps
                sourcemaps_reader:
                    default: http://${SOURCEMAPREADER_HOST}:9000/{}/sourcemaps
            configs:
                - path: /opt/zeabur-ns-wrapper.sh
                  template: |
                    #!/bin/sh
                    # Resolve the k8s namespace from Zeabur's DNS so health.py can reach
                    # backend services via their FQDN (e.g. alerts-openreplay.<ns>.zeabur.internal).
                    # assist-openreplay has a declared port (9001) so it always has a ClusterIP DNS entry.
                    FQDN=$(nslookup assist-openreplay 2>/dev/null | awk '/^Name:/{print $2; exit}')
                    if [ -n "$FQDN" ]; then
                      export POD_NAMESPACE=$(echo "$FQDN" | cut -d. -f2)
                      export CLUSTER_URL=$(echo "$FQDN" | cut -d. -f3-)
                    fi
                    exec /sbin/tini -- ./entrypoint.sh
                  permission: 493
                  envsubst: false
        - name: integrations-openreplay
          template: PREBUILT_V2
          spec:
            id: integrations-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/integrations:v1.23.0
            ports:
                - id: web
                  port: 8080
                  type: TCP
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                AWS_ACCESS_KEY_ID:
                    default: ${S3_KEY}
                AWS_ENDPOINT:
                    default: http://${MINIO_HOST}:9000
                AWS_REGION:
                    default: us-east-1
                AWS_SECRET_ACCESS_KEY:
                    default: ${S3_SECRET}
                BUCKET_NAME:
                    default: mobs
                INTEGRATIONS_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                JWT_SECRET:
                    default: ${JWT_SECRET}
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                POSTGRES_STRING:
                    default: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres?sslmode=disable
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
                TOKEN_SECRET:
                    default: ${TOKEN_SECRET}
                pg_password:
                    default: ${POSTGRES_PASSWORD}
        - name: spot-openreplay
          template: PREBUILT_V2
          spec:
            id: spot-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/spot:v1.23.0
            ports:
                - id: web
                  port: 8080
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                AWS_ACCESS_KEY_ID:
                    default: ${S3_KEY}
                AWS_ENDPOINT:
                    default: http://${MINIO_HOST}:9000
                AWS_REGION:
                    default: us-east-1
                AWS_SECRET_ACCESS_KEY:
                    default: ${S3_SECRET}
                BUCKET_NAME:
                    default: spots
                CACHE_ASSETS:
                    default: "true"
                FS_CLEAN_HRS:
                    default: "24"
                JWT_SECRET:
                    default: ${JWT_SECRET}
                JWT_SPOT_SECRET:
                    default: ${JWT_SPOT_SECRET}
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                POSTGRES_STRING:
                    default: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres?sslmode=disable
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
                SPOT_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                TOKEN_SECRET:
                    default: ${TOKEN_SECRET}
                pg_password:
                    default: ${POSTGRES_PASSWORD}
        - name: frontend-openreplay
          template: PREBUILT_V2
          spec:
            id: frontend-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/frontend:v1.23.0
            ports:
                - id: web
                  port: 8080
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                FRONTEND_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                HTTP_PORT:
                    default: "80"
                TRACKER_HOST:
                    default: https://${PUBLIC_DOMAIN}/script
        - name: alerts-openreplay
          template: PREBUILT_V2
          spec:
            id: alerts-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/alerts:v1.23.0
            ports:
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                AWS_ACCESS_KEY_ID:
                    default: ${S3_KEY}
                AWS_ENDPOINT:
                    default: http://${MINIO_HOST}:9000
                AWS_REGION:
                    default: us-east-1
                AWS_SECRET_ACCESS_KEY:
                    default: ${S3_SECRET}
                EMAIL_FROM:
                    default: OpenReplay<do-not-reply@openreplay.com>
                EMAIL_HOST:
                    default: ""
                EMAIL_PASSWORD:
                    default: ""
                EMAIL_PORT:
                    default: "587"
                EMAIL_USE_SSL:
                    default: "false"
                EMAIL_USE_TLS:
                    default: "true"
                EMAIL_USER:
                    default: ""
                LICENSE_KEY:
                    default: ""
                LOGLEVEL:
                    default: INFO
                POSTGRES_STRING:
                    default: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres?sslmode=disable
                PYTHONUNBUFFERED:
                    default: "0"
                ch_host:
                    default: ${CH_HOST}
                ch_password:
                    default: ""
                ch_port:
                    default: "9000"
                ch_port_http:
                    default: "8123"
                ch_username:
                    default: default
                pg_dbname:
                    default: postgres
                pg_host:
                    default: ${POSTGRES_HOST}
                pg_password:
                    default: ${POSTGRES_PASSWORD}
                pg_port:
                    default: "5432"
                pg_user:
                    default: postgres
        - name: images-openreplay
          template: PREBUILT_V2
          spec:
            id: images-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/images:v1.23.0
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                AWS_ACCESS_KEY_ID:
                    default: ${S3_KEY}
                AWS_ENDPOINT:
                    default: http://${MINIO_HOST}:9000
                AWS_REGION:
                    default: us-east-1
                AWS_SECRET_ACCESS_KEY:
                    default: ${S3_SECRET}
                BUCKET_NAME:
                    default: mobs
                FS_CLEAN_HRS:
                    default: "24"
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
        - name: sink-openreplay
          template: PREBUILT_V2
          spec:
            id: sink-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/sink:v1.23.0
            ports:
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                ASSETS_ORIGIN:
                    default: https://${PUBLIC_DOMAIN}/sessions-assets
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
        - name: storage-openreplay
          template: PREBUILT_V2
          spec:
            id: storage-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/storage:v1.23.0
            ports:
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                AWS_ACCESS_KEY_ID:
                    default: ${S3_KEY}
                AWS_ENDPOINT:
                    default: http://${MINIO_HOST}:9000
                AWS_REGION:
                    default: us-east-1
                AWS_SECRET_ACCESS_KEY:
                    default: ${S3_SECRET}
                BUCKET_NAME:
                    default: mobs
                FS_CLEAN_HRS:
                    default: "24"
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
        - name: assets-openreplay
          template: PREBUILT_V2
          spec:
            id: assets-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/assets:v1.23.0
            ports:
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                ASSETS_ORIGIN:
                    default: https://${PUBLIC_DOMAIN}/sessions-assets
                AWS_ACCESS_KEY_ID:
                    default: ${S3_KEY}
                AWS_ENDPOINT:
                    default: http://${MINIO_HOST}:9000
                AWS_REGION:
                    default: us-east-1
                AWS_SECRET_ACCESS_KEY:
                    default: ${S3_SECRET}
                BUCKET_NAME:
                    default: sessions-assets
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
        - name: canvases-openreplay
          template: PREBUILT_V2
          spec:
            id: canvases-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/canvases:v1.23.0
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                AWS_ACCESS_KEY_ID:
                    default: ${S3_KEY}
                AWS_ENDPOINT:
                    default: http://${MINIO_HOST}:9000
                AWS_REGION:
                    default: us-east-1
                AWS_SECRET_ACCESS_KEY:
                    default: ${S3_SECRET}
                BUCKET_NAME:
                    default: mobs
                FS_CLEAN_HRS:
                    default: "24"
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
        - name: db-openreplay
          template: PREBUILT_V2
          spec:
            id: db-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/db:v1.23.0
            ports:
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                CLICKHOUSE_HTTP_STRING:
                    default: http://${CH_HOST}:8123
                CLICKHOUSE_STRING:
                    default: tcp://${CH_HOST}:9000
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                POSTGRES_STRING:
                    default: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres?sslmode=disable
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
                ch_host:
                    default: ${CH_HOST}
                ch_password:
                    default: ""
                ch_port:
                    default: "9000"
                ch_port_http:
                    default: "8123"
                ch_username:
                    default: default
                pg_password:
                    default: ${POSTGRES_PASSWORD}
        - name: ender-openreplay
          template: PREBUILT_V2
          spec:
            id: ender-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/ender:v1.23.0
            ports:
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                POSTGRES_STRING:
                    default: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/postgres?sslmode=disable
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
                pg_password:
                    default: ${POSTGRES_PASSWORD}
        - name: heuristics-openreplay
          template: PREBUILT_V2
          spec:
            id: heuristics-openreplay
            source:
                image: public.ecr.aws/p1t3u8a3/heuristics:v1.23.0
            ports:
                - id: metrics
                  port: 8888
                  type: TCP
            volumes:
                - id: efs
                  dir: /mnt/efs
            env:
                KAFKA_SERVERS:
                    default: kafka:9092
                KAFKA_USE_SSL:
                    default: "false"
                LICENSE_KEY:
                    default: ""
                REDIS_STRING:
                    default: redis://${REDIS_HOST}:6379
        - name: nginx
          template: PREBUILT_V2
          spec:
            id: nginx
            source:
                image: nginx:1.27-alpine
            ports:
                - id: web
                  port: 80
                  type: HTTP
            env:
                NGINX_ENVSUBST_TEMPLATE_VARS:
                    default: MINIO_HOST HTTP_OPENREPLAY_HOST INTEGRATIONS_HOST API_HOST CHALICE_HOST SPOT_HOST ASSIST_HOST FRONTEND_HOST
                OR_SITE_URL:
                    default: ${ZEABUR_WEB_URL}
                    expose: true
            configs:
                - path: /etc/nginx/templates/default.conf.template
                  template: |
                    map $arg_peerId $sessionid {
                      default "";
                      "~.*-(\d+)(?:-.*|$)" $1;
                    }
                    map $http_x_forwarded_for $real_ip {
                        ~^(\d+\.\d+\.\d+\.\d+) $1;
                        default $remote_addr;
                    }
                    map $http_upgrade $connection_upgrade {
                      default upgrade;
                      '' close;
                    }
                    map $http_x_forwarded_proto $origin_proto {
                      default $http_x_forwarded_proto;
                      '' $scheme;
                    }
                    server {
                        listen 80;
                        client_body_buffer_size 512k;
                        client_max_body_size 10m;
                        proxy_buffer_size 64k;
                        proxy_buffers 32 64k;
                        proxy_busy_buffers_size 128k;
                        proxy_max_temp_file_size 2048m;
                        proxy_buffering on;
                        proxy_connect_timeout 120s;
                        proxy_read_timeout 300s;
                        proxy_send_timeout 300s;
                        real_ip_header X-Forwarded-For;
                        set_real_ip_from 0.0.0.0/0;
                        real_ip_recursive on;
                        add_header X-XSS-Protection "1; mode=block" always;
                        add_header X-Content-Type-Options "nosniff" always;
                        add_header Referrer-Policy "same-origin" always;
                        server_tokens off;

                        location ~ ^/(mobs|sessions-assets|frontend|static|sourcemaps|ios-images|uxtesting-records|records|spots)/ {
                          proxy_set_header X-Real-IP $remote_addr;
                          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                          proxy_set_header X-Forwarded-Proto $scheme;
                          proxy_set_header Host $http_host;
                          proxy_connect_timeout 300;
                          proxy_http_version 1.1;
                          proxy_set_header Connection "";
                          chunked_transfer_encoding off;
                          proxy_pass http://${MINIO_HOST}:9000;
                        }

                        location /minio/ {
                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection "Upgrade";
                          proxy_set_header Host $host;
                          proxy_pass http://${MINIO_HOST}:9000;
                        }

                        location /ingest/ {
                          rewrite ^/ingest/(.*) /$1 break;
                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection "Upgrade";
                          proxy_set_header X-Forwarded-For $real_ip;
                          proxy_set_header X-Forwarded-Host $host;
                          proxy_set_header X-Real-IP $real_ip;
                          proxy_set_header Host $host;
                          proxy_pass http://${HTTP_OPENREPLAY_HOST}:8080;
                          proxy_read_timeout 300s;
                          proxy_connect_timeout 120s;
                          proxy_send_timeout 300s;
                          add_header 'Access-Control-Allow-Origin' '*' always;
                          add_header 'Access-Control-Allow-Methods' 'POST' always;
                          add_header 'Access-Control-Allow-Headers' 'Content-Type,Authorization,Content-Encoding' always;
                          add_header 'Access-Control-Expose-Headers' 'Content-Length' always;
                        }

                        location /integrations/ {
                          rewrite ^/integrations/(.*) /$1 break;
                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection "Upgrade";
                          proxy_set_header X-Forwarded-For $real_ip;
                          proxy_set_header X-Forwarded-Host $host;
                          proxy_set_header X-Real-IP $real_ip;
                          proxy_set_header Host $host;
                          proxy_pass http://${INTEGRATIONS_HOST}:8080;
                          proxy_read_timeout 300s;
                          proxy_connect_timeout 120s;
                          proxy_send_timeout 300s;
                          add_header 'Access-Control-Allow-Origin' '*' always;
                          add_header 'Access-Control-Allow-Methods' 'POST,PATCH,OPTIONS,DELETE' always;
                          add_header 'Access-Control-Allow-Headers' 'Content-Type,Authorization,Content-Encoding,X-Openreplay-Batch' always;
                          add_header 'Access-Control-Expose-Headers' 'Content-Length' always;
                        }

                        location /v2/api/ {
                          rewrite ^/v2/api/(.*) /$1 break;
                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection "Upgrade";
                          proxy_set_header Host $host;
                          proxy_set_header X-Forwarded-Proto $origin_proto;
                          proxy_set_header X-Forwarded-For $real_ip;
                          proxy_set_header X-Real-IP $real_ip;
                          proxy_pass http://${API_HOST}:8080;
                          proxy_read_timeout 300s;
                          proxy_connect_timeout 120s;
                          proxy_send_timeout 300s;
                          add_header 'Access-Control-Allow-Origin' '$http_origin' always;
                          add_header 'Access-Control-Allow-Methods' 'POST, GET, PATCH, DELETE, OPTIONS, PUT' always;
                          add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Content-Encoding, X-Openreplay-Batch' always;
                          add_header 'Access-Control-Expose-Headers' 'Content-Length' always;
                          add_header 'Access-Control-Max-Age' '3600' always;
                          add_header 'Access-Control-Allow-Credentials' 'true' always;
                          if ($request_method = 'OPTIONS') {
                            add_header 'Access-Control-Allow-Origin' '$http_origin' always;
                            add_header 'Access-Control-Allow-Methods' 'POST, GET, PATCH, DELETE, OPTIONS, PUT' always;
                            add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Content-Encoding, X-Openreplay-Batch' always;
                            add_header 'Access-Control-Max-Age' '3600' always;
                            add_header 'Content-Length' '0';
                            add_header 'Content-Type' 'text/plain';
                            return 204;
                          }
                        }

                        location /api/ {
                          rewrite ^/api/(.*) /$1 break;
                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection "Upgrade";
                          proxy_set_header Host $host;
                          proxy_set_header X-Forwarded-Proto $origin_proto;
                          proxy_set_header X-Forwarded-For $real_ip;
                          proxy_pass http://${CHALICE_HOST}:8000;
                          add_header Cache-Control "no-store,no-cache" always;
                          add_header Pragma "no-cache" always;
                        }

                        location /spot/ {
                          rewrite ^/spot/(.*) /$1 break;
                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection "Upgrade";
                          proxy_set_header Host $host;
                          proxy_set_header X-Forwarded-Proto $origin_proto;
                          proxy_set_header X-Forwarded-For $real_ip;
                          proxy_set_header X-Real-IP $real_ip;
                          proxy_pass http://${SPOT_HOST}:8080;
                          proxy_read_timeout 300s;
                          proxy_connect_timeout 120s;
                          proxy_send_timeout 300s;
                          add_header 'Access-Control-Allow-Origin' '*' always;
                          add_header 'Access-Control-Allow-Methods' 'POST, PATCH, DELETE, OPTIONS' always;
                          add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Content-Encoding, X-Openreplay-Batch' always;
                          add_header 'Access-Control-Expose-Headers' 'Content-Length' always;
                          if ($request_method = 'OPTIONS') {
                            add_header 'Access-Control-Allow-Origin' '*' always;
                            add_header 'Access-Control-Allow-Methods' 'POST, PATCH, DELETE, OPTIONS' always;
                            add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Content-Encoding, X-Openreplay-Batch' always;
                            add_header 'Access-Control-Max-Age' '3600';
                            add_header 'Content-Type' 'text/plain charset=UTF-8';
                            add_header 'Content-Length' 0;
                            return 204;
                          }
                        }

                        location /assist/ {
                          rewrite ^/assist/(.*) /$1 break;
                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection $connection_upgrade;
                          proxy_set_header Host $host;
                          proxy_set_header X-Real-IP $real_ip;
                          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                          proxy_set_header X-Forwarded-Proto $origin_proto;
                          proxy_read_timeout 3600s;
                          proxy_send_timeout 3600s;
                          proxy_connect_timeout 75s;
                          proxy_buffering off;
                          proxy_pass http://${ASSIST_HOST}:9001;
                        }

                        location /ws-assist/ {
                          rewrite ^/ws-assist/(.*) /$1 break;
                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection $connection_upgrade;
                          proxy_set_header Host $host;
                          proxy_set_header X-Real-IP $real_ip;
                          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                          proxy_set_header X-Forwarded-Proto $origin_proto;
                          proxy_read_timeout 3600s;
                          proxy_send_timeout 3600s;
                          proxy_connect_timeout 120s;
                          proxy_buffering off;
                          add_header 'Access-Control-Allow-Origin' '$http_origin' always;
                          add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
                          add_header 'Access-Control-Allow-Headers' 'sessionid, Content-Type, Authorization' always;
                          add_header 'Access-Control-Max-Age' '1728000' always;
                          add_header 'X-Debug-Session-ID' $sessionid always;
                          proxy_pass http://${ASSIST_HOST}:9001;
                        }

                        location /script/ {
                          rewrite ^/script/(.*)/openreplay(.*).js$ /$1/openreplay$2.js break;
                          proxy_http_version 1.1;
                          proxy_ssl_protocols TLSv1.2 TLSv1.3;
                          proxy_ssl_server_name on;
                          proxy_set_header Host static.openreplay.com;
                          proxy_pass https://static.openreplay.com;
                          proxy_read_timeout 300s;
                          proxy_connect_timeout 120s;
                          proxy_send_timeout 300s;
                          proxy_buffering on;
                          client_max_body_size 8m;
                        }

                        location / {
                          index /index.html;
                          rewrite ^((?!.(js|css|png|svg|jpg|woff|woff2)).)*$ /index.html break;
                          proxy_set_header Host $http_host;
                          proxy_set_header X-Forwarded-For $real_ip;
                          proxy_set_header X-Forwarded-Proto $origin_proto;
                          proxy_pass http://${FRONTEND_HOST}:8080;
                          proxy_intercept_errors on;
                          error_page 404 =200 /index.html;
                        }
                    }
                  permission: null
                  envsubst: false
            healthCheck:
                type: TCP
                port: web
          domainKey: PUBLIC_DOMAIN
localization:
    ja-JP:
        description: |
            オープンソースのセッションリプレイプラットフォーム。セッション録画、ネットワークログ、コンソールエラーなどでWebアプリのデバッグを支援します。
        variables:
            - key: PUBLIC_DOMAIN
              type: STRING
              name: OpenReplayドメイン
              description: OpenReplayインスタンスの公開ドメイン（例：replay.example.com）
        readme: |
            # OpenReplay

            [OpenReplay](https://openreplay.com) はオープンソースのセッションリプレイスイートです。DOM スナップショット、ネットワークリクエスト、コンソールログ、JSエラーなど、ユーザーが体験したすべてを再生できます。

            ## 構成

            このテンプレートは完全な OpenReplay スタックをデプロイします：PostgreSQL、ClickHouse、Redis、MinIO、17 のマイクロサービス、Nginx リバースプロキシ。

            ## リソース要件

            **最低：8 GB RAM、4 vCPU。** 専用サーバープランを推奨します。

            ## デプロイ後の設定

            1. 設定したドメインにアクセス
            2. 管理者アカウントを登録
            3. 「設定 → プロジェクト」からトラッキングスニペットをコピーしてアプリに追加

            ## ライセンス

            [Elastic License 2.0 (ELv2)](https://github.com/openreplay/openreplay/blob/main/LICENSE)
    zh-CN:
        description: |
            开源 Session Replay 平台。通过 Session 录像、网络日志、Console 错误等协助调试 Web 应用，自托管且隐私友好。
        variables:
            - key: PUBLIC_DOMAIN
              type: STRING
              name: OpenReplay 域名
              description: 你的 OpenReplay 实例的公开域名（如 replay.example.com）
        readme: |
            # OpenReplay

            [OpenReplay](https://openreplay.com) 是开源的 Session Replay 工具套件，可重播用户完整操作过程，包含 DOM 快照、网络请求、Console 日志、JS 错误等。

            ## 架构

            本模板部署完整的 OpenReplay 技术栈：PostgreSQL、ClickHouse、Redis、MinIO、17 个微服务、Nginx 反向代理。

            ## 资源需求

            **最低需求：8 GB RAM、4 vCPU。** 建议使用专用服务器方案。

            ## 部署后配置

            1. 访问你配置的域名
            2. 注册管理员账号
            3. 从「设置 → 项目」复制追踪代码并添加到你的应用

            ## 已知限制

            OpenReplay 在 Kubernetes 中使用共享 EFS Volume。在 Zeabur 上每个服务有独立 Volume，Session 录像的原始数据处理流水线可能无法正常工作。UI、API 和仪表板功能可正常使用。

            ## 授权

            [Elastic License 2.0 (ELv2)](https://github.com/openreplay/openreplay/blob/main/LICENSE)
    zh-TW:
        description: |
            開源 Session Replay 平台。透過 Session 錄影、網路記錄、Console 錯誤等方式協助你除錯網頁應用程式，自架且隱私友好。
        variables:
            - key: PUBLIC_DOMAIN
              type: STRING
              name: OpenReplay 網域
              description: 你的 OpenReplay 實例的公開網域（例如 replay.example.com）
        readme: |
            # OpenReplay

            [OpenReplay](https://openreplay.com) 是開源的 Session Replay 工具組。可重播使用者的完整操作過程，包含 DOM 快照、網路請求、Console 記錄、JS 錯誤等。

            ## 架構

            本模板部署完整的 OpenReplay 技術棧：PostgreSQL、ClickHouse、Redis、MinIO、17 個微服務、Nginx 反向代理。

            ## 資源需求

            **最低需求：8 GB RAM、4 vCPU。** 建議使用專用伺服器方案。

            ## 部署後設定

            1. 前往你設定的網域
            2. 註冊管理員帳號
            3. 從「設定 → 專案」複製追蹤程式碼並加入你的應用程式

            ## 已知限制

            OpenReplay 在 Kubernetes 中使用共享 EFS Volume。在 Zeabur 上每個服務有獨立的 Volume，Session 錄影的原始資料處理管線（`http-openreplay` → `ender-openreplay`）可能無法正常運作。UI、API 和儀表板功能可正常使用。

            ## 授權

            [Elastic License 2.0 (ELv2)](https://github.com/openreplay/openreplay/blob/main/LICENSE)
