Full Configuration Example
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”Quick Start production Compose 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"]
# init-compose-env.sh 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=...Start and verify:
docker compose configdocker compose up -dcurl -fsS http://localhost:8080/health/readyUse the YAML templates below for bare-metal, Kubernetes, or custom secret files.
Use _file keys for secrets whenever possible.
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"This 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.
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 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.
Continue Reading
Section titled “Continue Reading”- Configuration Index: all fields, types, defaults, and detail pages.
- Environment Variables: supported
SYNCTV_*variables. - How Configuration Works: precedence, search paths, secret files, and
data_dirpath resolution.