DevOps · Networking · Linux

Debugging Nginx 504 Gateway Timeout

April 2026 Mayank Rajput OCI · Docker · Nginx · iptables

My portfolio was live in Docker and Nginx was running fine — curl localhost returned the right HTML. But when I hit the public IP from a browser, nothing. Just a timeout. This is the full debugging story.

The Situation

I had deployed my portfolio on an Oracle Cloud Free Tier VM (1 CPU, 1 GB RAM, Ubuntu). Docker was running a container with Nginx serving static files on port 8080, with a host mapping of 80:8080. Everything checked out locally.

Step 1 — Eliminate Docker First

Testing from inside the VM confirmed Docker and Nginx were not the problem:

curl localhost
# Returns correct HTML — container is healthy

docker ps
# PORTS: 0.0.0.0:80->8080/tcp — mapping looks right

Step 2 — Check the OCI Security List

Oracle Cloud has its own network security layer — the Security List on the subnet. Port 80 (and 443) ingress rules need to be explicitly added. I confirmed they were present. Still timing out.

Step 3 — Check if Nginx is Binding Correctly

sudo lsof -i :80
# COMMAND  PID  USER  FD  TYPE  DEVICE  NODE  NAME
# nginx   1459  root  ...  *:http

Nginx was correctly listening on all interfaces (*:http). Not the issue.

Step 4 — tcpdump Reveals the Truth

Running tcpdump on the VM while hitting the public IP from my browser told the real story:

sudo tcpdump -i any port 80
# Packets arrived at the VM — but no response was sent back

Packets were making it through the OCI Security List and reaching the VM. But something on the OS level was dropping them silently.

Root Cause — iptables DROP Rule

Oracle Cloud VMs ship with a default iptables rule that drops all inbound traffic not explicitly allowed. The rule was processed before Nginx could handle the request:

sudo iptables -L INPUT --line-numbers -n
# Chain INPUT (policy ACCEPT 0 packets)
# 1  ACCEPT  all  --  0.0.0.0/0  0.0.0.0/0  state RELATED,ESTABLISHED
# 2  ACCEPT  icmp --  0.0.0.0/0  0.0.0.0/0
# 3  ACCEPT  all  --  0.0.0.0/0  0.0.0.0/0  (lo interface)
# 4  ACCEPT  tcp  --  0.0.0.0/0  0.0.0.0/0  dport 22
# 5  REJECT  all  --  0.0.0.0/0  0.0.0.0/0  <-- EVERYTHING ELSE DROPPED

Port 80 was not in the ACCEPT rules. Line 5 was rejecting it.

The Fix

sudo iptables -I INPUT 5 -p tcp --dport 80 -j ACCEPT
sudo iptables -I INPUT 6 -p tcp --dport 443 -j ACCEPT

Using -I INPUT 5 inserts the rule before the REJECT line — order matters in iptables. If you append with -A, the REJECT rule wins because it's evaluated first.

Making It Persistent

sudo apt install iptables-persistent -y
sudo netfilter-persistent save

Without this, the rules reset on reboot.

What I Learned

There are three separate firewall layers on an OCI VM — all three need to allow traffic:

tcpdump is the fastest way to determine at which layer packets are being dropped. If you see traffic arriving but no response leaving — it's the OS firewall, not the cloud security group.

← Back to Blog