rubyrackpuma

Puma Server: Facing Address already in use error while starting two instances on different ports on my local machine


I am trying to start two instances of Puma server (one API and another WebApp) (through hanami gem based application) on my development machine but unable to start the server for WebApp successfully. API server starts normally but WebApp server shows error Address already in use - bind(2) for "127.0.0.1" port 9293.

Please find below the server startup logs.

API App Logs

jignesh@jignesh-Latitude-7290:~/hanami_projects/my_api_app$ bundle exec hanami server --port=2400
13:59:36 - INFO - Using Guardfile at /......./my_api_app/Guardfile.
13:59:36 - INFO - Puma starting on port 2400 in development environment.
13:59:36 - INFO - Guard is now watching at '/......./my_api_app'
[15170] Puma starting in cluster mode...
[15170] * Puma version: 6.2.1 (ruby 3.1.2-p20) ("Speaking of Now")
[15170] *  Min threads: 5
[15170] *  Max threads: 5
[15170] *  Environment: development
[15170] *   Master PID: 15170
[15170] *      Workers: 2
[15170] *     Restarts: (✔) hot (✖) phased
[15170] * Preloading application
[15170] * Listening on http://0.0.0.0:2400
[15170] Use Ctrl-C to stop
[15170] * Starting control server on http://127.0.0.1:9293
[15170] * Starting control server on http://[::1]:9293
[15170] - Worker 0 (PID: 15177) booted in 0.0s, phase: 0
[15170] - Worker 1 (PID: 15179) booted in 0.0s, phase: 0
[15170] ! Terminating timed out worker (worker failed to check in within 60 seconds): 15177
[15170] - Worker 0 (PID: 25100) booted in 0.0s, phase: 0

WebApp Logs

jignesh@jignesh-Latitude-7290:~/hanami_projects/my_web_app$ bundle exec hanami server
15:32:31 - INFO - Using Guardfile at /......./my_web_app/Guardfile.
15:32:32 - INFO - Puma starting on port 2300 in development environment.
15:32:32 - INFO - Guard is now watching at '/......./my_web_app'
[25763] Puma starting in cluster mode...
[25763] * Puma version: 6.2.2 (ruby 3.1.2-p20) ("Speaking of Now")
[25763] *  Min threads: 5
[25763] *  Max threads: 5
[25763] *  Environment: development
[25763] *   Master PID: 25763
[25763] *      Workers: 2
[25763] *     Restarts: (✔) hot (✖) phased
[25763] * Preloading application
[25763] * Listening on http://127.0.0.1:2300
[25763] Use Ctrl-C to stop
/home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/binder.rb:335:in `initialize': Address already in use - bind(2) for "127.0.0.1" port 9293 (Errno::EADDRINUSE)
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/binder.rb:335:in `new'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/binder.rb:335:in `add_tcp_listener'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/binder.rb:329:in `block in add_tcp_listener'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/binder.rb:328:in `each'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/binder.rb:328:in `add_tcp_listener'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/binder.rb:164:in `block in parse'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/binder.rb:147:in `each'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/binder.rb:147:in `parse'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/runner.rb:78:in `start_control'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/cluster.rb:410:in `run'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/launcher.rb:194:in `run'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/lib/puma/cli.rb:75:in `run'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/gems/puma-6.2.2/bin/puma:10:in `<top (required)>'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/bin/puma:25:in `load'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/bin/puma:25:in `<main>'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/bin/ruby_executable_hooks:22:in `eval'
    from /home/jignesh/.rvm/gems/ruby-3.1.2@my_web_app/bin/ruby_executable_hooks:22:in `<main>'

Following is the Puma config files app/config/puma.rb. In both the application the configuration is identical.

# frozen_string_literal: true

max_threads_count = ENV.fetch("HANAMI_MAX_THREADS", 5)
min_threads_count = ENV.fetch("HANAMI_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

port        ENV.fetch("HANAMI_PORT", 2300)
environment ENV.fetch("HANAMI_ENV", "development")
workers     ENV.fetch("HANAMI_WEB_CONCURRENCY", 2)

on_worker_boot do
  Hanami.shutdown
end

preload_app!

So for my WebApp I tried making following changes to my_web_app/config/puma.rb based on the suggestions in https://github.com/puma/puma/issues/2113

#port        ENV.fetch("HANAMI_PORT", 2300)
bind        "tcp://127.0.0.1:2300"

but no luck.

Exploring more I found few more resources at following locations

https://github.com/puma/puma/issues/782 https://github.com/puma/puma/issues/1022 https://github.com/puma/puma/pull/1318

but couldn't get exactly what I should do in my case.

Can anybody please help me resolving this?

Just in case it helps following are the contents of my /etc/hosts


127.0.0.1   localhost
127.0.1.1   jignesh-Latitude-7290
127.0.0.1 api.some_api.local

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Thanks.


Solution

  • When puma starts it has two ports that listen: the HTTP server and the control server. You are configuring a different port for the HTTP server but not for the control server. That causes the error you see because the second server is attempting to start a control server on a port that is already in use.

    Check the documentation on Control/Status Server:

    Puma has a built-in status and control app that can be used to query and control Puma.

    $ puma --control-url tcp://127.0.0.1:9293 --control-token foo
    

    Puma will start the control server on localhost port 9293. All requests to the control server will need to include control token (in this case, token=foo) as a query parameter. This allows for simple authentication. Check out Puma::App::Status or status.rb to see what the status app has available.

    Thus the answer is that you need to start the second app with the --control-url option, e.g.:

    bundle exec puma --port=2400
    

    and

    bundle exec puma --control-url tcp://127.0.01:9294 --port=2300
    

    But I would recommend that you specifically define all options on the command line for each for clarity, and then I'd flip the order so that you know that 9293 is for 2300 since they both have 3 and 9294 is for 2400 since they both have 4:

    bundle exec puma --control-url tcp://127.0.01:9293 --port=2300
    bundle exec puma --control-url tcp://127.0.01:9294 --port=2400
    

    There is no option in puma.rb that allows this option to be set so it must be configured from the command line. You're presently using bundle exec hanami ... rather than bundle exec puma ... so there are two possible paths to make this flag work:

    1. You run bundle exec hanami --control-url ... and it successfully passes those flags to Puma (but I don't know if this will work as I don't have a hanami app to test it with)
    2. You run bundle exec puma --control-url ... and have to configure puma.rb to load your hanami app (which is explained in the answer to How to configure Puma for a Hanami Application?)

    I recommend trying #1 first and if it doesn't work then look at #2.

    Either way, defining the --control-url flag will resolve this error.