A Wazuh agent that will not connect fails in one of eight places, and you can narrow it down in about ten minutes. Read the state file on the endpoint, then the two log files, then test port 1517. Most cases end there: an enrolment password the agent does not have, a group that does not exist on the manager, a health probe aimed at the wrong URL path, or a clock that has drifted far enough to break the 60-second JWT. This page walks the tree in order, with the exact log paths and the error strings, most of them reproduced on a 5.0.0-beta5 lab rather than quoted from the documentation.

What you need

  • Shell access on the endpoint, and root or Administrator.
  • Shell access on the manager, or at least the manager log.
  • Manager API credentials if you want to check state through the API.

The Linux commands and every error string below were run on a 5.0.0-beta5 stack. The Windows paths, the Windows state file and the clock-skew evidence in step 8 came from a Windows 11 endpoint on the same stack. The macOS paths come from the documentation and were not exercised.

The four states, and what each one tells you

Read the state correctly before diagnosing anything. The manager tracks four, and they are not interchangeable.

State What actually happened
Never connected The agent enrolled and has a record, but has never established a connection.
Pending The manager received the initial connection request but authentication has not completed. Every agent passes through this state once per startup.
Active Connected and communicating.
Disconnected No keep-alive within agents_disconnection_time, default 15 minutes.

That table is the first fork in the tree. Never connected means enrolment worked and the connection did not, which points at the network path or the listener. Pending that never clears points at authentication: the key, the password, the certificate or the clock. Disconnected means it worked once and stopped, which is usually the network, a stopped service, or a duplicate agent stealing the identity.

Step 1: Read the state on the endpoint

The agent writes its own view of the connection to a state file. Start here, because it distinguishes “the agent is not running” from “the agent is running and cannot get through”.

sudo grep ^status /var/ossec/var/run/wazuh-agentd.state

The documented output on a healthy agent is status='connected'. On Windows the file has a different name and sits in the agent folder rather than under var/run:

Select-String -Path 'C:\Program Files (x86)\ossec-agent\wazuh-agent.state' -Pattern "^status"

Read the whole file at least once, because the Windows one carries counters the status line does not. From a healthy beta5 agent:

status='connected'
last_keepalive='2026-09-23 13:30:58'
msg_count='315'
msg_sent='0'
msg_buffer=''
dispatched='0'
discarded_duplicate='0'
failed='0'

last_keepalive is the field that tells you when it stopped, and msg_count tells you whether the agent is generating events at all, which separates “cannot reach the manager” from “has nothing to send”. Do not diagnose from msg_buffer or msg_sent: the file itself explains that the HTTPS accumulator reports buffer occupancy as a ladder rather than a count, and both read empty or zero on a working agent.

On macOS the file is at /Library/Ossec/var/run/wazuh-agentd.state.

If the agent is not running at all on Windows, the service is WazuhSvc, verified on beta5. Get-Service WazuhSvc and Start-Service WazuhSvc. Beta documentation pages that say Restart-Service -Name wazuh do not match the package.

The three documented values: connected means the manager acknowledged the connection, pending means the agent is waiting for that acknowledgement, and disconnected means no acknowledgement in the last 60 seconds, or a lost connection.

While you are on the endpoint, confirm the daemons are actually up:

/var/ossec/bin/wazuh-control status

A healthy agent on the beta5 lab, with the state file and key alongside it:

$ /var/ossec/bin/wazuh-control status
wazuh-modulesd is running...
wazuh-logcollector is running...
wazuh-syscheckd is running...
wazuh-agentd is running...
wazuh-execd is running...
$ grep ^status /var/ossec/var/run/wazuh-agentd.state
status='connected'
$ cat /var/ossec/etc/client.keys | cut -d" " -f1-3
001 lab-agent-container any
$ ls /var/ossec/bin
manage_agents wazuh-agentd wazuh-control wazuh-execd wazuh-logcollector wazuh-modulesd wazuh-syscheckd

Five daemons. If wazuh-agentd is missing from that list, you have a service problem, not a connectivity problem. An empty or absent client.keys means the agent never completed enrolment, which sends you to steps 6 and 7 rather than to the network.

Step 2: Check the manager’s view

Two things to look at. The daemons:

/var/wazuh-manager/bin/wazuh-manager-control status

On a beta5 stack this returns eight daemons:

wazuh-manager-clusterd is running...
wazuh-manager-modulesd is running...
wazuh-manager-monitord is running...
wazuh-manager-remoted is running...
wazuh-manager-analysisd is running...
wazuh-manager-db is running...
wazuh-manager-authd is running...
wazuh-manager-apid is running...

Count eight, not seven. wazuh-manager-monitord is listed among the removed daemons in the migration guide, and beta5 ships and starts it anyway, so a health check written from that guide will report a false failure. For this lesson the two that matter are remoted, without which no agent connects at all, and authd, without which no agent enrols.

Then the manager’s record of this specific agent, through the API:

TOKEN=$(curl -u <USER>:<PASSWORD> -k -X POST "https://<WAZUH_MANAGER_IP_ADDRESS>:55000/security/user/authenticate?raw=true")
curl -k -X GET "https://<WAZUH_MANAGER_IP>:55000/agents?pretty=true&agents_list=<WAZUH_AGENT_ID>" -H "Authorization: Bearer $TOKEN"

The response carries status, status_code, lastKeepAlive, version, group and group_config_status. lastKeepAlive is the number that tells you when it stopped, which is often more useful than the state word itself. The same information is in the dashboard under Agents management, Summary.

Step 3: Is the port reachable?

A Wazuh 5.0 agent needs outbound TCP to the manager on 1517. Test it from the endpoint, not from your laptop.

nc -zv <WAZUH_MANAGER_IP_ADDRESS> 1517 55000

The documented success output is a line per port in the form Connection to <WAZUH_MANAGER_IP_ADDRESS> port 1517 [tcp] succeeded!.

On Windows:

(new-object Net.Sockets.TcpClient).Connect("<WAZUH_MANAGER_IP>", 1517)

Success produces no output at all. Failure produces A connection attempt failed because the connected party did not properly respond after a period of time (...).

The HTTPS listener also answers an unauthenticated liveness probe, which is a better test because it proves the service is there and not just the socket. Get the path right, because the listener is mounted under the value of <global_prefix>, which ships as /wazuh-manager/:

curl -k https://<manager>:1517/wazuh-manager/

Verified on the beta5 lab, that returns {"status":"ok","module":"remoted"}. The same probe against the root returns {"error":"not_found"}, which is what a healthy manager says to a request for a path it does not serve. More than one person has declared an outage on the strength of that response.

A 4.x agent talking to a 5.0 manager needs 1514/TCP for communication and 1515/TCP for enrolment instead. Both are open on a stock manager. Listing the listening TCP ports on the beta5 lab manager returned 1514 1515 1516 1517 55000, plus one high ephemeral port: 1514 and 1515 for legacy agents, 1516 for the cluster, 1517 for 5.x agents, 55000 for the API.

Step 4: Read the <remote> block, and stop assuming the default

For a year this step read “check whether the listener is bound to loopback”, because the 5.0-beta enrolment requirements page says the 1517 and 1514 listeners bind to 127.0.0.1 and accept only local connections. That is not what the package does.

Verified on a 5.0.0-beta5 stack, the shipped <remote> block is:

<remote>
  <https>
    <port>1517</port>
    <bind_addr>0.0.0.0</bind_addr>
    <global_prefix>/wazuh-manager/</global_prefix>
    <certificate>etc/certs/remoted.pem</certificate>
    <key>etc/certs/remoted-key.pem</key>
  </https>

  <legacy>
    <enabled>yes</enabled>
    <port>1514</port>
    <protocol>tcp</protocol>
    <local_ip>0.0.0.0</local_ip>
    <queue_size>131072</queue_size>
  </legacy>

  <agents>
    <allow_higher_versions>no</allow_higher_versions>
  </agents>
</remote>

Both listeners are on every interface, and the legacy channel is enabled. So loopback binding is not the usual cause of “enrolled but never connected” on a stock install. Read your own file anyway, because someone may have edited it:

grep -A8 '<https>' /var/wazuh-manager/etc/wazuh-manager.conf

Three things in that block do cause real failures. <global_prefix> decides the URL path, so a monitoring check on / reports a dead manager that is fine. <port> has to match a non-default <endpoint> port on the agent. And <agents><allow_higher_versions> ships as no, so an agent newer than the manager is refused.

The manager log also reports the legacy listener state at startup, as either Legacy listener disabled ('<remote><legacy>' absent or disabled). or Listening on port <port>/<protocol> (secure)., which tells you whether a 4.x agent has anywhere to connect to.

Step 5: TLS and certificates

The HTTPS listener is fail-closed. The manager does not generate its own certificate and key; they are issued with the installation assistant’s certificate tool and deployed under etc/certs before first start. Without them the service refuses to start, with the documented error (1244): Invalid configuration at '/remote/https/certificate': file not found: …. A pair the service user cannot read stops remoted at startup with Cannot start the HTTPS agent listener: … instead.

authd and remoted share one pair in 5.0: etc/certs/remoted.pem and etc/certs/remoted-key.pem, with etc/certs/root-ca.pem as the certificate authority. A 4.x configuration pointing <auth><ssl_manager_cert> at authd.pem should be repointed or the override dropped.

On the agent side, TLS lives in <agent><ssl>. verification_mode defaults to none and accepts certificate, full and system. When it is certificate or full, certificate_authorities is required and the agent fails closed without it. The 4.x options server_ca_path, agent_certificate_path and agent_key_path are no longer read and are silently ignored with an INFO log line, which is a quiet way to lose a working configuration during an upgrade.

Client-certificate verification on the manager is separate, set with <remote><https><verification_mode>. If you raised it to full, the peer’s address has to appear in the certificate’s subject alternative name, so agents behind NAT will fail.

Step 6: The enrolment password

The installer sets <use_password>yes</use_password>, so password authentication is on unless you turned it off. The manager reads the password from /var/wazuh-manager/etc/authd.pass, generating a random one at first start if the file is absent:

sudo cat /var/wazuh-manager/etc/authd.pass

The agent needs the same value at /var/ossec/etc/authd.pass, readable by the account the agent daemon runs as:

echo "<PASSWORD>" | sudo tee /var/ossec/etc/authd.pass
sudo chown root:wazuh /var/ossec/etc/authd.pass && sudo chmod 640 /var/ossec/etc/authd.pass

On Windows the file sits beside the configuration at C:\Program Files (x86)\ossec-agent\authd.pass, and on macOS at /Library/Ossec/etc/authd.pass. A non-default location needs <enrollment><authorization_pass_path> set.

Without the file, or with the wrong value in it, the enrolment request is rejected. This is what that looks like on the agent, captured from a beta5 lab after deliberately writing a wrong password:

$ grep ^status /var/ossec/var/run/wazuh-agentd.state
status='pending'
$ grep agentd /var/ossec/logs/ossec.log | tail -8
2026/09/23 02:41:01 wazuh-agentd:https-client: INFO: TLS verification is DISABLED (verify_mode=none).
2026/09/23 02:41:01 wazuh-agentd: ERROR: Enrollment rejected by the manager: invalid or missing authentication. Invalid client authentication
2026/09/23 02:41:06 wazuh-agentd: INFO: Using password specified on file: etc/authd.pass
2026/09/23 02:41:06 wazuh-agentd:https-client: INFO: TLS verification is DISABLED (verify_mode=none).
2026/09/23 02:41:06 wazuh-agentd: ERROR: Enrollment rejected by the manager: invalid or missing authentication. Invalid client authentication
2026/09/23 02:41:16 wazuh-agentd: INFO: Using password specified on file: etc/authd.pass
2026/09/23 02:41:16 wazuh-agentd: ERROR: Enrollment rejected by the manager: invalid or missing authentication. Invalid client authentication

Note the shape of it as much as the string. The agent retries on a widening backoff, roughly 5 then 10 seconds, and the state file sits at pending the whole time rather than moving to never_connected, because no key was ever issued. The manager log stays silent for this case, which is a useful discriminator: a rejection with nothing in the manager log is an authentication rejection.

Two further details worth knowing: in a cluster the password is maintained on the master and distributed to workers, and a worker rejects enrolment until it has received the file; and if authd.pass exists on the manager but is empty or invalid, wazuh-manager-authd does not start at all.

Step 7: The group does not exist

This one is specific to Wazuh 5.0 and it catches nearly everyone migrating a fleet.

During enrolment the agent sends the groups declared in <enrollment><groups>. The manager validates them, and if a declared group does not exist, enrolment is rejected and the agent cannot connect. Reproduced on the beta5 lab with a group called does-not-exist, the agent log shows:

$ grep ^status /var/ossec/var/run/wazuh-agentd.state
status='pending'
$ grep agentd /var/ossec/logs/ossec.log | tail -8
2026/09/23 02:41:26 wazuh-agentd:https-client: INFO: TLS verification is DISABLED (verify_mode=none).
2026/09/23 02:41:26 wazuh-agentd: ERROR: Enrollment rejected by the manager: invalid request. Invalid Group(s) Name(s)
2026/09/23 02:41:31 wazuh-agentd: INFO: Using password specified on file: etc/authd.pass
2026/09/23 02:41:31 wazuh-agentd: ERROR: Enrollment rejected by the manager: invalid request. Invalid Group(s) Name(s)
2026/09/23 02:41:41 wazuh-agentd: ERROR: Enrollment rejected by the manager: invalid request. Invalid Group(s) Name(s)

The agent never names the group. The manager does, once per attempt:

2026/09/23 02:41:26 wazuh-manager-authd: ERROR: Invalid group: does-not-exist
2026/09/23 02:41:31 wazuh-manager-authd: ERROR: Invalid group: does-not-exist
2026/09/23 02:41:41 wazuh-manager-authd: ERROR: Invalid group: does-not-exist

So when an agent declares several groups, read the manager log. Note how similar the two failure shapes are on the agent side: same retry cadence, same pending state, and the only difference is invalid request against invalid or missing authentication. Read that clause carefully before you start rotating passwords.

Three ways out. Create the group and restart the agent:

/var/wazuh-manager/bin/agent_groups -a -g <GROUP_NAME> -q

Or create it through the API:

curl -k -X POST "https://<manager-ip>:55000/groups" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"group_id": "<group-name>"}'

Or remove the <groups> tag so the agent lands in default, then assign the group afterwards with PUT /agents/<agent-id>/group/<group-name>.

Step 8: Clock skew and the 60-second token

New in 5.0, and invisible if you do not know to look for it. The pre-shared key in client.keys is still the credential, but each request now carries a JSON Web Token signed with it, using HS256, with a 60-second lifetime and a fresh identifier per request. The migration guide is precise about the window: a token is accepted while the time since issue is within 60 seconds plus 30 seconds of tolerated skew, and it is never accepted more than 30 seconds before it was issued.

So the manager and its agents need roughly synchronised clocks. A skewed agent clock surfaces as 401 Invalid client authentication. The agent corrects itself from the manager’s Date header and retries, which means a modest drift self-heals and a large or drifting one does not.

This was reproduced on the beta5 lab with a Windows 11 virtual machine whose guest clock read the hardware clock as local time, putting it five and a half hours ahead of the manager. The agent enrolled anyway, and said exactly why:

2026/09/23 07:55:10 wazuh-agentd:https-client: INFO: https_client: clock skew of 19801 s detected against the manager's response (Date header) during enrollment; correcting the signing timestamp and retrying.
2026/09/23 07:55:10 wazuh-agent: INFO: Valid key received
2026/09/23 07:57:00 wazuh-agentd:https-client: INFO: https_client: clock skew of 19801 s detected against the manager's response (Date header); correcting the token timestamp for this and future requests.

That is the trap. The agent self-corrects, reaches active, and every dashboard says the endpoint is healthy. The manager log tells a different story, one line, at the same second:

2026/09/23 07:55:11 wazuh-manager-remoted:endpoints: WARNING: Rejected 1 request(s) in the last 90 s whose token fell outside the accepted time window (stale_token). Check clock synchronization between the agents and the manager; the window is set by 'jwt_max_age' and 'jwt_clock_skew' (remoted internal options).
2026/09/23 07:55:11 wazuh-manager-authd: INFO: Agent key generated for agent 'WIN11-LAB' (requested locally)

Three things to take from it. The rejection is real: the first request was thrown away, and only the corrected retry got through. The warning names the two knobs, jwt_max_age and jwt_clock_skew, both remoted internal options, which is where you would widen the window if you genuinely cannot fix a fleet’s clocks. And the warning is aggregated over 90 seconds, so one line can stand for one endpoint or for hundreds.

Fix the clock rather than the window. On a virtual machine the usual cause is the guest treating the hardware clock as local time; on Windows that is one registry value and a resync:

Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation' -Name RealTimeIsUniversal -Type DWord -Value 1
w32tm /resync /force

w32tm /resync answering The computer did not resync because no time data was available. means the endpoint has no reachable time source at all, which is the real finding. Afterwards the agent logs the correction in the other direction, clock skew of -19801 s detected, as it unwinds what it had been compensating for.

Check the clock on both ends before you start regenerating keys. If your endpoints have no time source, fix that first: it is a prerequisite for the protocol, not a nice-to-have.

One behaviour to know while reading logs: every credential failure, whether an unknown agent, a key mismatch, a disallowed address, a bad signature or a stale token, deliberately collapses into the same generic 401. The response never names the cause. The manager log does.

Step 9: Duplicate agents and key mismatch

Two endpoints presenting the same identity produce intermittent, confusing symptoms: an agent that flaps between active and disconnected as each host overwrites the other.

A name collision shows in the agent log during enrolment:

2022/01/26 08:59:10 wazuh-agentd: ERROR: Invalid agent name: localhost.localdomain (from manager)
2022/01/26 08:59:10 wazuh-agentd: ERROR: Unable to add agent (from manager)

Every agent needs a unique name. Without <enrollment><agent_name>, Wazuh uses the hostname, which is how golden images and cloned VMs produce this.

A key mismatch shows in the manager log instead:

2022/02/03 10:07:32 wazuh-remoted: WARNING: (1404): Authentication error. Wrong key or corrupt payload. Message received from agent '001' at 'any'.

Compare the agent’s key with the manager’s entry:

Endpoint client.keys location
Manager /var/wazuh-manager/etc/client.keys
Linux and Unix /var/ossec/etc/client.keys
macOS /Library/Ossec/etc/client.keys
Windows C:\Program Files (x86)\ossec-agent\client.keys

The documentation names the two usual causes: deleting agents with the highest IDs and letting the IDs be reused, and copying client.keys between hosts. The ip column is enforced, so a key issued for one address will not authenticate from another unless the column says any or covers it with a CIDR.

Re-enrolment over an existing record is governed by <auth><force>: by default it is allowed, a key mismatch triggers it, and an hour must have passed since both the disconnection and the original registration. If a re-enrolment is being refused, those four options are why. Separately, <purge> decides whether a removed record leaves a !-prefixed placeholder line in client.keys so the ID and name are not silently reused.

One more trap on a mixed-version fleet: an agent newer than the manager needs allow_higher_versions set to yes in both <auth><agents> and <remote><agents>. Set only one and the agent can get a key but cannot communicate, or the reverse.

Step 10: The disconnection timeouts

If agents are being marked disconnected that you believe are fine, the timer is in the <global> section of wazuh-manager.conf. In 5.0 that section has shrunk to two options, and verified on a beta5 stack, both of them ship in the default file:

<global>
  <agents_disconnection_time>15m</agents_disconnection_time>
  <agents_disconnection_alert_time>0</agents_disconnection_alert_time>
</global>

agents_disconnection_time is how long an agent may go without communication before the manager marks it disconnected. Default 15m, minimum 1s. That is the one you change.

agents_disconnection_alert_time is the survivor of an argument between two sources. The GitHub manager-configuration migration guide marks it removed, the beta reference page still lists it, and beta5 ships it in the default configuration with a value of 0. So it is present and valid today, and it is on its way out. Do not build anything on it: the alert path it fed is gone with the manager’s own alerting, and a disconnected-agent notification belongs on the indexer, as the notifications lesson shows.

The matching setting on the agent is <notify_time>, the interval between keep-alives, defaulting to 20 seconds. The reference gives the rule directly: keep it lower than the manager’s disconnection time so a keep-alive always arrives first. <time-reconnect> is the wait before a reconnection attempt, default 60 seconds, and should be higher than notify_time. Note the inconsistent punctuation, an underscore in one and a hyphen in the other.

Where every log file lives

Half of all agent troubleshooting is reading the right file.

What Path
Manager log /var/wazuh-manager/logs/wazuh-manager.log
Manager configuration /var/wazuh-manager/etc/wazuh-manager.conf
Manager keys /var/wazuh-manager/etc/client.keys
Enrolment password (manager) /var/wazuh-manager/etc/authd.pass
Agent log, Linux and Unix /var/ossec/logs/ossec.log
Agent log, macOS /Library/Ossec/logs/ossec.log
Agent log, Windows 64-bit C:\Program Files (x86)\ossec-agent\ossec.log
Agent log, Windows 32-bit C:\Program Files\ossec-agent\ossec.log
Agent state, Linux /var/ossec/var/run/wazuh-agentd.state
Agent state, Windows C:\Program Files (x86)\ossec-agent\wazuh-agent.state
Agent configuration, Linux /var/ossec/etc/ossec.conf

The full side-by-side against 4.x is in the ports, paths and log files reference.

Common errors

ERROR: Enrollment rejected by the manager: invalid request. Invalid Group(s) Name(s)

In the agent log, repeating. The agent declared a group in <enrollment><groups> that does not exist on the manager, and enrolment was rejected. The manager log names the group: wazuh-manager-authd: ERROR: Invalid group: <name>. Create the group on the manager, or drop the tag and assign the group afterwards, then restart the agent so it re-enrols. The agent state file stays at pending throughout.

ERROR: Enrollment rejected by the manager: invalid or missing authentication. Invalid client authentication

In the agent log, repeating, with nothing in the manager log. The enrolment password in the agent’s authd.pass is wrong or the file is missing while the manager has <use_password>yes</use_password>. Copy the manager’s value from /var/wazuh-manager/etc/authd.pass, check for stray whitespace, and restart the agent.

ERROR: Invalid agent name: localhost.localdomain (from manager)

Appears in the agent log with ERROR: Unable to add agent (from manager). The name is already taken, usually because two endpoints share a hostname. Set a unique <enrollment><agent_name>, or delete the stale record holding the name.

WARNING: (1404): Authentication error. Wrong key or corrupt payload. Message received from agent '001' at 'any'.

In the manager log. The agent’s key does not match the manager’s client.keys entry. Compare the two files and re-enrol the agent if they differ.

401 Invalid client authentication

Every credential failure on the HTTPS channel returns this, so it is not diagnostic on its own. Check clock synchronisation first, because the 60-second token with roughly 30 seconds of tolerated skew is the 5.0-specific cause. Then check the key, the ip column in client.keys, and the manager log for the real reason.

WARNING: Rejected 1 request(s) in the last 90 s whose token fell outside the accepted time window (stale_token).

In the manager log, from wazuh-manager-remoted:endpoints, aggregated over 90 seconds so one line may stand for many endpoints. The full message names the two options that set the window, jwt_max_age and jwt_clock_skew, both remoted internal options. An agent’s clock is far enough out that its signed token had expired on arrival.

The agent side of this is not an error: a 5.0 agent detects the skew from the manager’s Date header, logs clock skew of <n> s detected, corrects its signing timestamp and retries successfully. So the endpoint reaches active and nothing looks wrong from the dashboard. Fix the endpoint clock rather than widening the window, and treat this warning as the only visible symptom of a fleet with no working time source.

The computer did not resync because no time data was available.

From w32tm /resync on a Windows endpoint. There is no reachable time source configured, which is the underlying cause of the stale_token warning above rather than a separate problem. Point the endpoint at a time server, and on a virtual machine also set RealTimeIsUniversal so the guest stops reading the hardware clock as local time.

ERROR: Unable to read CA certificate file "/var/ossec/etc/rootCA.pem"

Followed by ERROR: Could not set up SSL connection! Check certification configuration. in the agent log. Manager identity verification is enabled but the CA bundle is not at the configured path. In 5.0 the agent’s TLS material is configured under <agent><ssl>.

(1244): Invalid configuration at '/remote/https/certificate': file not found: …

The manager refuses to start because the HTTPS listener’s certificate is missing. It is fail-closed by design. Deploy etc/certs/remoted.pem and etc/certs/remoted-key.pem, and check that the service user can read them, otherwise you get Cannot start the HTTPS agent listener: … instead.

Agents show Active but the count keeps dropping

Look for duplicates before anything else. Two hosts sharing an identity will alternate, and each transition looks like a disconnection. connection_overtake_time on the legacy listener, default 60 seconds, governs how quickly a new connection may replace an existing one for the same agent.

Everything is stuck in Pending

Pending means the connection request arrived and authentication did not complete. That is steps 5 through 8: certificates, enrolment password, group, clock. Work them in that order and read the manager log after each change.

Clear out what is genuinely dead

Once the broken agents are separated from the retired ones, delete the retired ones. Hundreds of never-connected records make every future triage slower.

curl -k -X DELETE "https://<WAZUH_MANAGER_IP>:55000/agents?pretty=true&older_than=21d&agents_list=all&status=never_connected,disconnected" -H "Authorization: Bearer $TOKEN"

Adjust older_than to your retirement policy, and run it on a schedule rather than once in anger.

Where to go next

The two reference pages behind most of this are the official agent enrolment troubleshooting page and the <remote> configuration reference. The token, the 401 semantics and the listener behaviour come from the Wazuh agent-manager protocol migration guide.

If you are here because a whole fleet stopped rather than one endpoint, read agent enrolment explained for the handshake itself and agent groups and centralised configuration for the group dependency that breaks migrations. A disconnected agent also silently stops feeding vulnerability detection, so check the agent state before you conclude the scanner is broken. Every error string used across this tutorial is collected in the Wazuh error messages index.

Next: Write a Custom Wazuh Decoder in YAML.

Files for this lesson

Every file this lesson refers to, exactly as used. Open a file to read it in the browser, or download the set as a zip. Download wazuh-agent-disconnected.zip. The full set for all lessons is at wazuh-tutorial-files.zip.

Frequently asked questions

What does never connected mean in Wazuh?

The agent enrolled successfully and the manager has a record and a key for it, but the agent has never established a connection. Enrolment and connection are separate steps. Never connected almost always means the agent cannot reach the manager on its communication port, or the listener is not accepting remote connections.

Which port does a Wazuh 5.0 agent need?

1517/TCP over HTTPS, used for both enrolment and ongoing communication. Wazuh 4.x agents use 1514/TCP for communication and 1515/TCP for enrolment. Verified on 5.0.0-beta5: the shipped manager configuration enables the legacy listener, so a stock manager listens on 1514, 1515, 1516, 1517 and 55000. Port 55000/TCP is the manager API, needed only if you enrol through the API.

How long before Wazuh marks an agent disconnected?

Fifteen minutes by default. The manager marks an agent disconnected when it receives no keep-alive within the agents_disconnection_time interval in the global section of wazuh-manager.conf, whose default is 15m. The default file also still ships agents_disconnection_alert_time set to 0, although the manager-configuration migration guide marks that option for removal, so plan to build disconnected-agent notifications on the indexer.

Where are the Wazuh agent logs?

On Linux and Unix at /var/ossec/logs/ossec.log, on macOS at /Library/Ossec/logs/ossec.log, and on 64-bit Windows at C:\Program Files (x86)\ossec-agent\ossec.log. The manager log is separate, at /var/wazuh-manager/logs/wazuh-manager.log. When an agent misbehaves, read both.

Why does my Wazuh agent keep getting 401 errors?

Every credential failure on the 5.0 HTTPS channel collapses into a generic 401, so the cause is not in the response. A new cause in 5.0 is clock skew: each request carries a JWT with a 60-second lifetime and about 30 seconds of tolerated skew, so an endpoint whose clock has drifted cannot authenticate. Check time synchronisation, then check the key.