Check the logs first: 7890 is the proxy port; 9090 is the control API
If Clash or a client using the mihomo core fails to start, open the core logs and look for an error line containing listen, bind, and a specific address. The message address already in use usually means something is already listening on that address and port. On Windows, you may also see “Only one usage of each socket address is normally permitted.” Note the port, bind address, and protocol before deciding whether to stop a process or change the configuration.
7890 and 9090 are common configuration values, not fixed ports used by every client. Check the active configuration and startup logs to find the ports in use. These ports serve different purposes, so don’t change them all just because one reports an error.
| Setting | Example value | Purpose and impact |
|---|---|---|
mixed-port | 7890 | Accepts HTTP proxy and SOCKS5 connections on the same port. Browsers, terminals, and system proxy settings may use it. |
external-controller | 127.0.0.1:9090 | Provides a control API for clients and web dashboards to read status and switch policies. It is not a web proxy port. |
port | 7892 | A separate HTTP proxy port; check it only if enabled. |
socks-port | 7893 | A separate SOCKS port. Changing mixed-port does not change this port. |
Windows: Find the process using netstat and PowerShell
Step 1: Check listening connections—not just the port number
Open PowerShell and run these two commands. The Local Address should end with the port you’re checking, and the state should be LISTENING. The last column shows the process ID (PID). Searching for 7890 alone may match a remote address or digits in another port, so check every column.
netstat -ano -p tcp | findstr ":7890"
netstat -ano -p tcp | findstr ":9090"
You can also filter for listeners on a local port directly in PowerShell, so established connections aren’t mistaken for a port conflict:
Get-NetTCPConnection -State Listen -LocalPort 7890,9090 -ErrorAction SilentlyContinue |
Select-Object LocalAddress,LocalPort,OwningProcess
127.0.0.1:7890 indicates a listener on the IPv4 loopback interface; 0.0.0.0:7890 means it listens on all IPv4 interfaces. The latter will usually prevent another program from binding to the same local port. Whether an IPv6 listener at [::]:7890 also affects IPv4 depends on the socket settings, so check IPv6 output too.
Step 2: Verify the executable path, then close duplicate instances
The command below uses PID 1234 as an example. Replace it with the actual PID from the previous command. PIDs change when a process restarts, so don’t copy the example and terminate that process blindly.
tasklist /FI "PID eq 1234"
Get-Process -Id 1234 | Select-Object Id,ProcessName,Path
- Another proxy client: Exit it from the system tray, then disable its startup or automatic core launch before starting the client you want to keep.
- A background core from the same client: Closing the window may only minimize it to the system tray. Use the tray menu to exit, then check whether the listener is gone.
- A service-managed process: Confirm which service owns it, then stop it through the associated client or the Services manager. If you terminate only the child process, a supervisor may start it again immediately.
- Another development tool: If the port belongs to a service you’re using, keep the service and change the Clash port instead.
If you don’t have permission to check the executable path, reopen the terminal as an administrator. Don’t terminate a system service just because its process name looks similar. Running as administrator won’t resolve every bind conflict: elevated permissions do not free a port already in use by another process.
macOS and Linux: Identify listeners with lsof or ss
On macOS, run lsof in Terminal. The same command works on Linux if the tool is installed. -nP keeps addresses and ports numeric, while -sTCP:LISTEN shows only TCP listeners, making it easier to check the PID and process name.
sudo lsof -nP -iTCP:7890 -sTCP:LISTEN
sudo lsof -nP -iTCP:9090 -sTCP:LISTEN
On Linux, you can also use ss to check both ports at once. If process details aren’t visible as a regular user, rerun the command with elevated privileges instead of repeatedly restarting the core.
sudo ss -ltnp '( sport = :7890 or sport = :9090 )'
Don’t start the same listener from both a desktop client and a systemd service
A common Linux conflict occurs when mihomo is already running as a background service and you then open a desktop client that includes its own core. They may load different configurations but both try to listen on 7890. If the service is named mihomo.service in your setup, check its status and logs first:
systemctl status mihomo --no-pager
journalctl -u mihomo -n 50 --no-pager
If you’ve confirmed that you want to use the desktop client instead, run sudo systemctl stop mihomo to stop the service. Whether to disable it at startup depends on your setup. If the service has a different name, use its actual name. On macOS, quit the old client from the menu bar first. If the core was launched by a background task, stop it using the same management method; otherwise, it may start again after you terminate it.
Change mixed-port: Update the configuration and proxy settings
Back up first, then find out what generates the active configuration
Desktop clients may generate the core’s runtime configuration through their settings, configuration overrides, or merge features. Direct edits to a cached subscription YAML file may be overwritten when the subscription updates or the client restarts. Back up your configuration, then change the port using the client’s persistent settings. Menu names vary by client, so don’t assume one client’s menu path applies to all software.
- Check that the ports you plan to use, such as
7891and9091, aren’t already in use. - If the client has a Mixed Port field, set it to
7891and save. For a manual setup, edit the configuration file that’s actually loaded. - Reload the configuration or restart the core as required by your client, then check the logs for the actual listening address.
- Update your system proxy, browser proxy extensions, command-line environment variables, and any other manually configured proxy addresses.
Merge the following snippet into the top level of your existing configuration. It is not a complete subscription you can import on its own. Keep your existing nodes, proxy groups, and rules; edit any existing keys with the same names instead of adding duplicates. The new ports are examples—make sure they’re available before using them.
mixed-port: 7891
allow-lan: false
external-controller: 127.0.0.1:9091
Also check whether separate port, socks-port, redir-port, or tproxy-port listeners are enabled. Fix the specific listener named in the error. Don’t assign the same address and port to multiple listeners that need to bind separately.
Check system proxy settings and terminal variables separately
On Windows 11, check the address and port under Settings → Network & internet → Proxy → Manual proxy setup. On macOS, go to System Settings → Network → select your current network service → Details → Proxies. If your client manages the system proxy automatically, let it reapply the settings to avoid having your manual changes overwritten.
Terminal environment variables don’t update automatically when the system proxy changes. The commands below apply only to the current terminal session and are useful for testing the new port. If a startup script or development tool still uses 7890, update that separately.
$env:HTTP_PROXY = "http://127.0.0.1:7891"
$env:HTTPS_PROXY = "http://127.0.0.1:7891"
export http_proxy=http://127.0.0.1:7891
export https_proxy=http://127.0.0.1:7891
The HTTPS_PROXY variable sets a proxy for HTTPS destinations; it doesn’t mean the local proxy URL must use https://. This connects to a local HTTP proxy, so the address is still http://127.0.0.1:7891. Also check ALL_PROXY and any application-specific proxy settings. Tools vary in how they handle uppercase and lowercase variables and which takes precedence.
Change 9090: Update the control dashboard URL and authentication
If external-controller is the port in conflict, change it to 127.0.0.1:9091 and update the web dashboard’s backend URL to http://127.0.0.1:9091. Keep your existing secret and enter the matching credentials in the dashboard. Some desktop clients manage the control interface and authentication settings themselves; change them through the client to avoid mismatched frontend and core settings.
- Proxy works, but the dashboard is disconnected: Check whether the dashboard is still connecting to the old
9090port, then verify the authentication details and client logs. - The API returns 401: The HTTP service is usually responding, but the request lacks valid authentication. This doesn’t mean the port is still in use.
- Connection refused: Usually, nothing was listening at that address to accept the connection. Check whether the core is running and bound to the correct address.
Verify the changes: Test the listener, proxy, and control API separately
1. Make sure the expected process is listening on the new ports
Run the earlier commands again, replacing the ports with 7891 and 9091. Confirm that each port has a listener and that its PID belongs to the core you intend to use. If the old process is still running or your browser still uses the old port, it may look as though the issue is fixed when it isn’t.
2. Specify the proxy explicitly to rule out stale system proxy settings
In Windows PowerShell, use curl.exe to avoid environments where curl resolves to another command. On macOS and Linux, use curl. The command below connects directly to the new proxy port; --noproxy "" clears the bypass list for this request. The example URL is for checking connectivity, not measuring speed.
curl.exe --noproxy "" --proxy http://127.0.0.1:7891 --connect-timeout 5 --max-time 15 -I https://example.com
If the destination site responds, the local proxy has handled the request at least as far as the proxy entry point. However, the request may have matched a DIRECT rule, so a successful command alone doesn’t prove that a remote proxy node is working. Check the client’s connection log to see which rule and proxy group matched the domain and which route was used.
3. Check the control API separately
curl.exe --connect-timeout 5 --max-time 10 -i http://127.0.0.1:9091/version
On macOS and Linux, replace curl.exe in the command with curl. If authentication is disabled, the API should return version information. If authentication is enabled, a request without credentials may return 401. Don’t remove an existing secret just to test the API, and never share commands or log screenshots containing real credentials.
Still can’t bind with no listener present? Check reserved ports and the error type
If the log says permission denied, access permissions, or cannot assign requested address, don’t treat it as a standard port conflict. Windows excluded port ranges, binding to an address that isn’t assigned to the local machine, and security software restrictions can all cause different types of bind errors.
On Windows, use this command to check TCP excluded port ranges. If your target port falls within a range, choose an available port outside it and check the related virtualization or networking settings. Don’t delete system-managed exclusions directly.
netsh interface ipv4 show excludedportrange protocol=tcp
netsh interface ipv6 show excludedportrange protocol=tcp
If the error involves a UDP listener, commands that check only TCP won’t give you the full picture. On Windows, run Get-NetUDPEndpoint -LocalPort 7890; on Linux, run sudo ss -lunp; on macOS, run sudo lsof -nP -iUDP:7890. TCP and UDP use separate port spaces, so check the protocol named in the error.
Finally, make sure you edited the right configuration file. A service running mihomo may specify a different configuration directory in its startup arguments, while a desktop client may generate its configuration at runtime. If the old port is still listening after a restart, check where the configuration comes from and which overrides take precedence instead of trying more port numbers.
Clash port conflict checklist
- Read the logs: Note the address, port, protocol (TCP or UDP), and full error message.
- Identify the process: Use netstat, lsof, or ss to find the listening PID, then check the executable path and how it was started.
- Choose what to do: Stop an unneeded duplicate instance, or assign different ports to services that need to run side by side.
- Update the persistent configuration: Change the active mixed-port or external-controller setting so a subscription update won’t restore the old value.
- Update dependent settings: The proxy port is used by system proxy and app settings; the control port is used by the dashboard backend URL and authentication.
- Test each layer: Check the listener first, then the explicit proxy, and finally the control API and matched routing rule.
TUN mode routes traffic through a virtual network interface; it’s separate from local proxy listeners. Enabling TUN won’t free 7890 or 9090 if another process is using them. Fix the listener named in the logs first, then troubleshoot TUN permissions, routing, or DNS to keep separate issues from getting mixed up.