Full Configuration Example
Choose a Configuration Template
Section titled “Choose a Configuration Template”Production configuration should pin only the values you own: secrets, database, Redis, domains, reverse proxy settings, storage paths, and enabled features. Do not copy the full default template into production.
| Goal | Use |
|---|---|
| Single-node Docker Compose deployment | Use Compose env, without synctv.yaml |
| Bare-metal or Kubernetes | Start from the minimal production skeleton |
| Look up field hierarchy | Use the full template |
| Inspect the effective config for this binary | Run synctv config show --output yaml |
Configuration Templates
Section titled “Configuration Templates”Docker Compose installation uses .env.postgres, .env.redis, and .env.synctv. Confirm at least:
SYNCTV_BOOTSTRAP_ROOT_PASSWORD=replace-with-a-strong-password
# Use a JSON array when frontend and API use different origins.SYNCTV_SERVER_CORS_ALLOWED_ORIGINS=["https://app.example.com"]
# make compose-init generates these long-lived secrets. Back up and reuse the same files.SYNCTV_JWT_SECRET=...SYNCTV_SECURITY_OPAQUE_SERVER_SETUP_SECRET=...SYNCTV_SECURITY_CREDENTIAL_ENCRYPTION_KEY=...SYNCTV_SECURITY_TOTP_ENCRYPTION_KEY=...SYNCTV_SECURITY_EMAIL_OUTBOX_ENCRYPTION_KEY=...SYNCTV_SECURITY_PROXY_SIGNING_KEY=...SYNCTV_SECURITY_MEDIA_SWARM_SIGNING_KEY=...SYNCTV_SECURITY_PROVIDER_SESSION_ENCRYPTION_KEY=...SYNCTV_SECURITY_LOGIN_DISCOVERY_KEY=...SYNCTV_SECURITY_WEBAUTHN_ENUMERATION_KEY=...SYNCTV_FILE_UPLOAD_TOKEN_SECRET=...Start and verify:
docker compose configdocker compose up -dcurl -fsS http://localhost:8081/health/readyUse the YAML templates below for bare-metal, Kubernetes, or custom secret files.
Use _file keys for secrets whenever possible.
logging: level: "info" format: "text" output: stdout color: auto
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" logging: level: "info" format: "text" output: stdout color: auto
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" compression: "zstd" s3_public: type: "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" totp_encryption_key_file: "/run/secrets/totp_encryption_key" email_outbox_encryption_key_file: "/run/secrets/email_outbox_encryption_key" opaque_server_setup_secret_file: "/run/secrets/opaque_server_setup_secret" proxy_signing_key_file: "/run/secrets/proxy_signing_key" media_swarm_signing_key_file: "/run/secrets/media_swarm_signing_key" provider_session_encryption_key_file: "/run/secrets/provider_session_encryption_key" login_discovery_key_file: "/run/secrets/login_discovery_key" webauthn_enumeration_key_file: "/run/secrets/webauthn_enumeration_key" 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"
health: enabled: true host: "0.0.0.0" port: 8081 logging: level: "info" format: "text" output: stdout color: auto
metrics: enabled: true host: "0.0.0.0" port: 9090 auth: mode: "bearer_token" bearer_token_file: "/run/secrets/metrics_bearer_token" logging: level: "warn" format: "text" output: stdout color: autoThis skeleton does not enable clustering, WebAuthn, OAuth2, SMTP, media provider instances, file-backed HLS storage, or file-backed proxy slice cache. Add those only when the corresponding feature is required.
This template covers all static configuration fields in the current version. Provider fields only configure local built-in provider adapters in this process; remote provider instances are persisted through the management API/CLI. See Media Providers.
The template lists both inline secret fields and *_file fields to show hierarchy. Production configuration should use *_file or environment injection.
logging: level: "info" format: "text" output: stdout color: auto
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 logging: level: "info" format: "text" output: stdout color: auto
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" totp_encryption_key: "" totp_encryption_key_file: "/run/secrets/totp_encryption_key" email_outbox_encryption_key: "" email_outbox_encryption_key_file: "/run/secrets/email_outbox_encryption_key" opaque_server_setup_secret: "" opaque_server_setup_secret_file: "/run/secrets/opaque_server_setup_secret" proxy_signing_key: "" proxy_signing_key_file: "/run/secrets/proxy_signing_key" media_swarm_signing_key: "" media_swarm_signing_key_file: "/run/secrets/media_swarm_signing_key" provider_session_encryption_key: "" provider_session_encryption_key_file: "/run/secrets/provider_session_encryption_key" login_discovery_key: "" login_discovery_key_file: "/run/secrets/login_discovery_key" webauthn_enumeration_key: "" webauthn_enumeration_key_file: "/run/secrets/webauthn_enumeration_key" ssrf: enabled: false allow_private_network_targets: false allowed_hosts: [] allowed_ip_ranges: []
data_dir: "/var/lib/synctv"
health: enabled: true host: "127.0.0.1" port: 8081 logging: level: "info" format: "text" output: stdout color: auto
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 logging: level: "warn" format: "text" output: stdout color: auto
management: enabled: true transport: "unix" port: 50052 unix_socket_path: "run/synctv.sock" auth_token: "" enable_reflection: false logging: level: "info" format: "text" output: stdout color: auto
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
livestream: rtmp_port: 1935 public_rtmp_host: "stream.example.com" logging: level: "info" format: "text" output: stdout color: auto 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_storage: type: "memory" memory_max_mb: 0 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" 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" apple_app_ids: - "ABCDE12345.org.example.app" android_apps: - package_name: "org.example.app" sha256_cert_fingerprints: - "AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99" allow_subdomains: false allow_any_port: false timeout_seconds: 300
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: false logging: level: "info" format: "text" output: stdout color: auto
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 host: "0.0.0.0" port: 50051 advertise_host: "" advertise_port: 0 logging: level: "warn" format: "text" output: stdout color: auto 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
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 Form and Split Form
Section titled “URL Form and Split Form”database and redis both support full URLs and split fields. Prefer url_file in production because it minimizes credential exposure.
Database URL form:
database: url_file: "/run/secrets/database_url"Database split form:
database: host: "postgres.example.com" port: 5432 username: "synctv" password_file: "/run/secrets/database_password" name: "synctv"Redis URL form:
redis: url_file: "/run/secrets/redis_url"Redis Sentinel form:
Sentinel cannot be combined with cluster.enabled=true. For clustered deployments, use a stable single Redis endpoint, managed Redis, or a platform that guarantees stable connection semantics.
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"Render the Complete Effective Config
Section titled “Render the Complete Effective Config”synctv config show --output yamlWith an explicit config file:
synctv --config /etc/synctv/synctv.yaml config show --output yamlTOML output:
synctv --config /etc/synctv/synctv.yaml config show --output tomlValidate Before Deployment
Section titled “Validate Before Deployment”synctv --config /etc/synctv/synctv.yaml config validateValidation checks required secrets, cluster Redis requirements, TCP management authentication, CORS origins, gRPC message size, WebAuthn origins, and path resolution.