App Hangs on Startup: How to Find the Blocking Connection
When an app hangs or dies right after a line like 'Connecting to upstream service...', it is waiting on a network dependency it cannot reach. Most often the config points at the wrong host or port. Compare the configured address with the port the dependency really listens on, then correct the config.
Why apps get stuck at startup
Many services check their dependencies before they accept traffic: a database, a cache, an internal API. That check is a network connection. If the address is wrong, one of two things happens:
- Connection refused - the host is reachable but nothing listens on that port. The connect fails at once.
- No answer - packets are dropped (wrong host, firewall). The connect waits.
With no timeout, it waits forever, and the app looks "running" in
psbut never serves a request.
Either way, the last line in the log is the clue.
Step 1: read the last lines of the log
curl -s http://localhost:8080/health # no answer
tail -n 8 /var/log/app.log
Example output:
Connecting to upstream service at 127.0.0.1:9999...
ERROR: Cannot reach upstream service at 127.0.0.1:9999: [Errno 111] Connection refused
FATAL: upstream unavailable, exiting
The app tells you exactly which host and port it tried. Write them down.
Step 2: find where the dependency really listens
List listening TCP ports and the process behind each one:
ss -ltnp # or: netstat -tlnp
LISTEN 0 128 0.0.0.0:5000 0.0.0.0:* users:(("python3",pid=41,fd=3))
Confirm the dependency answers on that port:
curl -s http://localhost:5000/health
If it answers on 5000 but the app dials 9999, the config is wrong, not the
dependency.
Step 3: correct the config
; config.ini
[upstream]
host = 127.0.0.1
port = 5000 ; was 9999 - nothing listens there
Restart the app (or let its supervisor restart it) and confirm:
tail -n 3 /var/log/app.log
curl -s http://localhost:8080/health
Change the config, not the dependency. Other services may already use the dependency on its current port.
When the process hangs with no log output
If the app prints nothing and never exits, find out what it is waiting on:
# Which connections does the process have open?
ss -tnp | grep <pid>
# Which system call is it blocked in?
strace -p <pid>
A connection in state SYN-SENT means the app is waiting for a host that
does not answer. For Python apps, py-spy dump --pid <pid> (if installed)
prints the current stack and shows the exact line that is blocked.
Prevent silent hangs: always set a timeout
The worst startup bug is one with no error and no progress. Always give a network call a timeout:
import socket
def check_upstream(host, port, timeout=5):
try:
with socket.create_connection((host, port), timeout=timeout):
return True
except OSError as e:
print(f'ERROR: cannot reach {host}:{port}: {e}')
return False
Most clients have a timeout option: requests.get(url, timeout=5),
psycopg2.connect(..., connect_timeout=5), redis.Redis(socket_connect_timeout=5).
Then decide what happens on failure: retry with backoff, exit with a clear
error, or start in a degraded mode. Never wait forever.
Checklist
- Read the last log line and note the host and port.
- Run
ss -ltnpto see the real listening ports. curlthe dependency directly.- Correct the host or port in the config.
- Add a timeout so the next mistake fails fast.
Do this project yourself. It's ready in a live cloud workspace in your browser - when you finish, it lands on your portfolio.
Start this project - freeWhat you'll practice
- Reading startup logs to find the address an app is trying to reach
- Finding listening ports and processes with ss and netstat
- Correcting an upstream port in a config file and confirming with curl
FAQ
Why does my app hang on startup with no error?
It is most likely blocked on a network connection with no timeout - to a database, cache, or API at an address that does not answer. Check open connections with ss -tnp and add a connect timeout so the call fails instead of waiting forever.
How do I see which port a service is listening on in Linux?
Run ss -ltnp (or netstat -tlnp). It lists every listening TCP port with the process name and PID.
What is the difference between connection refused and connection timed out?
Refused means the host answered but nothing listens on that port, so it fails at once. Timed out means no answer came back at all, usually a wrong host or a firewall dropping packets.
Should an app exit if a dependency is down at startup?
It should never hang. Either retry with backoff for a limited time, exit with a clear error so the supervisor restarts it, or start in a degraded mode and report unhealthy.
Keep learning
Do this project yourself. It's ready in a live cloud workspace in your browser - when you finish, it lands on your portfolio.
Start this project - free