ORA-00020 is a connection-admission cliff-edge. At 99% of the configured PROCESSES limit, everything works. At 100%, new connections are refused and the error cascades into every monitoring tool, DBA session, and application pool. Existing sessions keep running, which is why this is often discovered late.

The symptom: applications report connection failures, the listener answers TCP but the database rejects the actual connect with ORA-00020, and your usual SYSDBA session may also be refused. The alert log fills with the error. If your connection pool retries in a tight loop, the situation worsens before it improves, because each retry is another failed admission attempt.

The fix is rarely urgent-only. Raising PROCESSES is a static parameter change that requires an instance restart. The harder work is identifying why process count climbed: a connection leak, an oversized pool, a retry storm, dead-connection accumulation, or an OS-level limit that bit before Oracle’s own.

What this means

ORA-00020 means the Oracle instance has exhausted its allocation of process state objects. The PROCESSES initialization parameter hard-limits the number of OS processes the instance can manage: dedicated server processes, shared servers, dispatchers, and background processes all draw from this pool.

  • PROCESSES is static. Changing it requires an instance restart. There is no online resize.
  • SESSIONS is derived from PROCESSES. From 12c onward the formula is approximately 1.5 * PROCESSES + 22. On 11g and earlier it is approximately 1.1 * PROCESSES + 5. Oracle silently bumps SESSIONS up if you set it below the derived value.
  • Background processes consume slots too. Expect roughly 40 to 70 process slots reserved for background processes (DBWn, LGWR, PMON, ARCn, MMON, and the rest). The user-available count is less than the raw limit.
  • Each dedicated server is one OS process plus one PGA allocation. Oracle’s limit and the OS limit both apply, and either can fail first.
  • In RAC, each instance has its own PROCESSES limit. One node can hit ORA-00020 while others sit idle if load balancing is misconfigured.
  • OS limits can bite first. The oracle user’s ulimit -u (nproc) and the kernel’s pid_max can be exhausted before Oracle’s own limit. When that happens you see fork failures and ORA-27xxx errors rather than ORA-00020.

This is a cliff-edge failure with no graceful degradation. There is no slowdown phase. Connection admission works, then it does not.

Common causes

CauseWhat it looks likeFirst thing to check
Connection pool misconfigurationProcess count climbs to the pool max and stays there; MAX_UTILIZATION plateaus at the limitApplication pool config: max connections, number of pools
Connection leakProcess count climbs monotonically and never returns to baseline after load dropsV$SESSION for long-lived INACTIVE sessions from the same MACHINE/PROGRAM
Retry stormRapid climb, application logs show connection retries, listener handler count spikesApplication retry logic and backoff policy
Dead connections (no DCD)INACTIVE sessions accumulate from crashed or firewalled clientsSQLNET.EXPIRE_TIME in sqlnet.ora
OS limit hit firstFork failures in alert log, ORA-27xxx errors, no row in V$RESOURCE_LIMIT at limitulimit -u for the oracle user; /proc/sys/kernel/pid_max
Lock contention cascadeSessions queue behind an uncommitted transaction, pool spins up new connections that also blockBLOCKING_SESSION in V$SESSION, enq: TX waits
Undersized PROCESSESSteady-state usage always near the limit, no single runaway causeV$RESOURCE_LIMIT trend, MAX_UTILIZATION history

Quick checks

Run these read-only checks to confirm where the wall is. All are safe to run on a production instance.

-- Check process and session utilization against limits
SELECT RESOURCE_NAME, CURRENT_UTILIZATION, MAX_UTILIZATION, LIMIT_VALUE
FROM V$RESOURCE_LIMIT
WHERE RESOURCE_NAME IN ('processes', 'sessions');

MAX_UTILIZATION is the high-water mark since instance startup. If it equals LIMIT_VALUE, the limit has already been hit at least once, even if current usage is lower now.

# Run as the oracle user, not as root, to see the real nproc in effect
ulimit -u

# Check the kernel pid_max
cat /proc/sys/kernel/pid_max

# Count oracle processes at the OS level (adjust the username if yours differs)
ps -u oracle -o pid= | wc -l
# Grep the alert log for ORA-00020 and OS-level fork failures
adrci exec="show alert -tail 200" | grep -E "ORA-00020|ORA-27"
-- Identify which machines and programs are holding the most sessions
SELECT MACHINE, PROGRAM, COUNT(*) AS sessions,
       SUM(CASE WHEN STATUS = 'INACTIVE' THEN 1 ELSE 0 END) AS inactive
FROM V$SESSION
WHERE TYPE = 'USER'
GROUP BY MACHINE, PROGRAM
ORDER BY sessions DESC
FETCH FIRST 20 ROWS ONLY;
# Confirm the listener is up and check handler availability
lsnrctl status LISTENER

A listener that is up but reports services as BLOCKED, or returns TNS-12516 (no available handler) or TNS-12519 (too many connections), is the same exhaustion seen from the client side.

How to diagnose it

The first decision is whether Oracle’s limit or the OS limit is the binding constraint. The second is whether the saturation is steady-state, a leak, or a spike.

flowchart TD
  A[ORA-00020 reported] --> B{Can SYSDBA connect?}
  B -- No --> C[Use sqlplus -prelim as sysdba]
  B -- Yes --> D[Query V$RESOURCE_LIMIT]
  C --> D
  D --> E{CURRENT = LIMIT?}
  E -- No --> F[OS limit hit first: check ulimit -u and pid_max]
  E -- Yes --> G[Oracle PROCESSES exhausted]
  G --> H{MAX_UTILIZATION at LIMIT for long?}
  H -- Steady at limit --> I[Undersized PROCESSES: resize]
  H -- Recent spike --> J{INACTIVE sessions accumulating?}
  J -- Yes --> K[Connection leak or no DCD]
  J -- No, mostly ACTIVE --> L[Lock cascade or retry storm]
  1. Confirm the binding constraint. Query V$RESOURCE_LIMIT. If CURRENT_UTILIZATION for processes equals LIMIT_VALUE, Oracle’s limit is the wall. If current is below the limit but you are still seeing failures, the OS limit (ulimit -u or pid_max) is the culprit. Check the alert log for ORA-27xxx errors or fork failures to confirm.

  2. Identify the slot consumers. Group V$SESSION by MACHINE and PROGRAM. One application host or one program holding a disproportionate share points the finger. Cross-reference with the application team’s pool configuration.

  3. Distinguish leak from steady-state. If MAX_UTILIZATION has been at the limit for the whole uptime and current usage is always near the limit, the parameter is undersized for the workload. If current usage climbed recently and never returned to baseline, suspect a leak. If it spiked sharply, suspect a retry storm or a lock cascade driving pool growth.

  4. Check for dead connections. Count INACTIVE sessions grouped by MACHINE. INACTIVE sessions from hosts that should have disconnected are candidates for dead-connection cleanup. Confirm whether SQLNET.EXPIRE_TIME is set in sqlnet.ora. Without it, clients that crash or get firewalled leave server processes behind as INACTIVE sessions consuming process slots indefinitely.

  5. Check for a lock contention cascade. A single idle session holding an uncommitted transaction can queue dozens of waiters. Application connection pools, seeing latency, spin up new connections that also block, exhausting PROCESSES. Query V$SESSION for EVENT LIKE 'enq: TX%' and check BLOCKING_SESSION. See the blocking sessions guide for the full chain-walking procedure.

Metrics and signals to monitor

SignalWhy it mattersWarning sign
V$RESOURCE_LIMIT.CURRENT_UTILIZATION for processesDirect measure of slot usage against the hard limitPeak above 75% of limit
V$RESOURCE_LIMIT.MAX_UTILIZATIONHigh-water mark since instance startupEquals LIMIT_VALUE: already hit at least once
OS process count for the oracle userOS-level resource that can fail before Oracle’s limitApproaching ulimit -u
SQLNET.EXPIRE_TIME configurationEnables Dead Connection Detection to reclaim zombie slotsNot set, or set too high
Listener handler availabilityAdmission path beyond the TCP probeTNS-12516, TNS-12519, services BLOCKED
INACTIVE session countDead and leaked connection candidatesGrowing without churn
Application pool saturation errorsDemand-side pressure driving the exhaustionPool-exhausted errors in app logs

For historical trending, DBA_HIST_RESOURCE_LIMIT holds snapshots of the same utilization data over time, useful for capacity planning when AWR is licensed and the retention covers the period you care about.

Fixes

Immediate: regain access

When ORA-00020 prevents even a SYSDBA connection, use the preliminary connection mode to bypass the process limit:

# Emergency connection that does not consume a process slot
sqlplus -prelim / as sysdba

From there, SHUTDOWN ABORT is the fastest path to recovery. The consequences matter: in-flight transactions are not rolled back at shutdown; SMON performs crash recovery on the next STARTUP, which can take significant time on a busy system. This is a last-resort tool, not a routine fix.

If you can connect normally, the less disruptive path is to free slots by killing sessions. ALTER SYSTEM KILL SESSION is safe in the sense that it targets a specific session, but it is still disruptive to that session’s work. Identify targets first:

-- Identify the sessions to kill first
SELECT SID, SERIAL#, USERNAME, MACHINE, PROGRAM, STATUS, SQL_ID
FROM V$SESSION
WHERE TYPE = 'USER' AND STATUS = 'INACTIVE'
ORDER BY LOGON_TIME;

-- Kill a specific session. Disruptive to that session.
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;

ALTER SYSTEM KILL SESSION does not always free the process slot immediately. Killed sessions can linger in a KILLED state until PMON cleans them up. In some cases an OS process persists without a corresponding V$SESSION entry, still consuming a slot. If PMON is not keeping up, the only reliable cleanup is an instance restart.

Short-term: stop the bleeding

  • Throttle retry logic. If the application retries failed connections in a tight loop, each retry is another failed admission attempt. Add backoff. This alone can break the cascade.
  • Kill leaky or idle sessions. Long-lived INACTIVE sessions from a misbehaving application host are the usual suspects.
  • Enable Dead Connection Detection. Set SQLNET.EXPIRE_TIME in sqlnet.ora to a nonzero value. The server then probes idle connections and reclaims slots from dead clients. This prevents zombie accumulation.

Long-term: resize and re-architect

  • Raise PROCESSES. This is static. Plan a maintenance window. SESSIONS will derive upward automatically; TRANSACTIONS will too if it is unset. If you set TRANSACTIONS explicitly, raise it to match.
  • Fix the OS ulimit. Oracle on Linux recommends an nproc of 65536 or unlimited (both hard and soft) in /etc/security/limits.conf. The default soft nproc on RHEL/OEL is often 1024, which is far too low for a production database.
  • Right-size the connection pool. The sum of all application pool maximums across all hosts must fit comfortably under PROCESSES minus background overhead minus DBA headroom. Multiple application instances each opening a large pool is a common cause.
  • Consider shared server. For workloads with many idle sessions (OLTP with think time), shared server (formerly MTS) allows more sessions per process. It has tradeoffs: certain features are unavailable or restricted . Evaluate before adopting.

Prevention

  • Leave 25% headroom. Peak usage should not exceed 75% of PROCESSES. The remainder covers background processes, DBA sessions during incidents, and unexpected spikes.
  • Set SQLNET.EXPIRE_TIME. Dead Connection Detection is the single most effective preventative for zombie-driven ORA-00020.
  • Tune ulimit proactively. Do not wait for fork failures. Set nproc to 65536 or unlimited for the oracle user before production load.
  • Monitor MAX_UTILIZATION as a capacity signal. If it is climbing toward the limit over weeks, resize before the cliff.
  • Walk blocking chains proactively. Lock contention cascades exhaust processes by driving pool growth. Catch the blocker early.
  • Match pool size to limit. Document the math: sum of pool maximums plus background overhead plus headroom must be under PROCESSES.

How Netdata helps

  • Per-second process and session utilization from V$RESOURCE_LIMIT catches the climb toward the limit before the cliff, not after.
  • MAX_UTILIZATION tracking surfaces the high-water mark so you know whether the limit has ever been hit, even if current usage looks fine.
  • Correlation with OS process count and ulimit distinguishes an Oracle-limit failure from an OS-limit failure without switching tools.
  • Listener handler availability signals (TNS-12516, TNS-12519, BLOCKED services) show the admission path failing before clients report it.
  • Anomaly detection on connection churn flags retry storms and leaks as they form, rather than after ORA-00020 fills the alert log.

Netdata’s Oracle Database monitoring with Netdata brings these signals together with per-second metrics and ML anomaly detection.