# Deploy {#deploy}


How to run `icey-server` in production. Pick the deployment method that fits your infrastructure.







**Docker Compose**

```yaml
version: "3.8"
services:
  icey:
    image: 0state/icey-server:0.1.1
    network_mode: host
    environment:
      ICEY_MODE: stream
      ICEY_SOURCE: /app/media/video.mp4
      ICEY_LOOP: 1
    volumes:
      - ./media:/app/media:ro
      - ./recordings:/app/recordings
    restart: unless-stopped
```



> [!NOTE]
> `network_mode: host` is the simplest path. It avoids port mapping and NAT complications. If you need bridge networking, you must publish ports 4500 and 3478 and set `ICEY_TURN_EXTERNAL_IP` to the host's public IP.





> [!NOTE]
> The published Docker image is environment-driven (`ICEY_MODE`, `ICEY_SOURCE`, `ICEY_LOOP`, `ICEY_TURN_EXTERNAL_IP`). For config-file-driven bring-up, use a native `icey-server` install or the repo-backed `auscaster/icey-server` source path.









**systemd**

Create `/etc/systemd/system/icey-server.service`:

```ini
[Unit]
Description=icey-server media runtime
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/icey-server \
  --config /etc/icey-server/config.json
Restart=on-failure
RestartSec=5
User=icey
Group=icey
WorkingDirectory=/var/lib/icey-server

[Install]
WantedBy=multi-user.target
```

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now icey-server
sudo journalctl -u icey-server -f
```







**Kubernetes**

Minimal deployment. Adjust resource limits and storage for your workload.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: icey-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: icey-server
  template:
    metadata:
      labels:
        app: icey-server
    spec:
      containers:
      - name: icey-server
        image: 0state/icey-server:0.1.1
        ports:
        - containerPort: 4500
          name: http
        - containerPort: 3478
          name: turn
          protocol: UDP
        env:
        - name: ICEY_MODE
          value: relay
        livenessProbe:
          httpGet:
            path: /api/health
            port: 4500
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /api/ready
            port: 4500
          periodSeconds: 5
```



> [!WARNING]
> WebRTC and TURN require UDP. If your cluster uses a network policy that blocks UDP, TURN relay will not work. You may need `hostNetwork: true` or a UDP-capable load balancer.








## Bring-Up Order {#bring-up-order}


If you are deploying for the first time, validate each layer before adding the next.



1. **Stream mode, local file, no TURN**

   ```bash
   icey-server --source ./demo.mp4 --no-turn
   ```
   
   This validates HTTP serving, Symple signalling, and the server-to-browser media path. If the browser cannot see the server peer, you have a signalling problem. If video does not play, you have a media path problem. Neither involves TURN.

2. **Record mode**

   ```bash
   icey-server --mode record --record-dir ./recordings --no-turn
   ```
   
   This validates the browser-to-server media path. Grant camera access in the browser, click Call, and check that MP4 files appear in the recording directory.

3. **Relay mode**

   ```bash
   icey-server --mode relay --no-turn
   ```
   
   This validates the full bidirectional path. First browser becomes the source, second browser becomes the viewer.

4. **Enable TURN**

   Remove `--no-turn`. Set `--turn-external-ip` to your public IP. Test from a browser outside your local network.
   
   If relay fails under NAT, check the [TURN deployment guide](https://auscaster.com/icey/docs/guides/operate/turn.md) before touching the media code.




## Pre-Flight Checklist {#pre-flight-checklist}


Before starting the server in any environment:

- OpenSSL is installed and discoverable by the binary
- FFmpeg is installed and discoverable by the binary
- `--web-root` points at the actual built `dist/` directory (or the Docker image includes it)
- `--record-dir` is writable if you use record mode
- `--source` points to a real file, device, or reachable RTSP URL if you use stream mode
- TURN port (3478) is reachable from outside if you expect relay through NATs


## Ports And Firewalls {#ports-and-firewalls}


| Service | Port | Protocol | Required |
| --- | --- | --- | --- |
| HTTP / WebSocket | 4500 | TCP | Always |
| TURN relay | 3478 | UDP + TCP | Unless `--no-turn` |

If you are behind a firewall, both ports must be reachable from the browser. TURN uses UDP by default with TCP fallback.


## TLS {#tls}


See the [TLS guide](https://auscaster.com/icey/docs/guides/operate/tls.md) for HTTPS and WSS termination options.
