How to Find Deadlock: A Practical Step-by-Step Guide for Devs and Sysadmins

Imagine you get a 2 a.m. alert that your company’s core SaaS platform is unresponsive. New user signups are failing, payment processing is stuck, and your support queue is blowing up with frustrated messages. You check the basic metrics first: CPU isn’t maxed out, there’s plenty of memory left, and none of your core services have crashed. This is the exact scenario where you need to know how to find deadlock fast, before the outage costs you thousands in lost revenue and customer trust. Deadlocks aren’t just a textbook concept from your operating systems 101 class; they’re a common production issue that hits everything from custom Python apps to enterprise Oracle databases, and they’re notoriously hard to spot if you don’t know what to look for.

First, Rule Out Non-Deadlock Issues That Mimic Deadlock Symptoms

Before you start digging through process logs and resource allocation tables, take 10 minutes to rule out issues that look exactly like deadlock but have much simpler fixes. I’ve seen engineering teams waste half a day troubleshooting a supposed deadlock earlier this year, only to find the root cause was a misconfigured network firewall that was dropping requests between their app server and database. You don’t want to make that same mistake. If all of these pre-checks come back clean, you’re ready to learn how to find deadlock in your specific environment.

The first red flags that you might be dealing with a deadlock and not another issue are uninterrupted 100% resource utilization for a single process thread, no process crash logs or error messages, and zero progress for stuck requests even after you wait 10+ minutes for processes to complete. If you see all three of these, you can move past the pre-checks, but first confirm none of the following common issues are to blame:

  • Infinite loops in application code that hold a resource indefinitely without releasing it
  • Network timeouts that make it look like a resource is locked when it’s just unresponsive
  • Misconfigured resource access policies that block legitimate requests without a circular wait
  • Out-of-memory errors that pause process execution without any resource locking conflict

To rule these out, run a quick check of your application error logs for uncaught exceptions, test network connectivity between all connected services, and run a memory usage scan across all your core servers. It only takes a few minutes, and it will save you from wasting hours chasing the wrong problem.

How to Find Deadlock in 3 Common Production Environments

Deadlock detection works a little differently depending on what environment you’re working in, because different systems track resource allocation and process states in different ways. The process for finding a deadlock in a Linux operating system is nothing like the process for finding one in a PostgreSQL database, so it’s important to use the right tools for your use case. No matter what stack you’re working with, the core logic of how to find deadlock stays the same: look for a circular wait between processes holding and requesting resources.

If you’re working with a standard Linux or Windows operating system, the easiest way to spot a deadlock is to track process and thread states over a 5 minute window. On Linux, you can use the ps command with thread flags to see which threads are holding mutex locks and which are waiting for locks that never get released. You can also use tracing tools like bpftrace to map out which threads are holding which resources, and cross-reference that list to find a circular wait pattern. That pattern, where Process A holds Resource 1 and waits for Resource 2, while Process B holds Resource 2 and waits for Resource 1, is the core marker of a deadlock.

For database environments, the process is even simpler, because most modern databases have built-in deadlock tracking tools. For MySQL or MariaDB using the InnoDB engine, you can pull the database deadlock log directly with a simple SHOW ENGINE INNODB STATUS query. This log will show you exactly which transactions are holding locks, which are waiting for locks, and what queries are involved in the deadlock. For PostgreSQL, you can query the pg_locks system table to map active locks to waiting transactions, and spot the circular wait pattern in just a few minutes. Most cloud managed databases like AWS RDS will also surface deadlock events directly in your console, so you don’t have to run manual queries at all.

If you’re working with custom application code, especially in languages like Java or C# that use explicit thread locking, you can use built-in profiling tools to find deadlocks. For Java, the jstack utility will generate a full thread dump that explicitly flags deadlocked threads for you, no manual analysis required. For Python or Node.js apps, you can use async profiling tools to track which coroutines are holding event loop locks and waiting for other locks that never get released. I’ve found that application-level deadlocks are the easiest to fix once you find them, because you can usually adjust the lock acquisition order in your code to eliminate the circular wait entirely.

Step-by-Step Workflow to Confirm a Deadlock Is Occurring

Once you suspect you have a deadlock, you need to confirm it before you take any recovery steps, because restarting services or killing transactions can cause data loss if you’re wrong. This workflow works for every environment, and takes less than 15 minutes to run even if you’ve never had to find deadlock in that system before. If you’re not sure how to find deadlock in your specific tech stack, you can usually find official documentation for deadlock detection tools from your software provider to pair with this workflow.

First, map out all currently held resources and all waiting process requests. For operating systems, this means pulling a list of all active mutex, semaphore, or file locks, and matching them to the threads that hold them and the threads that are waiting for them. For databases, this means pulling the full list of active locks and waiting transactions. You don’t need to map every single resource, just the ones that are related to the stuck processes you’ve already identified.

Next, build a deadlock wait-for graph from the data you collected. Each node in the graph is a process or transaction, and each directed edge from Node A to Node B means Process A is waiting for a resource held by Process B. If you find any cycle in this graph, that’s a confirmed deadlock. You don’t need to draw a formal graph if you’re in a hurry; you can just trace the wait chain manually until you find a process that’s waiting for a resource held by another process in the same chain.

Finally, confirm that none of the processes in the cycle are making any progress over a 3 to 5 minute window. Sometimes processes will hold locks for a long time for legitimate reasons, like running a large bulk database update, so you need to make sure they’re truly stuck. If none of the processes in the cycle are advancing, and none are releasing their locks, you’ve got a confirmed deadlock.

One pro tip here: if you’re dealing with a non-critical system, you can test your conclusion by killing one of the processes in the cycle. If the rest of the processes in the chain start working again immediately, you know you were right. Don’t do this for systems that handle sensitive transactional data though, because killing a transaction mid-execution can lead to corrupted data if you don’t have proper rollback mechanisms in place.

Common Mistakes to Avoid When Looking for Deadlocks

Even experienced engineers make mistakes when they’re trying to find deadlock in complex distributed systems, because they’re such a tricky issue to spot. Most of these mistakes add unnecessary time to your troubleshooting process, but some can cause even more damage than the original deadlock, so it’s worth knowing what to avoid.

The first big mistake is relying on a single data point to confirm a deadlock. I once saw a junior engineer kill 12 active database transactions because he saw a circular wait in the pg_locks table, but he didn’t wait to confirm the processes were stuck. It turned out the transactions were part of a scheduled bulk data migration, and killing them forced the team to restore the database from a backup, which added 3 hours of extra downtime. Always wait to confirm processes are not making progress before you take any action.

Another common mistake is forgetting to check for nested locks across different systems. A lot of deadlocks don’t happen within a single database or operating system; they happen across two connected systems. For example, your app server might hold a database lock while waiting for a cache lock, and your cache server might hold a cache lock while waiting for a database lock. If you only check the database for deadlocks, you’ll never find the issue. Always expand your search to all connected systems if you don’t find a deadlock in the first environment you check.

The last mistake to avoid is skipping isolated test environment replication if you have time. If the deadlock isn’t causing a full production outage, take the time to replicate the issue in a staging environment before you make any code or configuration changes. This lets you confirm that your fix works, without risking breaking production even more. It also lets you log exactly what caused the deadlock, so you can prevent the same issue from happening again in the future.

Deadlocks can be incredibly frustrating to troubleshoot, especially when you’re under pressure to fix a production outage fast. But if you follow the steps we’ve outlined, you can cut down your troubleshooting time from hours to minutes, and avoid the common mistakes that make outages worse. The next time you’re dealing with unresponsive processes and no obvious error logs, remember to rule out mimicking issues first, use the right detection tools for your environment, confirm the circular wait pattern, and avoid rushing to take action before you’re sure. Knowing how to find deadlock quickly is one of the most valuable skills you can have as an engineer or sysadmin, and it will save you and your team countless hours of stress down the line.