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:

  1. Start Elasticsearch alone.
  2. Set the kibana_system and logstash_system passwords and create a Fleet Server service token.
  3. Start Kibana with a generated kibana.yml that preconfigures Fleet.
  4. Call the Fleet setup API and wait until the fleet_server, system and windows integrations exist.
  5. Start the Fleet Server container and wait for a stable HEALTHY status.
  6. Start Logstash and Grafana.
  7. 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.