> ## Documentation Index
> Fetch the complete documentation index at: https://suvera.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Daemon Threads Sample Application

> Run a supervised background daemon with Winter Boot that watches an inbox directory and processes new files as they arrive.

Build an app with a supervised background daemon that watches an inbox directory and processes new files as they arrive. You use the `#[DaemonThread]` annotation on a `ServerWorkerProcess` subclass. The framework starts the daemon with your app and restarts it if it crashes, so you never wire up your own process supervisor.

Use a daemon for work that must always be running — watching a directory, polling a queue, or tailing a stream. Use [scheduled tasks](/examples/scheduler-app) instead when the work runs on a fixed cadence.

## Prerequisites

You need PHP 8.5 or later with the `swoole` and `pcntl` extensions. No external services are needed.

## Project structure

The sample uses this layout:

```
daemon/
├── bin/
│   └── application.php              # Application entry point
├── config/
│   └── application.yml              # Winter Boot application config
├── src/
│   ├── DaemonSampleApplication.php  # Main application class
│   └── daemon/
│       └── InboxWatcherDaemon.php   # Daemon thread implementation
└── composer.json                    # Dependencies
```

## Install dependencies

Require the framework package:

```bash theme={null}
composer require suvera/winter-boot
```

## Source files

Switch between the source files. Each tab shows the exact file from the sample.

<Tabs>
  <Tab title="DaemonSampleApplication.php">
    The single entry point. It points Winter Boot at the `config` directory and at the sample namespace.

    ```php theme={null}
    <?php

    namespace dev\example;

    use dev\winterframework\stereotype\WinterBootApplication;

    #[WinterBootApplication(
        configDirectory: [__DIR__ . "/../config"],
        scanNamespaces: [
            ['dev\\example', __DIR__ . '']
        ]
    )]
    class DaemonSampleApplication {

        public static function main(): void {
            $winterApp = new \dev\winterframework\core\app\WinterWebSwooleApplication();
            $winterApp->run(self::class);
        }
    }
    ```
  </Tab>

  <Tab title="InboxWatcherDaemon.php">
    The daemon. Every 5 seconds it scans `/tmp/inbox` for `.txt` files, logs each file's line count, and moves processed files to `/tmp/processed`. The loop never exits — the process lives as long as `run()` runs, and the framework restarts it on failure.

    ```php theme={null}
    <?php

    namespace dev\example\daemon;

    use dev\winterframework\io\process\ProcessType;
    use dev\winterframework\io\process\ServerWorkerProcess;
    use dev\winterframework\stereotype\cli\DaemonThread;

    #[DaemonThread(
        name: 'inbox-watcher',
        coreSize: 1
    )]
    class InboxWatcherDaemon extends ServerWorkerProcess {

        private const INBOX_DIR = '/tmp/inbox';
        private const PROCESSED_DIR = '/tmp/processed';

        public function getProcessType(): int {
            return ProcessType::OTHER;
        }

        public function getProcessId(): string {
            return 'inbox-watcher';
        }

        protected function run(): void {
            @mkdir(self::INBOX_DIR, 0777, true);
            @mkdir(self::PROCESSED_DIR, 0777, true);

            self::logInfo('InboxWatcherDaemon started, watching ' . self::INBOX_DIR);

            while (1) {
                $this->processInbox();

                // Sleep 5 seconds without blocking the event loop
                \Co::sleep(5);
            }
        }

        private function processInbox(): void {
            $files = glob(self::INBOX_DIR . '/*.txt') ?: [];

            foreach ($files as $file) {
                $lines = count(file($file, FILE_IGNORE_NEW_LINES));
                $name = basename($file);

                self::logInfo("Processing {$name}: {$lines} lines");

                rename($file, self::PROCESSED_DIR . '/' . $name);

                self::logInfo("Moved {$name} to processed");
            }
        }
    }
    ```

    Three members are mandatory: `getProcessType()` returns the process role, `getProcessId()` returns a unique id used by the supervisor, and `run()` holds the main loop. Always sleep with `\Co::sleep()` instead of `sleep()` so you never block the Swoole event loop.
  </Tab>

  <Tab title="bin/application.php">
    The launch script. It loads the Composer autoloader and starts the application.

    ```php theme={null}
    <?php

    use dev\example\DaemonSampleApplication;

    require_once(dirname(__DIR__) . '/vendor/autoload.php');

    DaemonSampleApplication::main();
    ```
  </Tab>

  <Tab title="composer.json">
    The sample declares the framework dependency with PSR-4 autoloading for its own namespace.

    ```json theme={null}
    {
        "name": "suvera/winter-boot-daemon-sample",
        "require": {
            "ext-pcntl": "*",
            "ext-swoole": "*",
            "suvera/winter-boot": "@dev"
        },
        "autoload": {
            "psr-4": {
                "dev\\example\\": "src/"
            }
        }
    }
    ```

    The runnable sample in `winter-boot-samples` adds a local `path` repository for `winter-boot` so it resolves from a sibling checkout. You do not need that entry when you install the released package from Packagist.
  </Tab>
</Tabs>

## Configuration

No module is needed — the daemon is a core framework feature. The full sample `application.yml` sets only the server and app identity:

```yaml theme={null}
server:
    port: 8080
    address: 0.0.0.0
    context-path: /
winter:
    application:
        name: Daemon Threads Sample Application
        id: daemon-sample-app
        version: 1.0.0
```

See [Configuration](/configuration) for every `application.yml` key.

## Run the app

Start the app, drop a file into the inbox, and watch the daemon process it.

**1. Start the application:**

```bash theme={null}
composer install
php bin/application.php
```

The daemon creates `/tmp/inbox` and `/tmp/processed` on startup and begins scanning.

**2. Drop a file into the inbox:**

```bash theme={null}
echo -e "line one\nline two\nline three" > /tmp/inbox/report.txt
```

**3. Verify processing:**

```bash theme={null}
ls /tmp/processed
```

You see `report.txt` in the processed directory within 5 seconds, and the app log shows the line count:

```text theme={null}
Processing report.txt: 3 lines
Moved report.txt to processed
```

## Next steps

* Read [Daemon Threads](/async/daemon-threads) for process types, `coreSize` scaling, and supervision details.
* Read the [Scheduler example](/examples/scheduler-app) when your work runs on a fixed cadence instead of continuously.
