Asterisk at 150% or 250% feels like the server is finished. On a host with several cores, that number can be one or two cores, and the rest of the machine can be idle. Poor audio or slow setup is a reason to dig in. A busy switch with clean audio and idle cores can wait.
The time inside the Asterisk process tells you which cost you are looking at. User time is codec math, dialplan, and log lines. System time is the kernel moving packets. A restart, a SIP debug session, or a module unload throws away the minute you needed. Collect that minute while the percentage is high, then change the thing the minute names.
Read the scale before you change a file
pidstat prints a task as a share of one CPU unless you pass -I. The pidstat manual divides that share by the processor count when -I is set. Leave -I off and a process on more than one core prints a number above 100. 170% system time is about 1.7 cores inside the kernel. The rest of the server can be idle. One thread pinned near a single core is a separate problem, and top -H is how you see it.
top uses the same kind of scale: 100% is one logical CPU. A host with 8 logical CPUs can show Asterisk at 100% while seven cores are free. The process ceiling on that box is about 800%. 100% is one core. People open a ticket because Asterisk “hit 100%” and calls keep flowing. Compare the percentage with the core count before you call the host full.
A host-wide figure from mpstat is a third scale. 14% system time on the whole machine and 150% system time on the Asterisk process can both be true in the same minute. Compare last week when both readings came from the same tool. Mixing -I and no -I compares two different scales.
top also prints steal time and I/O wait. Steal time means the hypervisor gave the CPU to another guest. Two VMs with the same core count can show different Asterisk percentages because one of them shares a noisy neighbor. I/O wait means a disk is the stall. Neither number is fixed by a codec change.
Asterisk’s own high-CPU note makes the same cut. Poor audio or a long setup is the fault you chase. The percentage is the measurement. Define the number you will accept before you edit a config file. Troubleshooting High CPU Utilization starts there.
See which process owns the minute
Sort top by CPU. Asterisk, mysqld, a backup compressor, a SIP proxy, and fail2ban-server are different jobs. If the database leads, the work is the query that is running. If a compressor leads, the work is the archive job. If a proxy leads, restarting Asterisk does not touch the process that is hot. If Asterisk leads, stay with Asterisk.
A spike at midnight, on the hour, or on a clock you can predict is a scheduled job until you prove otherwise. Database dumps, pbzip2 or gzip on those dumps, CDR deletion, and log rotation all sit on a core for the length of the job. A dump that runs 00:00 to 01:30 will page you every night and leave call quality alone. Confirm the clock against cron and the process name in top before you retune SIP.
CPU with almost no calls is a different shape from CPU that tracks the call count. No calls, plus a hot Asterisk, points at OPTIONS probes, a debug flag, a stuck dialog, a timer, or a thread spinning. Calls present, plus system time, points at packets. Calls present, plus user time, points at codecs, mixing, or logs.
Then sample the Asterisk pid for ten seconds:
pidstat -u -p "$(pidof asterisk)" 1 10
Leave -I off so a multi-core process can print above 100.
| Column | Where the time went |
|---|---|
| %usr | Application code: codec translation, dialplan, log lines |
| %system | Kernel: sockets, RTP, softirq |
| %CPU | Those shares added together, on the one-CPU scale |
A sample where %system is several times %usr is a packet path. A sample where %usr leads, and translators are in use, is codec work.
Three ways a live call spends CPU
Asterisk documents a bridge as the object that shares media between channels. Dial() creates one when the outbound channel answers. One ordinary call is two channels in one bridge. The shapes are in Asterisk’s bridge note.
A native RTP bridge (native_rtp in bridge show all) is the native technology for two RTP channels. It has two shapes.
* Local. Each device sends RTP to Asterisk, and Asterisk forwards it to the other device. There is no transcoding. Asterisk remains in the media path. This is the common case when either side is behind NAT.
* Remote. Asterisk redirects the media so the two devices send RTP to each other. Asterisk calls this direct media. It is the lowest CPU of the three shapes. Both channels have to support it, and neither device can be hidden behind NAT.
A core bridge (simple_bridge) is the fallback when Asterisk must see the audio: different codecs, recording, a forced jitter buffer, or in-band DTMF. A mixing bridge (softmix) is a conference. Mixing shows up as application time. A local native relay shows up as system time.
canreinvite and directmedia are the same sip.conf option. Asterisk renamed canreinvite to directmedia and kept the old name. In the Asterisk 20 sample config:
* yes tries to send the RTP from caller to callee. That is the default in the sample.
* no refuses that redirect, so a native_rtp bridge stays local and the host keeps the packets.
* nonat allows the redirect when the peer is not behind NAT, and refuses it when the peer is.
* update uses a SIP UPDATE for the redirect. Combined with nonat it is update,nonat. It implies yes.
* outgoing avoids an immediate re-INVITE glare with another server that does the same. It implies yes.
Hold and T.38 can send a re-INVITE with no set. no blocks the re-INVITE whose job is to take Asterisk out of the audio path. A peer section wins over [general]. [general] can say directmedia=yes while dozens of peers say no. Those peers stay on the host.
On chan_pjsip the matching idea is direct_media on the endpoint. The same NAT limit applies: two private addresses cannot send RTP to each other just because the option is on.
Packet rate
A 20 ms G.711 frame is 50 packets per second in one direction on one leg. A local bridge has two legs, so the host receives on the order of 100 UDP packets per second per call. About 200 per call is 10 ms framing. Same-codec forwarding is cheap next to transcoding, and a local bridge pulls every one of those packets through the server. If you drop ptime from 20 ms to 10 ms on the phones or the trunk, you about double the packet rate and the kernel time, with the codec pair unchanged.
Count packets without turning SIP debug on. A short tcpdump of UDP length and rate, or the interface counters in /proc/net/snmp and /proc/net/dev ten seconds apart, is enough to see 20 ms versus 10 ms. Debug writes a copy of every SIP message and makes the CPU worse.
When the bridge has to see the audio
A local native relay cannot record, and it cannot watch in-band tones. The moment you record, force a jitter buffer, generate in-band DTMF, or mix a conference, Asterisk has to see the samples. bridge show all names the type. simple_bridge on recorded calls is expected. softmix on a conference is expected.
transmit_silence in asterisk.conf is an easy one to miss. The sample file says the silence is generated as signed linear and then translated into the channel’s own format, so a channel that would have stayed native grows a translation path. Turn it on for a reason you can name. Leave it off when you are chasing CPU.
Music on hold, prompts, and voicemail are the same conversion. If the prompt files exist as G.729 and the caller is G.711, Asterisk translates for the length of the prompt. Keep files in the formats your callers use. Signed linear at 8 kHz and 16 kHz is the cheaper stand-in when you cannot store every codec.
In-band DTMF keeps Asterisk on the media path so it can watch the audio. The codec pair can stay the same. RFC 2833 or SIP INFO can leave a native bridge in place.
Transcoding
Forwarding two legs that share a codec is the cheap case. Transcoding is the expensive one. Asterisk’s high-CPU page prints core show translation as microseconds per second of audio on that machine. Compare the cells with each other. Your call count comes from a sample on your host. This table compares codecs on the machine that produced it.
On the machine in that doc, one second of audio costs about 9,150 microseconds from µ-law to A-law, about 15,000 from µ-law to G.729, and about 23,000 from µ-law to Opus. An Opus path to µ-law has four steps: Opus at 48 kHz, signed linear at 48 kHz, signed linear at 8 kHz, then µ-law. A WebRTC leg or an AI-agent leg that offers Opus, against a PSTN leg that is G.711, pays that path on every call. If the far end accepts 8 kHz signed linear, offer that and skip Opus.
µ-law and A-law are both G.711, and they are not the same encoding. The translation cell between them is the small one on the chart. It is a conversion. Calls that look free on a bandwidth chart and sound thin can be this pair. Check the read format and the write format on the channel. The allow list in sip.conf is what you permit. The channel line is what this call negotiated.
How to confirm
%usr leads. core show channels shows a translator, or a different read format and write format. bridge show all shows simple_bridge where you expected native_rtp. Recent CDRs disagree on the two legs. Read the translation table you have. Skip core show translation recalc during the hot minute. Recalc runs a new timing test on the cores you are trying to measure.
Match the codecs when you can. Let the carrier translate at the edge when the contract allows it. Hardware offload is a third option when you must translate at volume.
One thread, and the SIP stack in front of it
top -H -p shows threads. One Asterisk thread near a full core, with the host idle, will not go faster because you add vCPUs. Extra cores sit behind a thread that never leaves the one it has. Cutting vCPUs because “Asterisk is at 100%” throws away the idle cores. The 100% was one core. The other cores were the headroom.
chan_sip keeps a monitor thread (do_monitor) whose job is SIP I/O. If top -H shows that thread at a full core and the call setup rate is the thing falling over, you are looking at the SIP read loop, not at a shortage of idle cores. chan_pjsip spreads SIP work across a threadpool. The pool size is set in the system section of pjsip.conf. A pool that is too small starts dropping new SIP work so the threads can catch up. Raising the pool has a limit too: past it, you profile the next bottleneck instead of guessing a bigger number.
core show threads names the Asterisk-side threads (bridge, mixmonitor, taskprocessor, the SIP monitor). Match the hot thread in top -H to a name there before you decide the host is small.
OPTIONS, retransmissions, and DNS
qualify=yes sends SIP OPTIONS to each peer on a timer (qualifyfreq in sip.conf, qualify_frequency on a PJSIP AoR). A few hundred peers are a light tick. Thousands of peers on a short timer are a burst: CPU, disk, and a wave of registrations that drop together when the burst overruns the SIP thread. The graph shows a spike on a schedule, including minutes with almost no phone calls. Lengthen the timer or stop qualifying peers you do not need to mark unreachable.
A flood is a different shape. Many SIP packets from one address, or a SIP rate that is high for the number of real calls. Count packets with tcpdump or interface counters. Leave sip set debug on off while you count. One remote address at the top of that count, with no matching customer, is the source to reject in your firewall.
Stuck dialogs are a third shape. An originator sends INVITE and then goes quiet. On chan_sip:
asterisk -rx "sip show channels"
A long list with the same address and the same Last Message is that pile. On PJSIP, pjsip show channels is the matching view. Find the sender. A restart hides the list.
Read today’s log before you add debug. chan_sip logs Serious Network Trouble when __sip_xmit fails, and it logs retransmission timeouts when the other side never answers. Thousands of those lines in a day mean DNS or the path to the peer is failing. Asterisk then looks frozen: the SIP thread is busy timing out work that will never complete. A restart clears the queue. The timeouts return with the next outage. Fix the resolver and the route. A core upgrade does not answer a DNS query.
High latency with an idle Asterisk is the same family. A database on the other side of a slow link, or a trunk whose qualify RTT is hundreds of milliseconds, stalls setup without filling the CPU graph. Measure the RTT and the query time. Do not buy cores for a path that is waiting on the network.
Logging, and the firewall beside Asterisk
Logging spends CPU to build the line and I/O to write it. Asterisk measured Channel Event Logging at almost 40% of the instructions in the process on versions before 23.3.0, 22.9.0, and 20.19.0. That was more than the instructions spent on the calls. Those three releases brought the share under 10%. If you do not need CEL, turn it off. Verbose logging writes a line for every dialplan step. On a busy system that is hundreds of lines a second. Keep console and file logging at NOTICE, WARNING, and ERROR unless you are tracing one call. Debug on a production system is for that one issue, for a short window, and then off.
fail2ban is a separate process. Its line in top is its own CPU. Asterisk system time is a different line. A large ban list plus a long chain of firewall rules shows up as softirq and as Asterisk system time together: every RTP packet walks the chain. Rotate the logs fail2ban reads. Leave fail2ban running. Stopping it removes fail2ban’s CPU for as long as it is down, drops the bans, and leaves the RTP cost in place.
Disk, SQL, and jobs that wear Asterisk’s clothes
A spinning disk under a database makes calls fail while the CPU graph looks confusing. wa in top or iostat is the tell. Slow queries pile up, the GUI times out, trunks drop, and someone restarts Asterisk because that is the service they can name. The restart coincides with the queries finishing. Ask the disk. iostat -xz 1 during the minute, and SHOW FULL PROCESSLIST on the database, name the query. A per-call blacklist, a rating lookup, or a stats page that scans a large table will do this at a modest call rate. Move that work off the call path, or give the database an SSD, before you add Asterisk cores.
CDR deletion and call archiving are the same class of job as the midnight backup. If they run through the business day they compete with the SIP thread and the disk. Schedule them for the trough, and check the next day’s graph before you declare the SIP stack healed.
Free memory has its own door. minmemfree in asterisk.conf stops new calls when free RAM falls below a watermark. A host that “runs out of channels” with idle CPU may be on that floor. Read the setting and free -m in the same minute.
Heat
A critical sensor line that says the processor throttled is a hardware event. It can arrive on the same morning as a high Asterisk percentage. The two can be different problems. Check the governor and the current clock (cpupower frequency-info, or the values under /sys/devices/system/cpu/cpu*/cpufreq/). A core whose governor says performance and whose clock sits far below the maximum is being held down by heat. A sensor at a high temperature in that same minute confirms it. A reading an hour later describes a different minute.
A lower clock makes the same packet rate look like a higher percentage, because each core does less work per second. Switching tuned profiles (latency-performance, network-latency, powersave) is a decision for the person who owns the chassis and the BIOS power cap. Do that after you know the clock was down during the Asterisk spike. If the clocks were at maximum during the spike, the governor is the wrong afternoon.
The load-average door
maxload in asterisk.conf is a load-average ceiling. Above it, Asterisk refuses new calls. The CLI names the configured limit and the load it just measured. Load average is not the Asterisk %CPU you read in top. A ceiling of 0.9 or 1.0, which is what the sample comment and the core docs page use as illustrations, will refuse calls on a many-core host that is almost idle. The core config page shows maxload = 1.0 and warns that the page can lag the sample file. The current sample leaves the line commented and illustrates 0.9. Read the number in your file.
Raising maxload lets more calls in. The CPU cost of each call stays the same. Set the ceiling from the core count and from the load you have measured, then find out why the load is high. If Asterisk keeps refusing calls after a higher limit, the remaining options are more hardware, another Asterisk with traffic moved onto it, or less traffic.
What to record during the bad minute
Do this while the percentage is high. A quiet hour produces a quiet sample and a wrong story. Hours later, the process list is a different process list.
- Note the call count (
core show channels count) and whether audio or setup is worse than the hour you treat as normal. topfor ten seconds. Record the top three processes,%wa, and%st(steal).pidstat -u -p "$(pidof asterisk)" 1 10without-I.top -H -plong enough to see whether one thread sits near 100%.bridge show alland a handful of channel read/write formats.- Packet rate on the RTP interface, enough to tell 20 ms from 10 ms.
- If Asterisk is not the hot process:
SHOW FULL PROCESSLIST, oriostat -xz 1, or the proxy’s own stats. - A count of retransmission timeouts and
Serious Network Troublelines in today’s log, without enabling debug. - The same hour a week earlier, from the same tool.
Leave SIP debug off while you collect this. Leave chan_sip.so loaded. Unloading it drops live calls. Leave directmedia / canreinvite as they are until you have checked NAT on that peer. A global yes fails when the two ends cannot reach each other, and the result is one-way audio. Leave fail2ban running. Leave the tuned profile in place until the clocks say they dropped. Leave maxload in place until you know which process created the load.
A restart is the last step, and it is a way to restore service after you have the notes. It is a poor way to learn. If a restart buys a few hours and the heat returns, you are looking at a leak, a stuck dialog, or a job that starts again. The next restart can wait until the list above is on disk.
FAQ
Asterisk shows 200% CPU. Is the server full?
On a multi-core host, 200% without pidstat -I is about two cores. Read idle and steal in top, and host system time in mpstat, before you order hardware.
Both legs use the same codec. Why is CPU high?
The call can be a local native bridge. Same codec skips the translator. The packets remain. Check bridge show all and %system against %usr. Also check ptime. 10 ms framing doubles the packet rate of 20 ms framing.
Will directmedia=yes lower the CPU?
It can, when both ends support a remote bridge and neither side is behind NAT. When that path fails you get one-way audio. Peers set to no ignore a yes in [general].
Should I turn on SIP debug or restart first?
Record the minute first. Debug makes the CPU worse. A restart drops live calls and deletes the shape you needed.
The graph spikes every night at 00:00. Is that the call mix?
Look at cron and at the process name. Backup compression and CDR cleanup run on that clock. If the process is pbzip2, gzip, or mysqld and Asterisk is quiet, the calls are not the cost.
One host in October 2026
This is a production Asterisk we measured. Names, addresses, and the platform around it are left out. The shape is the one the sections above are for.
The morning started with a critical hardware event: the CPU temperature sensor reported that the processor had throttled. The host was on the latency-performance tuned profile. The question was whether to load network-latency while calls were up. That choice belongs to the person who owns the hardware. It is not an Asterisk setting.
Later the same day Asterisk’s own percentage was the complaint. On a comparable business day the week before, the process had peaked around 120%. This day it was around 250%, and the traffic count was not higher. A restart the next morning, outside the busy hours, did not bring it back. At 160 concurrent calls, with the day expected to reach 300 to 350, the reading was about 100%.
A one-minute sample that morning caught 223 calls:
| Reading | Value |
|---|---|
| Asterisk %system | 151.8% (about 1.5 cores in the kernel) |
| Asterisk %usr | 21.26% (about 0.2 cores in the application) |
| G.729 translators | none in use on the channels we sampled (0 of 20) |
| Bridges | native_rtp 113, simple_bridge 0, softmix 0 |
| UDP in | 83.3 packets per second per call, in the 20 ms band rather than the 10 ms band |
[general] |
directmedia unset, canreinvite=yes |
| Peers | canreinvite yes or update: 0. no: 42 |
| Codec mismatch on recent CDRs | a few hundred mixed legs against a few hundred thousand CDRs on the busy peers. Largest pair A-law to G.729 |
| fail2ban-server | 31.60% of one core |
| Log growth in the window | about 1.0 MB |
| Debug / verbose | off / not raised |
| Host %system this sample | 13.56% |
| Same hour, 7 days earlier | host %system 14.32%, host %user 5.24% |
System time was about seven times user time. The bridges were local native RTP. Translators were idle. The general section allowed a remote bridge. Forty-two peers refused it. Those peers were set that way before this morning. Incoming UDP sat with 20 ms framing (on the order of 100 packets per second per call). 10 ms framing would sit on the order of 200.
The host-wide system time matched the same hour a week earlier (13.56% against 14.32%). The box had the same kernel share. Asterisk’s process time was kernel time on a local relay, and fail2ban was a second process at about a third of one core. RTP volume against the prior week did not show a rise, and spot checks showed no transcoding. That agrees with the sample. The jump from about 120% to about 250% stays unexplained if the packet volume was flat and both percentages used the same scale. Heat can do that when a clock drops. This minute did not show a throttle in progress. A restart had happened earlier that morning, and the relay was the shape at 223 calls.
At 223 calls the kernel share was about 1.5 cores. A later peak of 300 to 350 calls on the same local path will spend more kernel time if each call keeps that packet rate. One minute cannot size the host. It can stop you treating 14% host system time as a full server. The next facts, during a high minute, are the throttle bit, the core clocks, one thread versus many in top -H, and the fail2ban ban count. An Asterisk reinstall skips those facts.
Take the same sample on your host
The checks above are one file, asterisk-cpu-load.sh. It reads Asterisk, the CPU clock, bridges, codec modules, direct media, and fail2ban. It does not restart Asterisk, turn on SIP debug, or change a setting.
Sample the hot minute before you restart
Record the process split, the bridge type, and the codec path while the percentage is high. A restart throws that minute away. Send the sample with the ticket if you want a second look.