Core and Full can serve HTTPS directly. You can move every endpoint onto one TLS listener, or keep an HTTP playback port while putting the console and admin API on a separate HTTPS port. Lite does not support TLS.
Choose a listener mode
| Mode | Configuration | Best for |
|---|---|---|
| HTTPS everywhere | tls_enabled = true, no tls_listen |
Players trust the certificate, or every surface must use TLS |
| HTTPS console, HTTP playback | tls_enabled = true plus tls_listen |
The console needs a secure context but players reject self-signed certificates |
Serve everything over HTTPS
[server]
listen = "0.0.0.0:8080"
public_base_url = "https://kiln.example.com:8080"
tls_enabled = trueAfter a restart, listen accepts HTTPS only. public_base_url must be the HTTPS address players can actually reach, or generated playlists will carry the wrong scheme or port.
Split the console from playback
[server]
listen = "0.0.0.0:8080"
tls_listen = "0.0.0.0:8443"
public_base_url = "http://kiln.lan:8080"
tls_enabled = trueAfter a restart, open https://kiln.lan:8443/admin. The TLS port serves everything. The HTTP port retains only this compatibility surface:
/healthz,/readyz, and/metrics/v1/epg.xml,/v1/epg.xml.gz, and/v1/logo/{id}/p/{token}/playlist.m3uand/p/{token}/play/...- Public
/v1/playlist.m3uand/v1/play/...only whensecurity.play_require_auth = false
Other GET and HEAD requests on HTTP redirect to the TLS port. Writes, requests carrying Authorization, and requests carrying ?token= are never replayed through a redirect; they return 403 with tls_required.
tls_listen never rewrites public_base_url. In split mode, point the public base at http://<playback-host>:<listen-port> when players should use HTTP. Pointing it at the TLS listener keeps generated playback URLs on HTTPS.
Certificates
Set both tls_cert_file and tls_key_file to use your own PEM certificate:
[server]
tls_enabled = true
tls_cert_file = "/etc/kiln/tls/fullchain.pem"
tls_key_file = "/etc/kiln/tls/privkey.pem"With both empty, Kiln generates and reuses {data_dir}/tls/kiln.crt and kiln.key. The certificate covers localhost, current network interfaces, the public_base_url host, a concrete tls_listen host, and security.public_hosts. It is reissued when a required host is not covered or less than 30 days remain; removing a host does not replace an otherwise valid certificate. A CA certificate generated automatically by an older release is replaced once with a leaf certificate the first time automatic TLS loads after the upgrade.
Browsers normally report the generated certificate as untrusted. Use it only where clients already accept that certificate; configure a certificate issued for the deployment hostname when ordinary clients must trust the service. Keep kiln.key private.
Docker
A split listener needs both ports published:
services:
kiln:
image: ghcr.io/babywbx/kiln:core
ports:
- "8080:8080"
- "8443:8443"The built-in Core and Full image health checks try HTTPS first and then HTTP. A manual kiln -healthcheck verifies the TLS certificate; in split mode, probing http://127.0.0.1:8080/healthz avoids that dependency.
Troubleshooting
server.tls_listen must differ from server.listen: the listener addresses overlap. A wildcard and a concrete address on the same port overlap too, such as0.0.0.0:8080and127.0.0.1:8080; assign a separate TLS port.tls_cert_file and tls_key_file must be set together: only one half of the key pair is configured.403 tls_required: credentials were sent to the HTTP port. Use the TLS listener, or use a path-based playback key when a player must stay on HTTP.- A playlist has the wrong scheme or port: update the public base URL in Settings. In Core and Full, the config value and
KILN_PUBLIC_BASE_URLonly initialize this database setting when it does not exist yet. - The browser reports an untrusted certificate: use a certificate accepted by that browser and confirm the access hostname appears in its host list.
See Configuration reference for every key and Docker for ports and persistence.