· Troubleshooting · 12 min read
Clash Port Already in Use on Startup: Find the Process and Change mixed-port
Use netstat, lsof, or ss to find processes using ports such as 7890 on Windows, macOS, and Linux, then update system proxy and browser settings after changing the port.
Check the logs first: identify the port, address, and error
If Clash or Clash Meta (now called mihomo) fails to bind a port on startup, first identify which listener failed. 7890 is often configured as the local proxy port, but it is not required, nor is it the server port for a remote node. Changing the local proxy port usually does not require changing node addresses, passwords, or proxy rules in your subscription.
Look in the client's logs for terms such as listen, bind, and address already in use. Note the full local address and port. For example, this log indicates a failed bind on the TCP IPv4 loopback address:
listen tcp 127.0.0.1:7890: bind: address already in use
Windows may also show Only one usage of each socket address. If the message says permission denied or An attempt was made to access a socket in a way forbidden by its access permissions, check permissions, security policies, or system-excluded ports too. Don't assume another Clash process is using the port.
| Config field | Purpose | Effect of a bind failure |
|---|---|---|
mixed-port | Shared HTTP and SOCKS5 listener | Apps using this listener can't connect to the proxy |
port / socks-port | Separate HTTP / SOCKS listener | The corresponding proxy protocol is unavailable |
external-controller | Controller API, commonly 9090 | The client UI may be unable to connect to the core |
dns.listen | Built-in DNS listening address | Related DNS queries may fail |
Check the protocol, local address, and port—not just the port number.
127.0.0.1:7890,0.0.0.0:7890, and[::]:7890have different listening scopes. Wildcard addresses and dual-stack listeners can conflict with more specific addresses.
Windows: Find the process with netstat and PowerShell
List listeners, then check the PID
Open PowerShell and run the commands below. The first gives a quick overview; the second filters for the local TCP port. You may need to run the terminal as an administrator to see full details for other processes.
netstat -ano -p tcp | findstr ":7890"
Get-NetTCPConnection -LocalPort 7890 -State Listen -ErrorAction SilentlyContinue |
Select-Object LocalAddress, LocalPort, State, OwningProcess
The last column in netstat shows the process ID (PID). Note that findstr may also match remote addresses or strings such as 78900, so confirm that the Local Address column shows exactly 7890 and the state is LISTENING. TIME_WAIT is a connection state; don't end a process based on it.
Suppose the PID is 4321. Next, check the process name, executable path, and command line. 4321 is just an example—replace it with the PID you actually find before running the command.
tasklist /FI "PID eq 4321"
Get-CimInstance Win32_Process -Filter "ProcessId = 4321" |
Select-Object Name, ExecutablePath, CommandLine
If the process is mihomo launched by another proxy client, quit that client from its system tray menu or stop its background service. Closing the window may not stop the core. If the PID belongs to a system service or unfamiliar software, check its path and purpose before taking action; don't force-kill it.
No listener found, but the bind error persists
- Check whether the log actually refers to port 7891, 9090, or another port—not 7890.
- Check whether the client repeatedly restarts the core after a failure. Your query may have caught the brief interval while the process was shutting down.
- If the log specifically mentions UDP, run
Get-NetUDPEndpoint -LocalPort 7890to check it; don't check TCP alone. - If the error is related to access permissions, use the command below to view the IPv4 TCP excluded port ranges. If the port falls within a range, choosing another available port is generally safer than removing a system reservation.
netsh interface ipv4 show excludedportrange protocol=tcp
macOS: Use lsof to find listeners and background services
Run the command below in Terminal. -nP prevents addresses and ports from being converted to names, while -sTCP:LISTEN shows listening sockets only, making it easier to distinguish local services from ordinary outbound connections.
sudo lsof -nP -iTCP:7890 -sTCP:LISTEN
In the output, COMMAND is the process name, PID is its process ID, and NAME shows the listening address. If you see *:7890, the process isn't bound to just one specific local interface. Next, inspect the full command line to find which client or startup item launched the core:
ps -p 4321 -o pid,ppid,user,command
If it's a leftover process from an old client, quit the app normally first. If it keeps running, select the confirmed process in Activity Monitor and try quitting it there. From the command line, you can send kill -TERM 4321 to the actual PID to give it a chance to shut down cleanly. Don't use kill -9 as your first step.
If a new PID appears right after you quit the process, something is probably restarting it automatically. On macOS 13 and later, check background items under System Settings → General → Login Items (called Login Items & Extensions in some versions). For services installed with Homebrew, also check brew services list. Disable the service through its own controls instead of repeatedly killing child processes.
Ubuntu and Linux: Use ss to find systemd services and duplicate cores
On Ubuntu, use ss to check TCP listeners. Adding sudo usually reveals the PID and process name for processes owned by other users; without elevated permissions, you may see the socket but not the process.
sudo ss -ltnp 'sport = :7890'
sudo ss -lunp 'sport = :7890'
The first command checks TCP; the second checks UDP. TCP output uses LISTEN to indicate a listener, while UDP typically shows UNCONN—the state labels differ. On systems with lsof installed, you can also use the same command as on macOS: sudo lsof -nP -iTCP:7890 -sTCP:LISTEN.
Desktop client and systemd running at the same time
A common conflict occurs when a systemd service left over from a server setup is already using 7890 and a desktop client tries to start another core. If the PID is 4321, check its command line and service before deciding which one to keep:
ps -p 4321 -o pid,ppid,user,args
sudo systemctl status 4321
Use systemctl status with the PID to find its unit. If you confirm it belongs to a custom service named mihomo.service and want to use the desktop client instead, stop the service:
sudo systemctl stop mihomo.service
sudo ss -ltnp 'sport = :7890'
Use the service name shown on your system. If you no longer want it to start at boot, run sudo systemctl disable mihomo.service as well; running disable alone won't stop the current process. For a user service, use systemctl --user. If a service is configured to restart automatically, killing its core will often just trigger another launch.
If the listener belongs to a container, check the container's host port mapping too. Don't stop the entire container runtime just to free a proxy port. Change the relevant container mapping or assign a different local proxy port instead.
Change mixed-port: distinguish UI settings from the active config
If you need to keep the process using the port, change Clash's local listener from 7890 to 17890. 17890 is only an example and may already be in use on your device; check it with the commands above before switching.
Change it in the GUI client settings first
Settings labels vary by client, but the port is usually under Settings → Ports, Proxy Ports, or Core Settings. Make sure you're changing the mixed port, not the controller port. The client may write this setting to a generated config and override the field in your subscription, so editing only the downloaded subscription YAML may not work.
For standalone mihomo, edit the top-level field
For a core that reads its config file directly, edit the file specified by the -f startup argument. If it uses the -d working directory, check which config file in that directory is actually loaded. This example shows the listener field only; it is not a complete config with nodes and rules:
mixed-port: 17890
allow-lan: false
bind-address: 127.0.0.1
mode: rule
log-level: info
- Put
mixed-portat the YAML root level. Don't add ageneral:section or indent it underdns:. - If the original config also has separate listeners such as
portorsocks-port, check each one for conflicts. Changing the mixed port won't change the other listeners automatically. - After changing a top-level listener, apply the config again or restart the core as required by your client. Switching proxy nodes alone won't necessarily update the listener.
- Subscription updates may overwrite manual edits. If your client supports overrides or config merging, save the local port in its persistent settings or override layer.
For a standalone deployment, run mihomo -t -f /etc/mihomo/config.yaml to check whether the config parses; adjust the path for your setup. A successful config test doesn't guarantee the port will be available at runtime, so still check the startup log and listener status.
Update system proxy, browser, and terminal port settings
After changing the core to 17890, apps still connecting to 127.0.0.1:7890 will typically switch from a core startup error to a proxy connection refused error. Update every place where you entered the old port manually—not just the client UI.
| How it's used | Where to check |
|---|---|
| Windows system proxy | Settings → Network & internet → Proxy → Manual proxy setup |
| macOS system proxy | System Settings → Network → Current connection → Details → Proxies |
| Ubuntu GNOME | Settings → Network → Network Proxy; check the manual proxy port |
| Firefox manual proxy | Settings → General → Network Settings → Settings |
| Browser proxy extension | HTTP / SOCKS5 server address and port in the active profile |
| Terminal and developer tools | Proxy environment variables, Git config, and per-app proxy settings in IDEs and package managers |
If the client manages your system proxy, first confirm the new listener is working, then toggle System Proxy off and back on so the client can write the updated address. In Firefox, Use system proxy settings usually means you don't need to enter the port separately; update it yourself if you use manual proxy settings.
In the current shell on Linux or macOS, update the environment variables as needed. It's normal for https_proxy to use http://: this tells the app to create an HTTPS tunnel through an HTTP proxy; it doesn't downgrade the destination site to HTTP.
export http_proxy=http://127.0.0.1:17890
export https_proxy=http://127.0.0.1:17890
export all_proxy=socks5h://127.0.0.1:17890
These commands affect only the current shell and its child processes; already-open apps won't update automatically. Check your shell startup files for the old value too. To inspect Git settings and their sources, run git config --show-origin --get-regexp '.*proxy.*', then change the relevant scope so you don't update only the global config and miss a repository-level setting.
TUN mode routes traffic through a virtual network interface; it works differently from explicit HTTP/SOCKS listeners. Enabling TUN won't free a port that's already in use. If the config still asks to listen on 7890, that listener can still fail. And if some apps work in TUN mode, don't assume your old system proxy settings are no longer relevant.
Verify the fix: listener, requests, and persistence after restart
- Check the listening process. Use the appropriate system command to check 17890, confirm the listener is the core you intend to keep, and verify whether it's bound to the loopback or a wildcard address.
- Send an explicit proxy request. Use the commands below to avoid uncertainty about whether an app follows the system proxy, and test the HTTP and SOCKS5 listeners separately.
- Check the client's connection log. Confirm the test request reaches the core, then check the matched rule and outbound route. Connecting to the local listener doesn't guarantee the remote node is available.
- Test your everyday apps. Reopen your browser or terminal and check that it no longer points to 7890.
- Check restart and subscription persistence. Restart the client and check the port again. After updating your subscription, make sure the persistent settings haven't been overwritten.
curl --noproxy "" --proxy http://127.0.0.1:17890 --connect-timeout 5 --max-time 20 -I https://example.com/
curl --noproxy "" --proxy socks5h://127.0.0.1:17890 --connect-timeout 5 --max-time 20 -I https://example.com/
In Windows PowerShell, use curl.exe explicitly in place of curl to avoid parameter differences caused by the alias in older PowerShell versions. --noproxy "" prevents proxy-bypass settings from affecting the test. The 5-second and 20-second values are the connection and total request timeouts, not speed test results.
If the connection is refused, keep checking the local listener and port. If the proxy connection succeeds but the request later times out, investigate rules, nodes, DNS, and reachability of the destination. If the failing port is actually the controller API at 9090, update the controller address and client connection settings instead of repeatedly changing mixed-port. Follow this sequence—check the logs, find the process, resolve the duplicate service or change the port, update apps, then verify—to separate local bind errors from subsequent network issues.