keepcalculator

06 Numbers and Powers

Fix a 504 Gateway Timeout with Nginx and Rails

Card No. 06·30354 · Updated · Standard formula, shown below

This guide explains how to diagnose and resolve HTTP 504 Gateway Timeout errors when Nginx sits in front of a Ruby on Rails application. You’ll learn the most common causes, step-by-step fixes, and how to prevent the error from recurring. Before starting, you’ll need SSH access to your server, sudo privileges, and the ability to restart Nginx and your Rails app.

What You’ll Need

  • SSH access to the server running Nginx and Rails
  • Sudo or root privileges to edit config files and restart services
  • Basic familiarity with the command line
  • Your Rails application’s deployment directory (e.g., /var/www/myapp)

Step-by-Step

  1. Identify the source of the timeout. A 504 means Nginx waited too long for the upstream (your Rails app) to respond. Check Nginx’s error log first: sudo tail -f /var/log/nginx/error.log. Look for lines containing “upstream timed out” or “504 Gateway Time-out”. This tells you which server block and upstream is affected.
  2. Check your Rails app’s response time. If the app itself is slow, the 504 is a symptom. Use time curl -I http://localhost:3000 (adjust port if needed) to see how long the app takes to respond. If it takes more than 60 seconds, you’re hitting Nginx’s default proxy timeout.
  3. Increase Nginx proxy timeouts. Edit your Nginx site config (usually in /etc/nginx/sites-available/ or /etc/nginx/conf.d/). Inside the location / block, add or modify these directives:
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 300s;

    Set proxy_read_timeout to a value that comfortably exceeds your slowest legitimate request (e.g., 300s for file uploads or reports). Save the file.

  4. Test and reload Nginx. Run sudo nginx -t to check syntax. If it says “syntax is ok”, reload with sudo systemctl reload nginx (or sudo service nginx reload).
  5. Check your Rails app’s own timeout settings. If you use Puma, the default worker_timeout is 60 seconds. If a request takes longer, Puma kills the worker and returns an error. Edit config/puma.rb and set worker_timeout 300 (or higher). Restart Puma after the change.
  6. Look for slow database queries. A 504 often comes from a query that takes minutes. Enable Rails’s slow query log (in config/environments/production.rb): config.active_record.verbose_query_logs = true and set a threshold with config.active_record.logger. Or use EXPLAIN on queries you suspect. Add indexes or optimize the query.
  7. Check if the app is stuck in a deadlock or infinite loop. Use top or htop to see if a Ruby process is consuming 100% CPU. If so, get a stack trace with kill -6 <pid> (SIGABRT) or use rbtrace to see where it’s stuck. Fix the code causing the loop.
  8. Ensure your Rails app can handle concurrent requests. If you’re using Puma with a single worker, long requests block others. Increase workers in config/puma.rb: workers ENV.fetch("WEB_CONCURRENCY") { 2 }. Restart Puma.
  9. Check for external service timeouts. If your Rails app calls an external API (e.g., payment gateway), that call might hang. Use Ruby’s Net::HTTP with a timeout: http.open_timeout = 5; http.read_timeout = 10. Or use a gem like faraday with a timeout setting.
  10. Restart everything. After changes, restart Nginx and your Rails app: sudo systemctl restart nginx and sudo systemctl restart myapp (or however your app is managed, e.g., passenger-config restart-app). Test with a request that previously timed out.

Common Mistakes

  • Only increasing Nginx timeouts without checking the app. This masks the real problem; a slow app will still time out eventually.
  • Setting timeout values too high. A 300s read timeout is fine for rare reports, but if every request takes that long, users will abandon the site.
  • Forgetting to restart Nginx after config changes. Reload is enough for config changes, but if you change Puma’s worker_timeout, you must restart Puma.
  • Ignoring the Rails log. The production log often shows the exact request that timed out and the query that was running.

Tips

  • Use a monitoring tool like New Relic or Scout to see which controller actions are slow.
  • Set up alerting for 504 errors so you know when they happen.
  • Consider background jobs for long tasks (e.g., report generation) using Sidekiq or Active Job.
  • Use a CDN or caching layer (e.g., Rails fragment caching) to reduce load on the app.
  • If you use Passenger, check its passenger_max_pool_size and passenger_pool_idle_time settings.

Troubleshooting

  • Still getting 504 after increasing timeouts? Check if the request is actually reaching Rails. Look at the Rails production log for the request. If it’s not there, the issue might be with the socket or port connection. Ensure Nginx’s proxy_pass points to the correct address (e.g., http://unix:/tmp/myapp.sock or http://127.0.0.1:3000).
  • Nginx returns 504 immediately. This usually means the upstream is down or unreachable. Check if your Rails app is running: ps aux | grep puma or sudo systemctl status myapp. If it’s down, start it.
  • Only certain requests time out. The problem is likely a specific endpoint that runs a heavy query or calls an external service. Reproduce the request in development and profile it.
  • 504s happen under high load. Your app may be running out of memory or threads. Check memory with free -h and increase Puma workers or switch to a threaded server.
  • After changing Puma config, app won’t start. Check the Puma log (log/puma.log or log/production.log) for syntax errors. Revert the change if needed.

Frequently Asked Questions

What exactly does a 504 error mean?

It means Nginx, acting as a reverse proxy, sent a request to your Rails app (the upstream) but didn’t receive a response within the configured timeout period.

Why does increasing Nginx’s timeout not fix it?

Because the real bottleneck is usually your Rails app or a database query taking too long. Increasing Nginx’s timeout only gives the app more time, but if the app is broken, it will still fail.

Should I set proxy_read_timeout to a very high value like 3600?

Only if you have legitimate long-running requests (e.g., file exports). Otherwise, it masks performance issues and ties up server resources. Better to optimize the app.

How do I find out which request is causing the timeout?

Check the Rails production log (log/production.log) around the time of the error. It will show the request path, parameters, and how long the controller action took.

Is a 504 the same as a 502 Bad Gateway?

No. A 502 means Nginx received an invalid response from the upstream (e.g., the app crashed). A 504 means the upstream didn’t respond in time.

Do I need to change both Nginx and Puma timeouts?

Yes. Nginx’s timeout is the outer limit, but Puma has its own worker timeout. If Puma kills the worker before Nginx times out, you’ll see a 504 anyway. Set both consistently.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →