- Published on
- 5 min read
> Fixing Port 8081 Already in Use: Orphaned Metro Bundler Processes
You run npx expo start or npx react-native start and instead of your familiar Metro bundler interface, you get asked whether you'd like to run on a different port. Something like:
Another process is using port 8081.
Would you like to run the app on another port instead? [Y/n]
This happens when a Metro bundler process from a previous session is still running, even though you thought you closed it. These orphaned processes are more common than you'd expect, and they can cause subtle issues if you don't clean them up.
Why Metro Gets Orphaned
Metro is supposed to shut down when you close your terminal or press Ctrl+C. But sometimes it doesn't. A few common scenarios:
If you close your terminal window directly instead of stopping Metro with Ctrl+C, the process might not receive the termination signal properly. This is especially common if you're using VS Code's integrated terminal and close the panel while Metro is running.
Crashes and force quits can leave Metro hanging. If your machine froze or you killed your terminal emulator, Metro might still be running in the background.
Running Metro in one terminal tab and forgetting about it while opening a new tab to start a fresh instance is another classic. You might have multiple Metro servers running without realizing it.
Finding the Orphaned Process
The quickest way to find what's using port 8081 is with lsof:
lsof -i :8081
This lists all processes using that port. You'll see output like:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 12345 you 23u IPv4 0x... 0t0 TCP *:sunproxyadmin (LISTEN)
The PID column (12345 in this example) is what you need. You can verify it's actually Metro by checking the full command:
ps -p 12345 -o command=
This will show something like /usr/local/bin/node /path/to/project/node_modules/@react-native-community/cli/build/bin.js start if it's Metro.
Killing the Process Properly
Once you've confirmed the PID, kill it with:
kill 12345
That's it. No flags needed.
You'll see a lot of Stack Overflow answers and forum posts telling you to use kill -9. This is almost never necessary for Metro, and it's worth understanding why.
Why You Shouldn't Default to kill -9
The -9 flag sends SIGKILL, which immediately terminates the process without giving it a chance to clean up. The process can't catch or ignore this signal. It just dies.
The default kill (without flags) sends SIGTERM, which politely asks the process to terminate. Well-behaved programs like Node.js handle this signal by cleaning up resources, closing file handles, and shutting down gracefully.
For Metro specifically, SIGTERM works fine. Metro isn't ignoring termination signals or stuck in some uninterruptible state. It's just sitting there serving bundles, unaware that you wanted it to stop. When you send SIGTERM, it shuts down immediately.
Using kill -9 as your default habit can cause problems with other processes that actually need to clean up. Database processes might not flush writes to disk. Build tools might leave partial outputs. Lock files might not get removed. For Metro it's harmless, but it's a bad habit to develop.
A One-Liner for Quick Cleanup
If you want to kill whatever is on port 8081 without looking up the PID manually:
lsof -ti :8081 | xargs kill
The -t flag makes lsof output only PIDs, which you can pipe directly to kill. If nothing is using the port, nothing happens.
Preventing Orphaned Processes
A few habits that help avoid this situation:
Always stop Metro with Ctrl+C rather than closing the terminal. This sends the proper termination signal.
If you're running Metro from a script or in the background, make sure your script handles cleanup. Expo's development server handles this better than raw Metro in most cases.
Consider using a single terminal dedicated to Metro rather than opening it in various tabs. It's easier to keep track of.
Some developers add a pre-start check to their npm scripts:
{
"scripts": {
"prestart": "lsof -ti :8081 | xargs kill 2>/dev/null || true",
"start": "expo start"
}
}
This automatically cleans up any orphaned Metro process before starting a fresh one. The || true ensures the script doesn't fail if nothing is on the port.
When You Actually Might Need -9
If plain kill doesn't work and the process is truly stuck, then kill -9 is appropriate. But verify first by running lsof -i :8081 again after your initial kill. If the process is still there after a few seconds, then escalate:
kill -9 12345
In practice, I've never needed this for Metro. If you find yourself regularly needing SIGKILL, something else is probably wrong with your development environment.
Other Ports to Watch
While 8081 is the default Metro port, you might also encounter conflicts on:
- 19000 and 19001 (Expo development server)
- 8082 or 8083 (if you've run Metro on alternate ports before)
The same lsof and kill approach works for any port.
// Continue_Learning
Testing Expo and React Native on Device Over Local Network with Bonjour
Skip Expo Go and test your development build directly on a physical device using Bonjour for automatic network discovery.
Using Vercel's Skills Library to Supercharge AI Agents for React Native
Vercel's Skills library lets you add reusable instruction sets to AI coding agents. Here's how to use it to make agents more effective for React Native development.
Configuring Claude Code Permissions for React Native Development
React Native projects require running lots of shell commands. Here's how to configure Claude Code's permission system so you're not constantly approving the same operations.
// Stay Updated
Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.