Search code examples
ruby-on-railsnginxpuma

nginx blocking puma from same port


I have a rails app running on puma and nginx. I launch nginx with sudo service nginx restart then puma/rails with rvmsudo rails server -p 80

This is my error:

`initialize': Address already in use - bind(2) for "localhost" port 80 (Errno::EADDRINUSE)

This error only occurs when nginx is already running.

The full error is:

 $rvmsudo rails server -p 80

=> Booting Puma
=> Rails 4.2.4 application starting in development on http://localhost:80
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
Puma 2.14.0 starting...
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://localhost:80
Exiting
/home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/puma/binder.rb:233:in `initialize': Address already in use - bind(2) for "localhost" port 80 (Errno::EADDRINUSE)
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/puma/binder.rb:233:in `new'
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/puma/binder.rb:233:in `add_tcp_listener'
    from (eval):2:in `add_tcp_listener'
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/rack/handler/puma.rb:33:in `run'
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/rack-1.6.4/lib/rack/server.rb:286:in `start'
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/railties-4.2.4/lib/rails/commands/server.rb:80:in `start'
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:80:in `block in server'
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:75:in `tap'
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:75:in `server'
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/railties-4.2.4/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
    from /home/my-user-name/apps/my-web-app/shared/bundle/ruby/2.1.0/gems/railties-4.2.4/lib/rails/commands.rb:17:in `<top (required)>'
    from bin/rails:4:in `require'
    from bin/rails:4:in `<main>'

Any help on how to get nginx to allow puma to talk to it would be very appreciated.

EDIT 1 nginx.conf

upstream puma {
  server unix:///home/my-user-name/apps/my-web-app/shared/tmp/sockets/my-web-app-puma.sock;
}

server {
  listen 80 default_server deferred;

  listen 443 ssl;

  # server_name example.com;
  ssl_certificate /etc/ssl/mysite/mysite.com.chained.crt;
  ssl_certificate_key /etc/ssl/mysite/mysite.key;

  root /home/my-user-name/apps/my-web-app/current/public;
  access_log /home/my-user-name/apps/my-web-app/current/log/nginx.access.log;
  error_log /home/my-user-name/apps/my-web-app/current/log/nginx.error.log info;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @puma;
  location @puma {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;

    proxy_pass http://puma;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 10M;
  keepalive_timeout 10;
}

Solution

  • Actually, let me answer this without requesting your nginx configuration. Your setup works like this:

    • nginx listens on port 80 for incoming HTTP requests
    • based on the configuration it will forward these requests to the correct application instance.
    • this is called a reverse proxy setup.

    So let's look at a generic nginx configuration that will tell us how to listen on port 80 and forward all requests to an application running on port 3000:

    # our http block to handle HTTP traffic with nginx
    http {
      include mime.types;
      default_type  application/octet-stream;
      sendfile on;
      keepalive_timeout  65;
    
      # NGinx Server Configuration
      server {
    
        listen 80; #listen on port 80
        server_name my.domain.com;  # which domain we are listening for.
    
        # Add some basic auth
        # Remove this if not needed.
        auth_basic "Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
    
        # Set up the location to map all requests to the Ruby App
        location / {
          proxy_pass http://127.0.0.1:3000;
          access_log off;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header Host $host;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
        }
      }
    }
    

    Now you need to do 2 things:

    • restart your nginx
    • start your puma application on port 3000

    Hope this explains how the 2 work together and what you need to change in your file to help them communicate.

    basically: if nginx claims port 80, puma cannot claim it and needs to run on a different port, letting nginx reroute all traffic to that port and back.