Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Fix High CPU Usage on Ubuntu

By Kokil Thapa | Last reviewed: September 2026

A server that suddenly feels sluggish often has one root cause: a process eating CPU cycles. To fix high CPU usage on Ubuntu, you start by identifying the offender, then apply a targeted fix—not a blind reboot. I've spent years on Ubuntu 22/24 boxes running Laravel, WordPress, and MySQL. CPU spikes show up during bad deploys, runaway cron jobs, and traffic surges. This guide walks through the same workflow I use on production servers, from first symptom to stable load. For deeper memory and I/O context, see our guide on diagnosing high CPU and memory on Linux servers.

How do you identify what's causing high CPU on Ubuntu?

Start with live process data. The kernel reports CPU time per process through /proc. Your job is to read that data quickly before the box becomes unresponsive.

Step 1: Check load average and top consumers

Run these commands over SSH as a user with sudo access:

uptime
top -b -n 1 | head -20
ps aux --sort=-%cpu | head -15

uptime shows load averages for 1, 5, and 15 minutes. On a 4-core server, sustained load above 4.0 means every core is busy. A short spike during a deploy is normal. Sustained high load is not.

Install and use htop if it is not already present:

sudo apt update
sudo apt install -y htop
htop

Press F6 in htop to sort by CPU. Press F5 for a tree view. That tree view often reveals a parent process spawning hundreds of children.

Step 2: Sample CPU per process over time

A single snapshot can miss brief spikes. Use pidstat from the sysstat package:

sudo apt install -y sysstat
pidstat 2 5

This prints CPU usage every 2 seconds for 5 intervals. Replace 2 5 with 1 60 when you need a full minute of samples during peak traffic.

For per-core breakdown:

mpstat -P ALL 2 5

If one core stays at 100% while others idle, you likely have a single-threaded bottleneck. That pattern is common with one heavy MySQL query or a PHP script stuck in a loop.

Ubuntu High CPU Diagnostic FlowSymptomSlow site, load spikeMeasureuptime, htop, pidstatIdentifyTop PID and commandClassifyApp, DB, cron, OSCommon Root Causes on Web ServersPHP-FPM poolMySQL queryCron overlapMalwareApply targeted fix — avoid reboot-first troubleshooting
Diagnostic workflow to fix high CPU usage on Ubuntu production servers

Log what you find before changing anything. Note the PID, command name, user, and start time. You will need that paper trail if the spike returns overnight.

How do you fix PHP-FPM and web stack CPU spikes on Ubuntu?

On Laravel and WordPress servers I maintain, PHP-FPM is the most frequent CPU hog. Apache or Nginx themselves rarely saturate CPU. The worker processes behind them do.

Inspect PHP-FPM pool configuration

Pool files live under /etc/php/8.4/fpm/pool.d/ on PHP 8.4. Adjust the version folder to match your install. See our PHP installation guide for Ubuntu if you run multiple versions side by side.

sudo grep -E '^(pm\.|max_children|start_servers)' /etc/php/8.4/fpm/pool.d/*.conf
sudo systemctl status php8.4-fpm

A pool with pm.max_children = 50 on a 2 GB VPS will thrash under traffic. Each child can consume 50–120 MB RAM. When memory fills up, the kernel swaps. Swap makes CPU spike harder because pages move to disk constantly.

Sensible starting values for a 4 GB Laravel server:

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 8
pm.max_requests = 500

After editing the pool file, reload FPM:

sudo systemctl reload php8.4-fpm

Reload is safer than restart on busy sites. Active requests finish on old workers while new ones pick up the config. Official PHP-FPM docs cover pool manager modes at php.net FPM configuration.

Find the exact PHP script burning CPU

When many php-fpm workers appear in htop, enable slow logging temporarily:

request_slowlog_timeout = 5s
slowlog = /var/log/php8.4-fpm-slow.log

Reload FPM, reproduce the spike, then read the log:

sudo tail -50 /var/log/php8.4-fpm-slow.log

The stack trace points to the controller, plugin, or cron route at fault. Fix the code or cache the query. Do not just raise max_children forever.

Nginx and Apache sanity checks

Verify you are not running a misconfigured proxy loop or excessive rewrite chain. For Nginx setups, review Nginx installation and tuning on Ubuntu. A single bad try_files rule can force thousands of internal redirects per minute.

LAMP Stack CPU HotspotsBrowserNginxLow CPUPHP-FPMHigh CPUWorker poolMySQLHigh CPUSlow queriesFix ActionsTune pm.max_childrenEnable OPcacheAdd query indexesCache with RedisQueue heavy jobsLaravel queuesRedis 8.10 offloads repeated DB reads
PHP-FPM and MySQL dominate CPU on typical Ubuntu web stacks running Laravel or WordPress

On sister sites I deploy with Deployer 7 and GitLab CI—legal-tech portals like those in our Adventure Third Pole Trek portfolio case—a post-deploy opcache miss can spike CPU for minutes. Reload PHP-FPM after symlink swap so workers pick up new bytecode.

How do you fix MySQL and database CPU usage on Ubuntu?

When mysqld tops htop, the database is working too hard or waiting on disk I/O that looks like CPU load. Start with live query inspection.

Find running queries

sudo mysql -e "SHOW FULL PROCESSLIST;"
sudo mysql -e "SELECT * FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;" 2>/dev/null

Look for queries in Sending data or Creating sort index state for more than a few seconds. Kill a runaway query only when you understand the impact:

sudo mysql -e "KILL 12345;"

Replace 12345 with the actual connection ID from PROCESSLIST.

Enable and read the slow query log

Edit /etc/mysql/mysql.conf.d/mysqld.cnf on MySQL 8.4 LTS or MySQL 9.7:

slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
sudo systemctl restart mysql
sudo mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

Add indexes for patterns that repeat. Cache read-heavy aggregates in Redis 8.10. MySQL status variable docs at MySQL 8.4 reference explain what Threads_running and Created_tmp_disk_tables mean under load.

Our MySQL installation guide for Ubuntu covers baseline tuning. For Laravel apps, enable query logging in staging and fix N+1 patterns before they hit production.

Compare common database CPU fixes

SymptomLikely causeFixRisk if ignored
One query at 100% CPUMissing index, full table scanAdd index, rewrite queryTable locks, site timeouts
Many short queriesN+1 from ORMEager load, cache layerCPU climbs with traffic
Spike at midnightBackup or cron importReschedule, use niceDisk and CPU contention
Constant moderate loadBuffer pool too smallRaise innodb_buffer_pool_sizeExcess disk reads

How do cron jobs and systemd services cause high CPU on Ubuntu?

Overlapping cron entries are a classic trap. A job that takes 10 minutes but runs every 5 minutes stacks until the server melts.

Audit scheduled tasks

sudo crontab -l
sudo ls -la /etc/cron.d/
grep -r "" /etc/cron.d/ /var/spool/cron/crontabs/ 2>/dev/null

Laravel apps also schedule via a single cron entry:

* * * * * cd /var/www/app && php artisan schedule:run >> /dev/null 2>&1

Check schedule:list and ensure heavy tasks use queues. Read our Ubuntu cron jobs guide for overlap prevention with flock:

* * * * * flock -n /tmp/backup.lock /usr/local/bin/nightly-backup.sh

flock -n skips a new run if the previous instance still holds the lock.

Find misbehaving systemd units

systemd-cgtop
systemctl list-units --type=service --state=running
systemctl status snapd avahi-daemon ModemManager

On headless servers, disable services you never use:

sudo systemctl disable --now avahi-daemon
sudo systemctl disable --now ModemManager

Desktop Ubuntu installs carry more background daemons than server images. If you built from a desktop ISO, trim packages per our Ubuntu performance tuning guide.

Check for crypto miners and compromised processes

Unexpected /tmp binaries or www-data running kinsing-style miners happen on exposed servers. Investigate unknown PIDs:

ls -l /proc/PID/exe
cat /proc/PID/cmdline | tr '\0' ' '
sudo lsof -p PID

Compare file hashes against known good packages. Harden SSH, enable fail2ban on Ubuntu, and follow security hardening steps after removing malware. Parse suspicious log lines with our regex tester tool before writing permanent filter rules.

Normal vs Overloaded ServerHealthy 4-core boxLoad avg: 0.8 – 2.0PHP workers: 4–8 activeMySQL threads: under 10Headroom for traffic spikesOverloaded 4-core boxLoad avg: 8.0+ sustainedPHP workers: maxed outSwap usage climbing502 errors, SSH lagfixRecovery Checklist1. Kill or tune top PID 2. Reduce FPM children 3. Fix slow query4. Add flock to cron 5. Monitor for 24 hours
Healthy versus overloaded Ubuntu server metrics when you fix high CPU usage on Ubuntu

What monitoring and limits prevent CPU spikes from returning?

Fixing today's spike is half the job. You need visibility before the next one hits during Dashain traffic or a client email blast.

Set up ongoing monitoring

Install basic metrics collection on every production box:

  1. Enable sysstat history: set ENABLED="true" in /etc/default/sysstat, then sudo systemctl enable --now sysstat.
  2. Review past data with sar -u 1 5 and sar -q for load trends.
  3. Configure alerts when load exceeds core count for 10+ minutes.
  4. Log PHP slow requests and MySQL slow queries permanently in staging-like thresholds.

Our Ubuntu server monitoring guide expands on log rotation and alert channels. For a fresh baseline, start from the Ubuntu server setup guide.

Use cgroups and nice values for heavy batch work

Limit non-interactive jobs so they cannot starve the web stack:

sudo systemd-run --scope -p CPUQuota=30% -- nice -n 19 backup.sh

CPUQuota=30% caps the scope at roughly 30% of one core on systemd 255+ on Ubuntu 24.04. Adjust for your backup window.

Know when hardware is the real bottleneck

Optimization has limits. A 1 GB VPS running MySQL 8.4, PHP 8.4, and Redis on the same node will spike under modest traffic. Signs you have outgrown the box:

  • CPU idle stays near 0% even after query and pool tuning.
  • Load average tracks visitor count linearly with no single bad process.
  • vmstat 1 shows constant swap in/out.
  • Upgrading from Laravel 12 to Laravel 13 with PHP 8.3 adds headroom demand you cannot config away.

Moving to a 4-core, 8 GB instance often costs Rs 3,000–6,000/month (~USD 22–45) on regional VPS providers. That beats hours of emergency firefighting. Our hosting and domain service and speed optimization service cover capacity planning for Nepal-based businesses.

CPU Fix Decision TreeHigh CPU detectedKnown app process?YesTune pool, query,cache, queueNoSecurity audit,kill unknown PIDStill high after tune?Scale CPU or RAM
Decision tree to fix high CPU usage on Ubuntu—tune first, investigate unknowns, scale last

Keep systems patched. Unattended upgrades on Ubuntu reduce exposure to kernel bugs that masquerade as CPU bugs. See Ubuntu security updates and the official Ubuntu Server documentation for release-specific notes.

If you manage multiple client servers, document a runbook. Same steps every time: uptime, htop, logs, fix, verify. Our Linux system administration service and support and maintenance plans exist for teams that want this handled proactively.

Bookmark essential Ubuntu terminal commands on your phone. When SSH is lagging, you want muscle memory—not a Google search.

Key Takeaways

  • Run htop and pidstat first to name the process before you change configs or reboot.
  • PHP-FPM pool sizes and MySQL slow queries cause most CPU spikes on Laravel and WordPress Ubuntu servers.
  • Overlap cron jobs with flock and move heavy work to queues capped by systemd CPU quotas.
  • Unknown processes in /tmp warrant a security review, not just a kill signal.
  • Enable sysstat, slow logs, and load alerts so the next spike does not surprise you at 2 a.m.
  • Scale the VPS when tuning no longer helps—cheap hardware costs less than repeated outages.

People Also Ask

What is a normal CPU load on Ubuntu?

On a machine with N cores, load average should usually stay at or below N during business hours. Brief spikes above N are fine. Sustained load at double your core count means processes are waiting for CPU time and users feel it.

Can I safely kill a high-CPU process on Ubuntu?

Yes, if you know what it is. Use sudo kill PID for a graceful stop, then sudo kill -9 PID if it ignores the signal. Never kill systemd, sshd, or init. Killing mysqld without warning drops active connections.

Why does Ubuntu use 100% CPU after an update?

unattended-upgrades, apt indexing, or a new kernel module rebuild can spike CPU temporarily. Check top for apt or fwupd. Wait 15 minutes on small VPS instances. If snapd is the culprit on servers, consider disabling snaps you do not need.

Does adding swap fix high CPU usage?

Swap relieves memory pressure but can increase CPU usage through disk paging. Add swap as a safety net on low-RAM boxes. It is not a substitute for tuning PHP-FPM, fixing queries, or adding RAM.

Next steps after you fix high CPU usage on Ubuntu

Work through diagnosis, targeted fixes, and monitoring in that order. Rebooting clears symptoms but hides the cause. The process that returned at boot will spike again tomorrow.

Document what you changed—pool values, indexes, cron locks—and schedule a follow-up review in one week. CPU problems rarely stay fixed unless someone watches the graphs.

Need hands-on help on a production box? Contact us for server troubleshooting, or explore Linux administration support for ongoing coverage.

Frequently Asked Questions

Start with live diagnostics, not a reboot. Run uptime, top, or htop to name the process eating CPU, log its PID and command, then apply a targeted fix. On Laravel and WordPress servers I maintain, PHP-FPM workers, MySQL queries, overlapping cron jobs, and occasionally malware in /tmp are the repeat offenders. Reload PHP-FPM after config changes rather than restarting when traffic is active. Document what you changed so the spike does not return unnoticed overnight.

On a machine with N cores, load average should usually stay at or below N during business hours. Brief spikes above N are fine; sustained load at double your core count means processes are waiting for CPU time.

Run uptime for load averages, then top or htop sorted by CPU—F6 in htop, F5 for tree view to spot parent processes spawning hundreds of children. A single snapshot can miss brief spikes, so use pidstat from sysstat for repeated samples, and mpstat -P ALL to see if one core stays at 100% while others idle. On a 4-core server, sustained load above 4.0 means every core is busy. Log PID, command name, user, and start time before changing anything.

Yes, if you know what it is. Use sudo kill PID for a graceful stop, then sudo kill -9 PID if it ignores the signal. Never kill systemd, sshd, or init.

unattended-upgrades, apt indexing, or a new kernel module rebuild can spike CPU temporarily after patches. Check top for apt or fwupd and wait about 15 minutes on small VPS instances. On headless servers, snapd is a common culprit—consider disabling snaps you do not need. Keep systems patched anyway; unattended upgrades on Ubuntu reduce exposure to kernel bugs that can masquerade as CPU problems. If load stays high beyond a short window, treat it like any other spike and identify the process with htop.

No. Swap relieves memory pressure but can increase CPU usage through constant disk paging. It is a safety net, not a substitute for tuning PHP-FPM, fixing queries, or adding RAM.

PHP-FPM is the most frequent CPU hog on Laravel and WordPress boxes I run—Apache or Nginx themselves rarely saturate CPU. Pool files live under /etc/php/8.4/fpm/pool.d/. A pool with pm.max_children = 50 on a 2 GB VPS thrashes under traffic because each child can consume 50–120 MB RAM; when memory fills, swap makes CPU spike harder. Sensible starting values on a 4 GB Laravel server: pm = dynamic, max_children = 20, start_servers = 4, min_spare_servers = 2, max_spare_servers = 8, max_requests = 500. Reload with sudo systemctl reload php8.4-fpm after edits.

When many php-fpm workers appear in htop, enable slow logging temporarily with request_slowlog_timeout = 5s and slowlog = /var/log/php8.4-fpm-slow.log, reload FPM, reproduce the spike, then read sudo tail -50 /var/log/php8.4-fpm-slow.log. The stack trace points to the controller, plugin, or cron route at fault—fix the code or cache the query instead of raising max_children forever. Post-deploy opcache misses on sites I deploy with Deployer 7 and GitLab CI can also spike CPU for minutes; reload PHP-FPM after symlink swap so workers pick up new bytecode.

When mysqld tops htop, inspect live queries with SHOW FULL PROCESSLIST and performance_schema.events_statements_summary_by_digest. Look for queries stuck in Sending data or Creating sort index for more than a few seconds. Kill a runaway query only when you understand the impact: sudo mysql -e "KILL connection_id;". Enable slow_query_log in /etc/mysql/mysql.conf.d/mysqld.cnf with long_query_time = 2, restart MySQL, then review with mysqldumpslow. Add indexes for repeating patterns and cache read-heavy aggregates in Redis 8.10. For Laravel apps, fix N+1 patterns in staging before they hit production.

Overlapping cron entries are a classic trap—a job that takes 10 minutes but runs every 5 minutes stacks until the server melts. Audit with sudo crontab -l, ls /etc/cron.d/, and grep through cron directories. Laravel apps use a single * php artisan schedule:run entry; check schedule:list and move heavy tasks to queues. Prevent overlap with flock -n on a lock file so a new run skips if the previous instance still holds the lock. Also check systemd-cgtop and disable unused services like avahi-daemon or ModemManager on headless servers.

Unexpected /tmp binaries or www-data running miner-style processes happen on exposed servers. Investigate unknown PIDs with ls -l /proc/PID/exe, cat /proc/PID/cmdline, and sudo lsof -p PID. Compare file hashes against known good packages. This warrants a security review, not just a kill signal—harden SSH, enable fail2ban, and follow security hardening steps after removing malware. Unknown processes deserve investigation before you change pool sizes or add RAM, because tuning will not stop a compromised box from spiking again.

Yes, though PHP-FPM and MySQL dominate on typical Laravel and WordPress stacks. Verify you are not running a misconfigured proxy loop or excessive rewrite chain. A single bad try_files rule in Nginx can force thousands of internal redirects per minute, which shows up as sustained load even when individual processes look modest. Review proxy and rewrite config alongside your web stack tuning guide. Fix routing before you keep raising PHP-FPM max_children, because misconfigured front-end rules can multiply backend work unnecessarily under normal traffic.

Fixing today's spike is half the job. Enable sysstat history by setting ENABLED="true" in /etc/default/sysstat, then sudo systemctl enable --now sysstat. Review past data with sar -u and sar -q for load trends. Configure alerts when load exceeds core count for 10 or more minutes. Log PHP slow requests and MySQL slow queries permanently at staging-like thresholds. For heavy batch work, cap non-interactive jobs with sudo systemd-run --scope -p CPUQuota=30% so backups cannot starve the web stack. Document a runbook: uptime, htop, logs, fix, verify—same steps every time.

Optimization has limits. Signs you have outgrown the box: CPU idle stays near 0% even after query and pool tuning, load average tracks visitor count linearly with no single bad process, and vmstat 1 shows constant swap in/out. A 1 GB VPS running MySQL 8.4, PHP 8.4, and Redis on the same node will spike under modest traffic. Upgrading from Laravel 12 to Laravel 13 with PHP 8.3 adds headroom demand you cannot config away. Moving to a 4-core, 8 GB instance often costs Rs 3,000–6,000/month (~USD 22–45) on regional VPS providers—that beats hours of emergency firefighting.

Work through diagnosis, targeted fixes, and monitoring in that order—rebooting clears symptoms but hides the cause. Step one: uptime, top or htop, pidstat to name the offender and log PID, user, and start time. Step two: apply a targeted fix—PHP-FPM pool tuning, MySQL index or slow-query work, cron flock locks, disabling unused systemd services, or security review for unknown /tmp processes. Step three: enable sysstat, slow logs, and load alerts so the next spike during a traffic surge or client email blast does not surprise you at 2 a.m. Schedule a follow-up review in one week; CPU problems rarely stay fixed unless someone watches the graphs.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: