TL;DR
I rebuilt my one-click Windows monitoring script for Elastic Stack 9.5.5, replacing Winlogbeat with a Fleet-managed Elastic Agent. One PowerShell command deploys Elasticsearch, Kibana, Fleet Server, Logstash and Grafana 13.3.0 in Docker, then installs and enrolls Elastic Agent on the Windows host. This post walks through the design and the problems I hit, which are worth knowing about if you automate Fleet yourself.
This is the follow-up to my earlier ELK + Grafana + Winlogbeat post.
What you get
| Component | Version | Role |
|---|---|---|
| Elasticsearch | 9.5.5 | Storage and search |
| Kibana | 9.5.5 | Search, Security, Observability and Fleet UI |
| Fleet Server | 9.5.5 (container) | Distributes policies to agents |
| Elastic Agent | 9.5.5 (Windows host) | Collects event logs and host metrics |
| Logstash | 9.5.5 | Optional Beats input |
| Grafana | 13.3.0 | Dashboards |
It targets Docker Engine 29.x with Compose v2. A second mode enrolls additional Windows machines into the same Fleet Server.
Architecture
Windows host Docker (Compose v2) +-------------------+ +-----------------------------------+ | Elastic Agent | -- :8220 --> | Fleet Server | | (Fleet-managed) | -- :9200 --> | Elasticsearch <-- Kibana (:5601) | +-------------------+ | Logstash (:5044) Grafana (:3000) | +-----------------------------------+ Agents check in with Fleet Server on 8220 for policies and send data straight to Elasticsearch on 9200. Logstash is not in that path.
Deployment order
Fleet has dependencies that must exist in the right order, so the script is sequential:
- Start Elasticsearch alone.
- Set the
kibana_systemandlogstash_systempasswords and create a Fleet Server service token. - Start Kibana with a generated
kibana.ymlthat preconfigures Fleet. - Call the Fleet setup API and wait until the
fleet_server,systemandwindowsintegrations exist. - Start the Fleet Server container and wait for a stable
HEALTHYstatus. - Start Logstash and Grafana.
- Fetch an enrollment token from the Kibana API, then download, verify and install Elastic Agent.
Preconfiguring Fleet in kibana.yml
Declaring Fleet in kibana.yml removes manual UI setup. A trimmed version:
xpack.fleet.packages: - name: fleet_server version: latest - name: system version: latest - name: windows version: latest xpack.fleet.fleetServerHosts: - id: default-fleet-server name: Default Fleet Server is_default: true host_urls: ["http://HOST_IP:8220"] xpack.fleet.outputs: - id: default-es-output name: Default type: elasticsearch is_default: true is_default_monitoring: true hosts: ["http://HOST_IP:9200"] xpack.fleet.agentPolicies: - name: Fleet Server Policy id: fleet-server-policy is_default_fleet_server: true package_policies: - name: fleet_server-1 package: name: fleet_server - name: Windows Endpoints id: windows-policy package_policies: - name: system-1 package: name: system - name: windows-1 package: name: windows HOST_IP is the address remote agents will use, so the script detects your default-route IP and asks you to confirm it.
The Fleet Server container
fleet-server: image: docker.elastic.co/elastic-agent/elastic-agent:${ELASTIC_VERSION} user: root environment: - FLEET_SERVER_ENABLE=true - FLEET_SERVER_ELASTICSEARCH_HOST=http://elasticsearch:9200 - FLEET_SERVER_SERVICE_TOKEN=${FLEET_SERVER_SERVICE_TOKEN} - FLEET_SERVER_POLICY_ID=fleet-server-policy - FLEET_SERVER_INSECURE_HTTP=true - FLEET_SERVER_HOST=0.0.0.0 - FLEET_SERVER_PORT=8220 - FLEET_URL=http://fleet-server:8220 - FLEET_INSECURE=true - FLEET_SERVER_TIMEOUT=10m ports: - "8220:8220" Gotchas I hit
| Symptom | Cause | Fix |
|---|---|---|
Empty reply from localhost:8220 | Fleet Server on plain HTTP binds to localhost inside the container | FLEET_SERVER_HOST=0.0.0.0 |
| Fleet cannot create API keys | API keys are disabled by default when HTTP TLS is off | xpack.security.authc.api_key.enabled=true on Elasticsearch |
| Elasticsearch container never healthy | Health check was unauthenticated while security is on | Use curl -u elastic:... in the health check |
Fleet Server loops on Waiting on policy with Fleet Server integration | It started before Kibana deployed the policy | Start it only after the policy and integrations exist, and re-run the idempotent POST /api/fleet/setup |
| Fleet Server component stops after policy reassignment | The Windows policy was marked as the default policy | Do not set is_default on the Windows policy |
process_cluster_event_timeout_exception during integration install | Slow storage: data was bind-mounted from a OneDrive folder | Use Docker named volumes for Elasticsearch and Grafana data |
| Agent download fails or the zip is corrupt | Slow progress bar and dropped connections | curl.exe -C - with retries, plus SHA-512 verification |
| Logstash Beats input rejects config | ssl option removed in Logstash 9 | Use ssl_enabled => false |
docker-compose not found | Docker 29 ships Compose v2 only | Use docker compose |
A note on downloads
The agent package is large, and Windows PowerShell's progress bar noticeably slows Invoke-WebRequest. The script downloads with resumable curl.exe, retries on failure, checks the checksum published next to the artifact, and caches the file in C:\ProgramData\elk-setup-cache. There is no overall timeout, so slow connections just take longer. If you already have the zip, pass it directly:
.\oneclick-setup-fleet.ps1 -AgentZipPath "C:\path\to\elastic-agent-9.5.5-windows-x86_64.zip" Running it
Requirements: Windows, Docker Desktop, PowerShell as Administrator, and about 12 GB of RAM or more.
.\oneclick-setup-fleet.ps1 -CleanInstall Choose 1 for the full stack or 2 to enroll another machine. Keep the project folder out of OneDrive or other synced folders.
Verify it
docker compose ps curl http://localhost:8220/api/status The Fleet Server should report HEALTHY. Then open Kibana at http://localhost:5601, sign in as elastic (the generated password is printed at the end and stored in .env) and check Fleet > Agents.
Limitations
- Lab setup, not production. Everything uses plain HTTP and Fleet runs in insecure mode. Add TLS before exposing it.
- Elastic's AI features are not included, since they need a subscription.
- Elastic Defend is not automated. Add it to the Windows policy in Fleet if you want it.
- Prebuilt detection rules are installed from Security > Rules once the stack is stable.
Source
Full script: Windows-ELK-Monitoring-Stack
Questions, issues and pull requests are welcome. If you automate Fleet differently, I would love to compare notes in the comments.
