# Flare daemon


The [Flare daemon](https://github.com/spatie/flare-daemon) is a long-running PHP process that accepts errors, traces, and logs from your application over a local HTTP connection and forwards them to Flare asynchronously. With the daemon running, Flare delivery is no longer part of the critical path of your request, which keeps response times stable even when the application produces a lot of telemetry.

## Why use the daemon

By default, the Flare client posts each report straight to Flare's ingress over HTTP. With the daemon, your application posts to a long-running process on the same host instead. The daemon buffers payloads and forwards them to Flare on its own schedule, so the application is free as soon as the local hop completes. If the daemon is unreachable for any reason, the `DaemonSender` falls back to direct HTTP delivery automatically, so a daemon outage degrades performance but never drops telemetry.

## Where the daemon should run

The daemon should sit as close to your application as possible. Ideally that means running it on the same host (server, instance, box, droplet, VM) so the client can post to `localhost`, which keeps the hop tiny and avoids any extra network legs between the application and the daemon.

If you run a single VPS, a long-lived `127.0.0.1:8787` daemon is all you need. If your fleet is spread across multiple machines, run one daemon per machine.

## Switching the sender

Whichever way you run the daemon, the application side is the same: swap the sender on your `FlareConfig` using `sendUsing()`:

```php
use Spatie\FlareClient\FlareConfig;
use Spatie\FlareClient\Senders\DaemonSender;

$config = FlareConfig::make('YOUR-API-KEY')
    ->useDefaults()
    ->sendUsing(DaemonSender::class, [
        'daemon_url' => getenv('FLARE_DAEMON_URL') ?: 'http://127.0.0.1:8787',
    ]);
```

The client will now post to the daemon when the lifecycle flushes, and the daemon takes care of the upstream delivery on its own.

### Sender options

| Key | Default | Description |
|---|---|---|
| `daemon_url` | `http://127.0.0.1:8787` | URL the `DaemonSender` posts to |
| `timeout` | `1` | Timeout (seconds) for normal payloads |
| `test_timeout` | `10` | Timeout (seconds) for test payloads (`X-Flare-Test`) |
| `fallback_sender_config` | `[]` | Config passed to the `CurlSender` used as fallback |

## Installing the daemon

The daemon binary ships with the Flare client. After installing `spatie/flare-client-php`, the binary is already available at `vendor/bin/flare-daemon`:

```bash
./vendor/bin/flare-daemon --version
```

Below are the recommended ways to run it, by hosting environment.

### Kubernetes

The Helm chart runs the daemon as a `DaemonSet` so each Kubernetes node has its own local instance. Install the chart from the OCI registry:

```bash
helm install flare-daemon \
    oci://ghcr.io/spatie/charts/flare-daemon \
    --namespace flare-daemon \
    --create-namespace
```

Helm pulls the latest published chart version when `--version` is omitted.

Application pods can then send daemon traffic to:

```text
http://flare-daemon.flare-daemon.svc.cluster.local:8787
```

The chart defaults to `service.internalTrafficPolicy=Local`, so application pods always hit the daemon on their own node. If a node-local daemon is unavailable, the Flare client falls back to direct delivery.

In your application's environment, set `FLARE_DAEMON_URL` to that in-cluster URL so the sender resolves to the right Service.

### Docker

A container image is published to GitHub Container Registry as `ghcr.io/spatie/flare-daemon`. The simplest standalone invocation:

```bash
docker run -d --name flare-daemon -p 8787:8787 ghcr.io/spatie/flare-daemon
```

If your application is also containerised, run the daemon as a sibling service in your `docker-compose.yml` and point `FLARE_DAEMON_URL` at it:

```yaml
services:
    app:
        # your existing application service
        environment:
            FLARE_DAEMON_URL: http://flare-daemon:8787

    flare-daemon:
        image: ghcr.io/spatie/flare-daemon
        restart: unless-stopped
```

Keep both services on the same Compose network (and ideally the same host) so the hop stays local.

### Generic VPS / self-hosted

On any other Linux host, run the daemon as a long-lived process supervised by [Supervisor](http://supervisord.org/), [systemd](https://systemd.io/), or any other process manager:

```bash
./vendor/bin/flare-daemon
```

A minimal Supervisor program looks like this:

```ini
[program:flare-daemon]
command=/path/to/your/site/vendor/bin/flare-daemon
autostart=true
autorestart=true
user=www-data
```

By default the daemon listens on `127.0.0.1:8787` and forwards to `https://ingress.flareapp.io`. If you prefer a single-file deployment, the same release ships a PHAR you can run directly:

```bash
php daemon.phar
```

### Verbose mode

By default the daemon logs lifecycle events (started, stopped) and a periodic summary of forwarded payloads. Pass `--verbose` (or `-v`) to also log every individual payload at `DEBUG` level:

```bash
./vendor/bin/flare-daemon --verbose
```

```bash
docker run -d --name flare-daemon -p 8787:8787 ghcr.io/spatie/flare-daemon --verbose
```

## Configuration

The daemon itself is configured through environment variables on the process running it:

| Variable | Default | Description |
|---|---|---|
| `FLARE_DAEMON_LISTEN` | `127.0.0.1:8787` | Address the daemon listens on |

For the full reference, see the [daemon README](https://github.com/spatie/flare-daemon).

## Verifying it works

Start the daemon in verbose mode in one shell:

```bash
./vendor/bin/flare-daemon --verbose
```

In another shell, dispatch a test report from the client. With Flare's bundled tester:

```php
use Spatie\FlareClient\Support\Tester;

(new Tester($flare))->run();
```

You should see the payload accepted by the daemon and forwarded to Flare's ingress.

## Fallback behavior

If the daemon is unreachable when the client tries to send (process down, wrong URL, network issue), the `DaemonSender` automatically falls back to a `CurlSender` instance configured with `fallback_sender_config` and emits a warning to `STDERR`. This means a daemon outage just brings you back to the default direct delivery behavior. Telemetry is never lost because of the daemon.
