What Causes a 504 in Odoo
Unlike a 503 (Odoo not running), a 504 Gateway Timeout means Odoo is running but took longer to respond than nginx was willing to wait. The default proxy_read_timeout in nginx is 60 seconds — short enough to catch almost any legitimate slow operation in Odoo.
The three main culprits are:
- Long-running reports — PDF generation via wkhtmltopdf on large datasets.
- Slow database queries — missing indexes, table bloat, or unvacuumed tables.
- nginx proxy_read_timeout too low — legitimate operations that exceed 60 s.
Step 1 — Identify the Slow Operation
Check the Odoo log for the request that timed out:
tail -f /var/log/odoo/odoo.log | grep -E "slow|took|timeout|warning"
Odoo logs a warning for any RPC call exceeding a threshold. You should see something like:
WARNING mydb odoo.service.server: worker timeout (pid:12345)
Note the PID and correlate it with the operation (the preceding log lines will show the model and method being called).
Step 2 — Increase nginx proxy_read_timeout
For report generation, a timeout of 120–300 seconds is reasonable:
# /etc/nginx/sites-enabled/odoo
location / {
proxy_pass http://127.0.0.1:8069;
proxy_read_timeout 300s;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
}
sudo nginx -t && sudo systemctl reload nginx
Do not set this to an arbitrarily large value — it masks real problems. 300 s is a sensible ceiling for legitimate Odoo operations.
Step 3 — Tune Odoo Worker Timeouts
Odoo itself kills workers that exceed its own timeout:
# /etc/odoo/odoo.conf
limit_time_cpu = 600
limit_time_real = 1200
limit_time_real_cron = 0
limit_time_real is the wall-clock limit per request. Set it higher than proxy_read_timeout so nginx, not Odoo, is the authority on when to give up on a request.
Step 4 — Find Slow PostgreSQL Queries
Enable pg_stat_statements and look for the worst offenders:
-- In psql connected to your Odoo database:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 20;
Queries taking more than 1 second are worth investigating. Common Odoo offenders include:
- Full table scans on
mail_message(add index onres_id, model). - Missing index on
stock_move.product_id. - Unvacuumed tables with high dead-tuple counts.
-- Check for missing autovacuum
SELECT relname, n_dead_tup, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC LIMIT 20;
-- Force vacuum if needed
VACUUM ANALYZE sale_order;
See Optimize PostgreSQL for Odoo for a complete tuning guide.
Step 5 — Optimise Report Generation
wkhtmltopdf is single-threaded and CPU-bound. For large reports:
- Run reports during off-peak hours using scheduled actions.
- Reduce the dataset (add filters before printing).
- Upgrade to a server with faster single-core performance.
- Consider the Odoo Enterprise report engine (faster for complex layouts).
Step 6 — Check for Lock Contention
SELECT pid, wait_event_type, wait_event, state, query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock';
If rows appear, a transaction is blocking others. Identify the blocking PID and either wait or terminate it:
SELECT pg_terminate_backend(blocking_pid);
How DeployMonkey Handles Slow Requests
DeployMonkey configures nginx and Odoo timeouts correctly out of the box for every instance. PostgreSQL is co-located on a low-latency SSD volume, and pg_stat_statements is pre-enabled so you can investigate slow queries immediately. The monitoring dashboard surfaces p95 response-time trends so you catch degradation before users do.
Start for free at deploymonkey.app.
Frequently Asked Questions
Should I just set proxy_read_timeout to 3600?
No. A very high timeout means users wait a long time for errors to surface, and nginx worker connections stay open unnecessarily. Fix the underlying slowness instead; use 300 s as a practical ceiling.
Why does the 504 only happen for specific users?
Those users are likely running large reports or accessing data-heavy views (e.g., a sales analysis with years of data). Add filters or pagination to those views.
Can PostgreSQL connection pooling help?
Yes — pgBouncer reduces connection overhead but does not speed up slow queries. It helps when you have many concurrent users exhausting PostgreSQL's max_connections.
How do I tell if it is a network issue vs. a slow query?
Check pg_stat_activity while the slow request is in flight. If you see the query actively running (state = 'active'), it is a slow query. If Odoo is not even querying (idle in transaction), it is application-level processing.