# yaml-language-server: $schema=https://schema.zeabur.app/template.json
apiVersion: zeabur.com/v1
kind: Template
metadata:
    name: Fast Note Sync PostgreSQL
spec:
    description: |
        PostgreSQL version. Self-host Fast Note Sync Service with a bundled PostgreSQL database for syncing and backing up Obsidian notes across devices.
    icon: https://cdn.zeabur.com/prebuilt.svg
    tags:
        - Tool
        - Productivity
        - Notes
        - Database
    readme: |
        # Fast Note Sync PostgreSQL

        PostgreSQL version.

        Fast Note Sync Service is the self-hosted backend for the Fast Note Sync Obsidian plugin. This template deploys both Fast Note Sync and a PostgreSQL database in the same Zeabur project.

        No manual variables are required. Zeabur generates the internal password automatically.

        ## After Deployment

        1. Wait until both `postgresql` and `fast-note-sync` are running.
        2. Open the generated Zeabur domain for `fast-note-sync`.
        3. Register your first user account.
        4. Set `USER_REGISTER_IS_ENABLE` to `false` after the first account is created.
        5. In the Obsidian plugin, paste the remote service configuration from the web UI.

        ## Database

        This template configures Fast Note Sync to use PostgreSQL:

        - `database.type: postgres`
        - `user-database.type: ""`, so user databases inherit the PostgreSQL main database
        - PostgreSQL user data is isolated by schema internally

        ## Backups

        Zeabur PostgreSQL supports online backup and automatic backup. Automatic database backups can be enabled from the PostgreSQL service backup page.

        Zeabur backup retention is limited to 7 days. If you need longer retention, download and store backups elsewhere.

        PostgreSQL automatic backups only cover the database. Files stored in Fast Note Sync persistent volumes, such as uploaded attachments under `/fast-note-sync/storage`, still need a separate backup strategy.
    services:
        - name: postgresql
          icon: https://cdn.zeabur.com/marketplace/postgresql.svg
          template: PREBUILT
          spec:
            id: postgresql
            source:
                image: postgres:18
                command:
                    - docker-entrypoint.sh
                    - -c
                    - config_file=/etc/postgresql/postgresql.conf
            ports:
                - id: database
                  port: 5432
                  type: TCP
            volumes:
                - id: data
                  dir: /var/lib/postgresql/18/docker
            instructions:
                - title: Connection String
                  content: postgresql://${POSTGRES_USERNAME}:${POSTGRES_PASSWORD}@${PORT_FORWARDED_HOSTNAME}:${DATABASE_PORT_FORWARDED_PORT}/${POSTGRES_DATABASE}
                - title: PostgreSQL Connect Command
                  content: psql "postgresql://${POSTGRES_USERNAME}:${POSTGRES_PASSWORD}@${PORT_FORWARDED_HOSTNAME}:${DATABASE_PORT_FORWARDED_PORT}/${POSTGRES_DATABASE}"
                - title: PostgreSQL username
                  content: ${POSTGRES_USERNAME}
                - title: PostgresSQL password
                  content: ${POSTGRES_PASSWORD}
                - title: PostgresSQL database
                  content: ${POSTGRES_DATABASE}
                - title: PostgreSQL host
                  content: ${PORT_FORWARDED_HOSTNAME}
                - title: PostgreSQL port
                  content: ${DATABASE_PORT_FORWARDED_PORT}
            env:
                PGDATA:
                    default: /var/lib/postgresql/18/docker/pgdata
                    expose: false
                POSTGRES_CONNECTION_STRING:
                    default: postgresql://${POSTGRES_USERNAME}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DATABASE}
                    expose: true
                POSTGRES_DATABASE:
                    default: ${POSTGRES_DB}
                    expose: true
                POSTGRES_DB:
                    default: fast_note_sync
                POSTGRES_HOST:
                    default: ${CONTAINER_HOSTNAME}
                    expose: true
                POSTGRES_PASSWORD:
                    default: ${PASSWORD}
                    expose: true
                POSTGRES_PORT:
                    default: ${DATABASE_PORT}
                    expose: true
                POSTGRES_URI:
                    default: ${POSTGRES_CONNECTION_STRING}
                    expose: true
                POSTGRES_USER:
                    default: root
                POSTGRES_USERNAME:
                    default: ${POSTGRES_USER}
                    expose: true
            configs:
                - path: /etc/postgresql/postgresql.conf
                  template: |
                    listen_addresses = '*'
                    max_connections = 256
                    shared_buffers = 256MB
                    dynamic_shared_memory_type = posix
                    max_wal_size = 1GB
                    min_wal_size = 80MB
                    log_timezone = 'UTC'
                    datestyle = 'iso, mdy'
                    timezone = 'UTC'
                    lc_messages = 'en_US.UTF-8'
                    lc_monetary = 'en_US.UTF-8'
                    lc_numeric = 'en_US.UTF-8'
                    lc_time = 'en_US.UTF-8'
                    default_text_search_config = 'pg_catalog.english'
                  permission: null
                  envsubst: null
            healthCheck:
                type: TCP
                port: database
            portForwarding:
                enabled: true
        - name: fast-note-sync
          icon: https://cdn.zeabur.com/prebuilt.svg
          dependencies:
            - postgresql
          template: PREBUILT_V2
          spec:
            id: fast-note-sync
            source:
                image: docker.io/haierkeys/fast-note-sync-service:2.14.4
                command:
                    - /bin/sh
                    - -c
                    - |
                      for i in $(seq 1 60); do
                        if nc -z "$POSTGRES_HOST" "$POSTGRES_PORT"; then
                          exec /entrypoint.sh
                        fi
                        echo "Waiting for PostgreSQL at $POSTGRES_HOST:$POSTGRES_PORT..."
                        sleep 1
                      done
                      echo "PostgreSQL did not become ready in time." >&2
                      exit 1
            ports:
                - id: api
                  port: 9000
                  type: HTTP
            volumes:
                - id: storage
                  dir: /fast-note-sync/storage
                - id: config
                  dir: /fast-note-sync/config
            env:
                PASSWORD:
                    default: ${PASSWORD}
                    expose: false
                PORT:
                    default: ${WEB_PORT}
                    expose: false
                USER_REGISTER_IS_ENABLE:
                    default: "true"
                    expose: false
            configs:
                - path: /fast-note-sync/config/config.yaml
                  template: |
                    server:
                      run-mode: release
                      http-port: ":9000"
                      read-timeout: 60
                      write-timeout: 60
                      private-http-listen: ""
                      mcp-sse-ping-interval: 30
                      webgui-port: ""
                      share-port: ""
                      share-base-url: ""
                      ext-api-url: ""
                    app:
                      default-page-size: 10
                      max-page-size: 100
                      default-context-timeout: 60
                      temp-path: storage/temp
                      is-return-sussess: false
                      soft-delete-retention-time: "7d"
                      sync-log-retention-time: "30d"
                      history-keep-versions: 100
                      history-save-delay: "10s"
                      upload-session-timeout: "1d"
                      file-chunk-size: "512KB"
                      download-session-timeout: "1h"
                      worker-pool-max-workers: 100
                      worker-pool-queue-size: 1000
                      write-queue-capacity: 1000
                      write-queue-timeout: "30s"
                      write-queue-idle-time: "10m"
                      ws-read-max-payload-size: "128MB"
                      ws-write-max-payload-size: "128MB"
                      ws-parallel-enabled: true
                      ws-parallel-golimit: 3
                      ws-check-utf8-enabled: true
                      ws-compression-enabled: true
                      ws-compression-level: 1
                      ws-compression-threshold: 512
                      log-save-fileurl: storage/logs/
                      log-file: log.log
                      pull-source: auto
                    security:
                      auth-token-key: "${PASSWORD}-auth"
                      token-expiry: 365d
                      share-token-key: "${PASSWORD}-share"
                      share-token-expiry: 30d
                    database:
                      type: postgres
                      path: ""
                      username: "${POSTGRES_USERNAME}"
                      password: "${POSTGRES_PASSWORD}"
                      host: "${POSTGRES_HOST}"
                      port: ${POSTGRES_PORT}
                      name: "${POSTGRES_DATABASE}"
                      ssl-mode: disable
                      table-prefix: ""
                      schema: public
                      auto-migrate: true
                      charset: ""
                      parse-time: true
                      max-idle-conns: 10
                      max-open-conns: 100
                      conn-max-lifetime: 30m
                      conn-max-idle-time: 10m
                      enable-write-queue: true
                      max-write-concurrency: 0
                    user-database:
                      type: ""
                      path: ""
                      username: ""
                      password: ""
                      host: ""
                      port: 0
                      name: ""
                      ssl-mode: ""
                      table-prefix: ""
                      schema: public
                      auto-migrate: true
                      charset: ""
                      parse-time: true
                      max-idle-conns: 10
                      max-open-conns: 100
                      conn-max-lifetime: 30m
                      conn-max-idle-time: 10m
                      enable-write-queue: true
                      max-write-concurrency: 0
                    log:
                      level: warn
                      file: storage/logs/log.log
                      production: true
                    user:
                      register-is-enable: true
                      admin-uid: 0
                    tracer:
                      enabled: true
                      header: "X-Trace-ID"
                    short-link:
                      base-url: "https://sink.cool"
                      api-key: "SinkCool"
                      password: ""
                      cloaking: false
                    storage:
                      local-fs:
                        is-enable: false
                        httpfs-is-enable: true
                        save-path: "storage/uploads"
                      aliyun-oss:
                        is-enable: true
                      aws-s3:
                        is-enable: true
                      cloudflare-r2:
                        is-enable: true
                      minio:
                        is-enable: true
                      webdav:
                        is-enable: true
                    git:
                      name: "FNS Service"
                      email: "fns@email.com"
                    webgui:
                      font-set: "local"
                    ngrok:
                      enabled: false
                      auth-token: ""
                      domain: ""
                    cloudflare:
                      enabled: false
                      token: ""
                      log-enabled: false
                  permission: 420
                  envsubst: true
            healthCheck:
                type: TCP
                port: api
            portForwarding:
                enabled: false
localization:
    zh-CN:
        description: |
            PostgreSQL 版本。自托管 Fast Note Sync Service，并内置 PostgreSQL 数据库，用于跨设备同步和备份 Obsidian 笔记。
        readme: |
            # Fast Note Sync PostgreSQL

            PostgreSQL 版本。

            Fast Note Sync Service 是 Fast Note Sync Obsidian 插件的自托管后端服务。此模板会在同一个 Zeabur 项目中部署 Fast Note Sync 和 PostgreSQL 数据库。

            不需要手动填写变量。Zeabur 会自动生成内部密码。

            ## 部署后

            1. 等待 `postgresql` 和 `fast-note-sync` 两个服务都运行成功。
            2. 打开 Zeabur 为 `fast-note-sync` 生成的域名。
            3. 注册第一个用户账号。
            4. 第一个账号创建完成后，将 `USER_REGISTER_IS_ENABLE` 改为 `false`。
            5. 在 Obsidian 插件中粘贴 Web UI 里的远端服务配置。

            ## 数据库

            此模板会把 Fast Note Sync 配置为 PostgreSQL：

            - `database.type: postgres`
            - `user-database.type: ""`，用户数据库继承主 PostgreSQL 数据库
            - PostgreSQL 用户数据会在内部按 schema 隔离

            ## 备份

            Zeabur PostgreSQL 支持在线备份和自动备份。可以在 PostgreSQL 服务的备份页面启用自动备份。

            Zeabur 备份保留时间为 7 天。如果需要更长保留期，请下载备份并另存到其它位置。

            PostgreSQL 自动备份只覆盖数据库。Fast Note Sync 持久化卷中的文件，例如 `/fast-note-sync/storage` 下的附件，仍然需要单独的备份策略。
