完整配置示例
生产配置只固定你负责的值:secret、数据库、Redis、域名、反向代理、存储路径和已启用功能。不要把全量默认值整份复制到生产环境。
| 目标 | 使用内容 |
|---|---|
| Docker Compose 单机部署 | 使用 Compose env,不写 synctv.yaml |
| 裸机或 Kubernetes | 从最小生产配置开始 |
| 查字段层级 | 查看全量配置模板 |
| 查当前二进制的最终配置 | 运行 synctv config show --output yaml |
快速开始 的生产 Compose 使用 .env.postgres、.env.redis 和 .env.synctv。最小需要确认:
SYNCTV_BOOTSTRAP_ROOT_PASSWORD=replace-with-a-strong-password
# 前端和 API 不同 origin 时填写 JSON 数组。SYNCTV_SERVER_CORS_ALLOWED_ORIGINS=["https://app.example.com"]
# init-compose-env.sh 会生成这些长期 secret。备份并复用同一组文件。SYNCTV_JWT_SECRET=...SYNCTV_SECURITY_OPAQUE_SERVER_SETUP_SECRET=...SYNCTV_SECURITY_CREDENTIAL_ENCRYPTION_KEY=...启动和验收:
docker compose configdocker compose up -dcurl -fsS http://localhost:8080/health/ready裸机、Kubernetes 或自定义 secret file 使用下面的 YAML 模板。
这个示例覆盖生产部署的常见必填项。secret 使用 _file 形式注入。
server: host: "0.0.0.0" port: 8080 enable_reflection: false cors_allowed_origins: - "https://app.example.com" trusted_proxies: - "10.0.0.0/8"
data_dir: "/var/lib/synctv"
database: url_file: "/run/secrets/database_url" max_connections: 20 min_connections: 5
redis: url_file: "/run/secrets/redis_url" key_prefix: "synctv:"
jwt: secret_file: "/run/secrets/jwt_secret"
file_storage: default_backend: "s3_public" chat_attachments_backend: "s3_public" user_avatars_backend: "s3_public" media_covers_backend: "s3_public" room_covers_backend: "s3_public" playlist_covers_backend: "s3_public" upload_token_secret_file: "/run/secrets/file_upload_token_secret" backends: database: type: "database" database: compression: "zstd" s3_public: type: "s3" s3: endpoint: "https://s3.example.com" access_key_id_file: "/run/secrets/file_storage_s3_access_key_id" secret_access_key_file: "/run/secrets/file_storage_s3_secret_access_key" bucket: "synctv-files" region: "auto" base_path: "files/" public_base_url: "https://cdn.example.com/files" upload_expires_seconds: 900
security: credential_encryption_key_file: "/run/secrets/credential_encryption_key" opaque_server_setup_secret_file: "/run/secrets/opaque_server_setup_secret" ssrf: enabled: false allow_private_network_targets: false allowed_hosts: [] allowed_ip_ranges: []
management: enabled: true transport: "unix" enable_reflection: false
bootstrap: create_root_user: true root_username: "root" root_password_file: "/run/secrets/bootstrap_root_password"
logging: level: "info" format: "json"
metrics: enabled: true host: "0.0.0.0" port: 9090 auth: mode: "bearer_token" bearer_token_file: "/run/secrets/metrics_bearer_token"这个骨架没有启用集群、WebAuthn、OAuth2、SMTP、Provider、HLS 文件存储或 slice cache 文件后端。按功能补充对应配置页。
这份模板覆盖当前版本的全部静态配置字段。Provider 相关字段只配置本进程内置 local provider adapter;远程 provider instance 通过管理 API/CLI 持久化,见 媒体 Provider。
模板同时列出 secret 直填字段和 *_file 字段,用于展示层级。生产配置使用 *_file 或环境变量注入。
server: host: "0.0.0.0" port: 8080 enable_reflection: false trusted_proxies: - "10.0.0.0/8" - "192.168.0.0/16" cors_allowed_origins: - "https://app.example.com" advertise_host: "synctv-0.synctv-headless.default.svc.cluster.local" shutdown_drain_timeout_seconds: 30 grpc_max_message_size_bytes: 16777216 grpc_compression_enabled: true
time: timezone: "Asia/Shanghai"
public_ids: sqids: alphabet: null min_length: 12
security: credential_encryption_key: "" credential_encryption_key_file: "/run/secrets/credential_encryption_key" opaque_server_setup_secret: "" opaque_server_setup_secret_file: "/run/secrets/opaque_server_setup_secret" ssrf: enabled: false allow_private_network_targets: false allowed_hosts: [] allowed_ip_ranges: []
data_dir: "/var/lib/synctv"
metrics: enabled: true host: "0.0.0.0" port: 9090 tls: enabled: false cert_path: "" key_path: "" auth: mode: "bearer_token" bearer_token: "" bearer_token_file: "/run/secrets/metrics_bearer_token" basic_username: "" basic_password: "" basic_password_file: "/run/secrets/metrics_basic_password" kubernetes: audience: "" authentication_cache_ttl_seconds: 60 authorization_cache_ttl_seconds: 60
management: enabled: true transport: "unix" port: 50052 unix_socket_path: "run/synctv.sock" auth_token: "" enable_reflection: false
database: url: "" url_file: "/run/secrets/database_url" host: "" port: 0 username: "" password: "" name: "" max_connections: 20 min_connections: 5 connect_timeout_seconds: 10 idle_timeout_seconds: 600 max_lifetime_seconds: 1800
redis: url: "" url_file: "/run/secrets/redis_url" host: "" port: 0 username: "" password: "" database: 0 connect_timeout_seconds: 5 response_timeout_seconds: 5 pipeline_buffer_size: 512 key_prefix: "synctv:" deployment_mode: "standalone" sentinel_master_name: null sentinel_addresses: []
jwt: secret: "" secret_file: "/run/secrets/jwt_secret" access_token_duration_hours: 1 refresh_token_duration_days: 30 guest_token_duration_hours: 4 clock_skew_leeway_secs: 60
logging: level: "info" format: "json" filter: null backtrace: false file_path: null
livestream: rtmp_port: 1935 public_rtmp_host: "stream.example.com" gop_cache_size: 2 stream_timeout_seconds: 300 cleanup_check_interval_seconds: 60 pull_max_retries: 10 pull_initial_backoff_ms: 1000 pull_max_backoff_ms: 30000 max_flv_tag_size_bytes: 10485760 gop_cache_max_memory_mb: 100 hls_memory_max_mb: 0 hls_storage_backend: "memory" hls_storage_path: "" hls_oss: endpoint: "" access_key_id: "" access_key_id_file: "" secret_access_key: "" secret_access_key_file: "" bucket: "" region: null base_path: "hls/" flv_max_connection_duration_seconds: 86400 flv_write_timeout_seconds: 30
file_storage: default_backend: "disabled" chat_attachments_backend: "" user_avatars_backend: "" media_covers_backend: "" room_covers_backend: "" playlist_covers_backend: "" upload_token_secret: "" upload_token_secret_file: "/run/secrets/file_upload_token_secret" backends: database: type: "database" database: compression: "zstd" compression_min_size_bytes: 4096 compression_min_savings_percent: 10
webauthn: enabled: true rp_id: "example.com" rp_origin: "https://app.example.com" rp_name: "SyncTV" allowed_origins: - "https://admin.example.com" allow_subdomains: false allow_any_port: false timeout_seconds: 300
email: smtp_host: "smtp.example.com" smtp_port: 587 smtp_username: "synctv@example.com" smtp_password: "" smtp_password_file: "/run/secrets/smtp_password" from_email: "synctv@example.com" from_name: "SyncTV" use_tls: true
media_providers: alist: request_timeout_seconds: 30 connect_timeout_seconds: 10 emby: request_timeout_seconds: 30 connect_timeout_seconds: 10 bilibili: request_timeout_seconds: 30 connect_timeout_seconds: 10
webrtc: mode: "peer_to_peer" enable_builtin_stun: true stun_port: 3478 stun_host: "0.0.0.0" stun_external_addr: "" filter_private_ice_candidates: true
connection_limits: max_per_user: 20 max_per_room: 2000 max_total: 100000 idle_timeout_seconds: 300 max_duration_seconds: 86400 ws_message_rate_limit_per_second: 50
bootstrap: create_root_user: true root_username: "root" root_password: "" root_password_file: "/run/secrets/bootstrap_root_password"
cluster: enabled: false secret: "" secret_file: "" critical_channel_capacity: 10000 publish_channel_capacity: 100000 discovery_mode: "redis" leader_election_mode: "redis" peers: [] catchup_window_secs: 300 stream_max_length: 100000
password_complexity: min_length: 8 require_uppercase: true require_lowercase: true require_digit: true require_special: false max_repeated_chars: 3 zxcvbn_enabled: false zxcvbn_min_score: 3
buffer_sizes: websocket_outbound: 256 audit_buffer: 10000
cache: l1_capacity: 5000 l1_ttl_seconds: 300 l2_ttl_seconds: 300 username_cache_capacity: 10000 username_cache_ttl_seconds: 3600 permission_cache_capacity: 20000 permission_cache_ttl_seconds: 300
proxy_slice_cache: enabled: true slice_size_bytes: 2097152 max_cache_size_bytes: 536870912 segment_ttl_seconds: 300 stale_max_age_seconds: 60 stale_while_revalidate: true file_backend_enabled: false file_cache_dir: "" eviction_interval_seconds: 60 watermark_ratio: 0.875
messaging_rate_limits: chat_per_second: 10 window_seconds: 1
request_rate_limits: auth_max_requests: 5 auth_window_seconds: 60 write_max_requests: 120 write_window_seconds: 60 read_max_requests: 600 read_window_seconds: 60 media_max_requests: 120 media_window_seconds: 60 admin_max_requests: 180 admin_window_seconds: 60 streaming_max_requests: 1200 streaming_window_seconds: 60 websocket_max_requests: 60 websocket_window_seconds: 60URL 形式和拆分形式
Section titled “URL 形式和拆分形式”database 和 redis 都支持完整 URL,也支持拆分字段。生产环境通常优先使用 url_file,因为最少暴露凭据。
数据库 URL 形式:
database: url_file: "/run/secrets/database_url"数据库拆分形式:
database: host: "postgres.example.com" port: 5432 username: "synctv" password_file: "/run/secrets/database_password" name: "synctv"Redis URL 形式:
redis: url_file: "/run/secrets/redis_url"Redis Sentinel 形式:
Sentinel 不能与 cluster.enabled=true 同时使用。集群部署请使用稳定单入口 Redis、托管 Redis 或平台保证连接语义的 Redis 服务。
redis: deployment_mode: "sentinel" sentinel_master_name: "mymaster" sentinel_addresses: - "redis://sentinel-0.redis:26379" - "redis://sentinel-1.redis:26379" - "redis://sentinel-2.redis:26379"查看当前版本完整生效配置
Section titled “查看当前版本完整生效配置”synctv config show --output yaml指定配置文件:
synctv --config /etc/synctv/synctv.yaml config show --output yaml输出 TOML:
synctv --config /etc/synctv/synctv.yaml config show --output toml部署前运行:
synctv --config /etc/synctv/synctv.yaml config validate检查内容:
- 必要 secret 是否为空或仍为默认值。
cluster.enabled=true时 Redis 和cluster.secret是否满足要求。- TCP management 是否配置了
management.auth_token。 - CORS origin、gRPC message size、WebAuthn origin 等字段是否合法。
- 路径类配置能否解析。