Skip to content

Production deployment

The included Compose profile runs Kite as a non-root user, stores ACME state in a persistent volume, exposes the control plane only on host loopback, and serves HTTP/1.1, HTTP/2, and HTTP/3 on the standard public HTTPS port.

Prerequisites

  • A Linux host with Docker Engine and the Compose plugin.
  • An A or AAAA record for the radio hostname pointing at that host.
  • Inbound TCP port 80 and inbound TCP and UDP port 443 allowed by the host and provider firewalls.
  • No other process bound to public ports 80 or 443.

Configure

cp kite.production.example.yaml kite.production.yaml
cp .env.example .env
chmod 600 .env

Edit kite.production.yaml and replace radio.example.com and admin@example.com. Edit .env and generate independent long random values for KITE_SOURCE_PASSWORD and KITE_ADMIN_TOKEN. Do not commit either copied file.

Validate and start the service:

docker compose -f compose.production.yaml run --rm kite validate -config /etc/kite/kite.yaml
docker compose -f compose.production.yaml up -d --build
docker compose -f compose.production.yaml ps
docker compose -f compose.production.yaml logs -f kite

The first public TLS connection triggers certificate issuance. The ACME HTTP-01 challenge is served through public TCP port 80. Certificate state survives container replacement in the kite-data volume.

Verify

curl https://radio.example.com/status-json.xsl
curl http://127.0.0.1:9090/healthz
curl http://127.0.0.1:9090/readyz
curl -H "Authorization: Bearer $KITE_ADMIN_TOKEN" \
  http://127.0.0.1:9090/api/v1/mounts

Prometheus can scrape http://127.0.0.1:9090/metrics locally. If Prometheus runs in another container, attach both services to a private Docker network rather than publishing the admin listener publicly.

Ready-to-load alert rules are in deploy/prometheus/kite-alerts.yml. Change the job="kite" selector in KiteNotReady if your Prometheus scrape job uses another name. The source-disconnected alert is intentionally a warning because a configured fallback may keep listeners online.

BUTT

Configure an IceCast server with:

  • Address: radio.example.com
  • Port: 443
  • User: source
  • Password: the value of KITE_SOURCE_PASSWORD
  • Mountpoint: /radio
  • SSL/TLS: enabled
  • Codec: MP3 at the bitrate declared for the mount

Listeners use https://radio.example.com/radio. UDP 443 enables HTTP/3 where the client supports it; TCP 443 remains available for every normal HTTP client.

Updates and backup

docker compose -f compose.production.yaml build --pull
docker compose -f compose.production.yaml up -d
./scripts/backup-kite-data.sh backups/kite-data-$(date -u +%Y%m%dT%H%M%SZ).tar.gz

The backup contains ACME account and certificate material and must be protected like any other credential.

To restore, stop the service first and use the explicit confirmation prompt:

docker compose -f compose.production.yaml down
./scripts/restore-kite-data.sh backups/kite-data-20260824T120000Z.tar.gz
docker compose -f compose.production.yaml up -d

Set KITE_DATA_VOLUME when the Compose project name is not the default, for example KITE_DATA_VOLUME=myproject_kite-data.