The site won't load. Your first instinct is to SSH in and restart everything. Resist it for two minutes. A restart might fix it. But you won't know what broke and it will break again.
Work from the outside in. Each step below either finds the problem or rules out a whole layer.
1. Is it down for everyone?
Try from a different network. Your phone on mobile data is the easiest. Then from a terminal somewhere else:
curl -I https://myapp.example.com
Read the result:
- An HTTP status line (200, 301, 404, 502): the server answered. Skip to step 5.
- A certificate error: go to step 3.
- "Could not resolve host": DNS. Go to step 2.
- A timeout or "connection refused": the network or the server. Go to step 4.
If it loads everywhere except your own machine, flush your DNS cache or wait out a stale one. The site is fine.
2. DNS: does the name point where you think?
dig +short myapp.example.com
You should see your server's IP. If you see nothing, the record is missing or the domain expired. If you see an old IP, someone changed the record or you're looking at a cached answer.
Check the domain's expiry too. An expired domain is one of the most embarrassing outages because it's on a calendar. whois example.com | grep -i expir shows the date.
3. The certificate: is HTTPS broken?
Browsers block the page completely when the certificate is wrong, so it looks like an outage. Check what the server is really serving:
echo | openssl s_client -connect myapp.example.com:443 -servername myapp.example.com 2>/dev/null \
| openssl x509 -noout -subject -enddate
Look for an expiry date in the past or a subject that doesn't match the domain. Renewal jobs fail quietly. With certificate lifetimes falling to 47 days by 2029, there's less slack every year. Here's why that's happening.
4. The network: can you reach the port?
A timeout usually means a firewall is dropping packets. Check the cloud provider's firewall or security group first, then the server's own firewall. Someone may have tightened a rule.
Connection refused means the machine is up but nothing is listening on that port. The app or the web server crashed or never started.
Test the app's own port directly if you know it:
nc -vz 203.0.113.10 3000
5. The server: is it healthy?
SSH in now. The usual suspects, in order of how often we see them:
The disk is full. Logs grow until they eat the disk, then databases and apps start failing in strange ways.
df -h
Out of memory. Linux kills a process when RAM runs out, often the biggest one, which is usually your app or database.
sudo dmesg -T | grep -i -E "killed process|out of memory"
The process isn't running.
docker ps -a # containers: look for "Exited"
systemctl status myapp # systemd services
6. The app: what do the logs say?
If the server answers with a 500 or 502, the network is fine and the app is the problem.
docker logs --tail 100 myapp
journalctl -u myapp -n 100
The last lines before a crash usually name the cause: a missing environment variable after a deploy, a database that refused connections, a migration that didn't run.
A 502 or 504 from a proxy (Nginx, Cloudflare, a gateway) means the proxy is up but can't get an answer from your app. Same fix: look at the app.
7. Write down what happened
After it's fixed, spend five minutes on notes: what broke, how you found it, what would have caught it sooner. Most outages repeat. The second time should take a fraction of the first.
Hear about it before your users do
The worst way to learn your site is down is a message from a user. The best way is an alert, a few minutes in, that already tells you why.
Good monitoring does three things:
- Checks from outside, the way visitors reach the site, not from the server itself.
- Confirms before alerting. One failed check can be a blip. Two in a row is an outage.
- Tells you when it's over, so you're not left refreshing a page.
This is built into DomainDock for every route. We check each address every 5 minutes through the same gateway visitors use. A failure is confirmed with a re-check before the route is marked down. Then:
- Visitors see a clear status page instead of a browser error, with a way to contact support.
- Your dashboard shows the reason in plain words, like "the server refused the connection on port 3000", with a hint for fixing it.
- If the route stays down past a short window, you get an email with the reason. When it comes back, you get a second email saying so and how long it was down.
You don't configure any of it. Add a route and monitoring starts. See how it works or put your first project on a free domain.
Cheat sheet
| Symptom | Likely layer | First command |
|---|---|---|
| Could not resolve host | DNS | dig +short domain |
| Certificate warning | SSL | openssl s_client ... |
| Timeout | Firewall or network | Check security group |
| Connection refused | Process not running | docker ps -a |
| 500 error | App | docker logs |
| 502 or 504 | App behind a proxy | docker logs |
| Random failures | Disk or memory | df -h, dmesg |