Upgrading Wazuh on Ubuntu 24.04 should be routine. Refresh the repository, install the updated packages, restart the services, and move on.
But sometimes an upgrade does not fail loudly. It stumbles at the very end, in a way that makes the whole platform look broken even though most of it has already upgraded successfully.
That was the situation here.
The environment was upgraded from Wazuh 4.13.1 to 4.14.4 on Ubuntu 24.04, using a deployment that had originally been installed with the Wazuh installation assistant, not Docker.
The package upgrade downloaded and unpacked correctly. The dashboard came back. The indexer came back. Filebeat came back. But the process still stopped with an error because wazuh-manager did not complete its post-install step.
At first glance, it felt like a failed upgrade. In reality, it was much narrower than that.
The real issue was that wazuh-manager needed more time to start than the default systemd unit allowed. Once that was understood, the fix became simple and the upgrade completed cleanly.
The Original Installation Method Matters
This server had originally been installed using the Wazuh assistant script:
curl -sO https://packages.wazuh.com/4.13/wazuh-install.sh && sudo bash ./wazuh-install.sh -a
Code language: JavaScript (javascript)
That matters because once Wazuh is already installed, the correct way to upgrade it is not to re-run the installer script. The correct path is to upgrade the installed packages through APT.
In this case, the target versions were:
wazuh-manager→4.14.4-1wazuh-indexer→4.14.4-1wazuh-dashboard→4.14.4-1filebeat→7.10.2-2
Updating the Wazuh Repository on Ubuntu 24.04
The first step was to make sure the server used the current Wazuh 4.x repository.
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH \
| gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import
sudo chmod 644 /usr/share/keyrings/wazuh.gpg
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" \
| sudo tee /etc/apt/sources.list.d/wazuh.list
sudo apt-get update
Code language: JavaScript (javascript)
Then the upgrade itself was started with
sudo apt-get install wazuh-manager wazuh-indexer wazuh-dashboard filebeat
Code language: JavaScript (javascript)
At this point, the process looked healthy. Packages downloaded, unpacked, and started configuring. But at the end, the upgrade stopped at the most uncomfortable point possible, almost finished, yet still incomplete.
The package manager reported an error on wazuh-manager.
The Symptom That Makes the Upgrade Look Worse Than It Is
After the upgrade attempt, the package state told the story.
dpkg -l | egrep 'wazuh-manager|wazuh-indexer|wazuh-dashboard|filebeat'
Code language: JavaScript (javascript)
The result looked like this
ii filebeat
ii wazuh-dashboard
ii wazuh-indexer
iF wazuh-manager
That iF state is the important clue. It means the package is installed, but configuration failed.
This created a confusing situation. Three components were already upgraded, but the whole upgrade still could not be considered complete because wazuh-manager had not successfully finished its post-install phase.
At the same time, the dashboard still opened normally in the browser. Login worked. But once inside, the Wazuh app reported that no API was available.
That can be misleading when you first see it. The dashboard frontend may be alive, but the Wazuh Manager API behind it can still be unavailable.
Why the Dashboard Can Work While Wazuh Is Still Broken
This is one of the most deceptive parts of the process.
wazuh-dashboard can start independently and still present the login page, even when the Wazuh Manager service behind the API is not fully ready. So the ability to log in does not prove that the whole platform is healthy.
If the dashboard loads but complains that there is no API available, the first place to look is not the dashboard. It is wazuh-manager.
That is where the actual problem was hiding.
Checking the Manager Service Properly
The first useful command was:
sudo systemctl status wazuh-manager --no-pager -l
This showed something subtle but important. wazuh-manager was not crashing immediately. It was beginning to start, loading modules, spawning API-related Python processes, and then getting terminated by systemd before it had fully completed startup.
That distinction matters.
This was not a corrupted package or a fatal binary crash. It was a startup timing problem.
Inspecting the unit file made the reason much clearer:
sudo systemctl cat wazuh-manager
The default unit included:
[Service]
Type=forking
LimitNOFILE=65536
TimeoutSec=45
There it was. 45 seconds.
On a platform like Wazuh, after a version upgrade, that may simply not be enough time for wazuh-manager to complete initialization, especially when several internal processes need to come back up together.
The Real Fix, Increase the systemd Startup Timeout
Once the problem was understood, the solution was straightforward, let wazuh-manager have more time to start.
Create a persistent systemd override
sudo systemctl edit wazuh-manager
Then add the following:
[Service]
TimeoutStartSec=300
TimeoutStopSec=300
Save the file, then reload systemd
sudo systemctl daemon-reload
This creates an override file at
/etc/systemd/system/wazuh-manager.service.d/override.conf
You can verify it with
sudo systemctl cat wazuh-manager
Once the override is in place, it remains persistent across reboots, restarts, and future service starts.
Restarting the Manager After the Override
With the longer timeout applied, restarting the manager allowed it to complete startup properly
sudo systemctl restart wazuh-manager
sudo systemctl status wazuh-manager --no-pager -l
This time, the manager reached the state it had been trying to reach all along
Active: active (running)
Code language: HTTP (http)
And beneath it, all the expected internal Wazuh processes appeared
wazuh-authdwazuh-dbwazuh-execdwazuh-analysisdwazuh-syscheckdwazuh-remotedwazuh-logcollectorwazuh-monitordwazuh-modulesd- multiple
wazuh_apid.pyprocesses
At that point, the upgrade was no longer blocked by service startup. What remained was simply finishing the interrupted package configuration.
Completing the Upgrade Cleanly
Once wazuh-manager was actually running, the pending package configuration could be completed
sudo dpkg --configure -a
This is the step that finally clears the half-finished package state.
Checking package versions again showed the clean result:
dpkg -l | egrep 'wazuh-manager|wazuh-indexer|wazuh-dashboard|filebeat'
Code language: JavaScript (javascript)
Now the output showed
ii filebeat
ii wazuh-dashboard
ii wazuh-indexer
ii wazuh-manager
That ii status across all packages is what confirms the upgrade is fully complete.
Final Validation Steps
Once the package state was clean, I still wanted to verify the platform as a whole.
First, check that all services are active
systemctl is-active wazuh-manager wazuh-indexer wazuh-dashboard filebeat
The ideal result is
active
active
active
active
Then test the API locally
curl -k https://127.0.0.1:55000
Code language: JavaScript (javascript)
If the API responds and the dashboard no longer shows No API available to connect, then the platform is back in a healthy state.
The Small Lesson Hidden Inside This Upgrade
What made this upgrade frustrating was not that it failed completely. It was that it failed only partially.
That is often worse.
A full crash gives clarity. A partial success creates doubt. The dashboard works, but not really. The manager starts, but not completely. The packages are upgraded, but not cleanly. Everything looks almost fine, which makes troubleshooting slower than it should be.
In this case, the fix was not a reinstall, not a rollback, and not a deep rebuild of the stack. It was simply allowing wazuh-manager enough time to finish starting, then letting dpkg complete the unfinished package configuration.
Sometimes that is all a stubborn upgrade needs.
Command Summary
For quick reference, here is the complete flow that worked
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH \
| gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import
sudo chmod 644 /usr/share/keyrings/wazuh.gpg
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" \
| sudo tee /etc/apt/sources.list.d/wazuh.list
sudo apt-get update
sudo apt-get install wazuh-manager wazuh-indexer wazuh-dashboard filebeat
sudo systemctl edit wazuh-manager
sudo systemctl daemon-reload
sudo systemctl restart wazuh-manager
sudo dpkg --configure -a
dpkg -l | egrep 'wazuh-manager|wazuh-indexer|wazuh-dashboard|filebeat'
systemctl is-active wazuh-manager wazuh-indexer wazuh-dashboard filebeat
curl -k https://127.0.0.1:55000
Code language: PHP (php)
Closing Thought
Ubuntu 24.04 is stable, modern, and perfectly capable of running Wazuh well. But not every post-upgrade service startup will fit neatly into the timeout values inherited from a package unit file.
Sometimes the service is healthy, the platform is healthy, and the only thing missing is enough time.
In this case, once wazuh-manager was allowed to finish starting, everything else followed naturally.
That is what made this upgrade feel broken at first, yet ultimately turn out to be completely recoverable.