class Puma::DSL
| on_refork | :before_refork | inside |
| on_worker_shutdown | :before_worker_shutdown | inside, after |
| on_worker_boot | :before_worker_boot | inside, before |
| DSL Method | Options Key | Fork Block Location |
The following hooks have been updated:
before the worker that need to be passed to the hook when the worker is shutdown.
parameter. This allows storage of data, typically objects that are created
hook blocks/procs. If a key name is specified, a hash is passed as the last
Previously, the worker index and the LogWriter instance were passed to the
{Puma::Cluster::Worker#run} method.
hooks that run inside the forked workers. All the hooks run inside the
Puma v6 adds the option to specify a key name (String or Symbol) to thetest/config.
You can also find many examples being used by the test suite in
puts config.options # => “tcp://127.0.0.1:3002”
config.load
config = Configuration.new(config_file: “puma_config.rb”)
Resulting configuration:
port 3002
$ cat puma_config.rb
Used to load file:
puts config.options # => “tcp://127.0.0.1:3001”
config.load
end
user_config.port 3001
config = Configuration.new({}) do |user_config|
Used manually (via CLI class):
internally.
These same methods are used in Puma cli and the rack handler
The methods that are available for use inside the configuration file.
def self.ssl_bind_str(host, port, opts)
- See: ssl_bind -
def self.ssl_bind_str(host, port, opts) verify = opts.fetch(:verify_mode, 'none').to_s tls_str = if opts[:no_tlsv1_1] then '&no_tlsv1_1=true' elsif opts[:no_tlsv1] then '&no_tlsv1=true' else '' end ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify) low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : '' backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : '' if defined?(JRUBY_VERSION) cipher_suites = opts[:ssl_cipher_list] ? "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil # old name cipher_suites = "#{cipher_suites}&cipher_suites=#{opts[:cipher_suites]}" if opts[:cipher_suites] protocols = opts[:protocols] ? "&protocols=#{opts[:protocols]}" : nil keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}" keystore_additions = "#{keystore_additions}&keystore-type=#{opts[:keystore_type]}" if opts[:keystore_type] if opts[:truststore] truststore_additions = "&truststore=#{opts[:truststore]}" truststore_additions = "#{truststore_additions}&truststore-pass=#{opts[:truststore_pass]}" if opts[:truststore_pass] truststore_additions = "#{truststore_additions}&truststore-type=#{opts[:truststore_type]}" if opts[:truststore_type] end "ssl://#{host}:#{port}?#{keystore_additions}#{truststore_additions}#{cipher_suites}#{protocols}" \ "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}" else ssl_cipher_filter = opts[:ssl_cipher_filter] ? "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil v_flags = (ary = opts[:verification_flags]) ? "&verification_flags=#{Array(ary).join ','}" : nil cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(cert)}" : nil key_flags = (key = opts[:key]) ? "&key=#{Puma::Util.escape(key)}" : nil password_flags = (password_command = opts[:key_password_command]) ? "&key_password_command=#{Puma::Util.escape(password_command)}" : nil reuse_flag = if (reuse = opts[:reuse]) if reuse == true '&reuse=dflt' elsif reuse.is_a?(Hash) && (reuse.key?(:size) || reuse.key?(:timeout)) val = +'' if (size = reuse[:size]) && Integer === size val << size.to_s end if (timeout = reuse[:timeout]) && Integer === timeout val << ",#{timeout}" end if val.empty? nil else "&reuse=#{val}" end else nil end else nil end "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{password_flags}#{ssl_cipher_filter}" \ "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}#{low_latency_str}" end end
def _load_from(path)
def _load_from(path) if path @path = path instance_eval(File.read(path), path, 1) end ensure _offer_plugins end
def _offer_plugins
def _offer_plugins @plugins.each do |o| if o.respond_to? :config @options.shift o.config self end end @plugins.clear end
def activate_control_app(url="auto", opts={})
@example
activate_control_app 'unix:///var/run/pumactl.sock', { auth_token: '12345' }
@example
activate_control_app 'unix:///var/run/pumactl.sock'
@example
Check out {Puma::App::Status} to see what the app has available.
simple authentication.
will need to include that token as a query parameter. This allows for
provide an authentication token, so all requests to the control server
be communicated with to control the main server. Additionally, you can
Start the Puma control rack application on +url+. This application can
def activate_control_app(url="auto", opts={}) if url == "auto" path = Configuration.temp_path @options[:control_url] = "unix://#{path}" @options[:control_url_temp] = path else @options[:control_url] = url end if opts[:no_token] # We need to use 'none' rather than :none because this value will be # passed on to an instance of OptionParser, which doesn't support # symbols as option values. # # See: https://github.com/puma/puma/issues/1193#issuecomment-305995488 auth_token = 'none' else auth_token = opts[:auth_token] auth_token ||= Configuration.random_token end @options[:control_auth_token] = auth_token @options[:control_url_umask] = opts[:umask] if opts[:umask] end
def add_pem_values_to_options_store(opts)
To avoid adding cert_pem and key_pem as URI params, we store them on the
def add_pem_values_to_options_store(opts) return if defined?(JRUBY_VERSION) @options[:store] ||= [] # Store cert_pem and key_pem to options[:store] if present [:cert, :key].each do |v| opt_key = :"#{v}_pem" if opts[opt_key] index = @options[:store].length @options[:store] << opts[opt_key] opts[v] = "store:#{index}" end end end
def after_worker_fork(&block)
- Note: - Cluster mode only.
def after_worker_fork(&block) warn_if_in_single_mode('after_worker_fork') process_hook :after_worker_fork, nil, block, 'after_worker_fork' end
def app(obj=nil, &block)
- See: Puma::Configuration#app -
def app(obj=nil, &block) obj ||= block raise "Provide either a #call'able or a block" unless obj @options[:app] = obj end
def before_fork(&block)
- Note: - Cluster mode only.
def before_fork(&block) warn_if_in_single_mode('before_fork') @options[:before_fork] ||= [] @options[:before_fork] << block end
def bind(url)
- See: Puma::Cluster#run -
See: Puma::Runner#load_and_bind -
Other tags:
- Example: Socket permissions -
Example: Disable optimization for low latency -
Example: SSL cert for mutual TLS (mTLS) -
Example: SSL cert -
Example: Backlog depth -
def bind(url) @options[:binds] ||= [] @options[:binds] << url end
def bind_to_activated_sockets(bind=true)
- Example: Only bind to systemd activated sockets, ignoring other binds -
Example: Use any systemd activated sockets as well as configured binds -
def bind_to_activated_sockets(bind=true) @options[:bind_to_activated_sockets] = bind end
def clean_thread_locals(which=true)
Work around leaky apps that leave garbage in Thread locals
def clean_thread_locals(which=true) @options[:clean_thread_locals] = which end
def clear_binds!
def clear_binds! @options[:binds] = [] end
def custom_logger(custom_logger)
def custom_logger(custom_logger) @options[:custom_logger] = custom_logger end
def debug
Show debugging info
def debug @options[:debug] = true end
def default_host
def default_host @options[:default_host] || Configuration::DEFAULTS[:tcp_host] end
def directory(dir)
@example
The default is the current directory.
The directory to operate out of.
def directory(dir) @options[:directory] = dir.to_s end
def drain_on_shutdown(which=true)
- See: Puma::Server#graceful_shutdown -
def drain_on_shutdown(which=true) @options[:drain_on_shutdown] = which end
def early_hints(answer=true)
def early_hints(answer=true) @options[:early_hints] = answer end
def environment(environment)
@example
The default is "development".
a string.
Set the environment in which the rack's app will run. The value must be
def environment(environment) @options[:environment] = environment end
def extra_runtime_dependencies(answer = [])
- See: Puma::Launcher#extra_runtime_deps_directories -
def extra_runtime_dependencies(answer = []) @options[:extra_runtime_dependencies] = Array(answer) end
def first_data_timeout(seconds)
- See: Puma::Server.new -
def first_data_timeout(seconds) @options[:first_data_timeout] = Integer(seconds) end
def force_shutdown_after(val=:forever)
- See: Puma::Server#graceful_shutdown -
def force_shutdown_after(val=:forever) i = case val when :forever -1 when :immediately 0 else Float(val) end @options[:force_shutdown_after] = i end
def fork_worker(after_requests=1000)
- Version: - 5.0.0
Other tags:
- Note: - Cluster mode only.
def fork_worker(after_requests=1000) @options[:fork_worker] = Integer(after_requests) end
def get(key,default=nil)
def get(key,default=nil) @options[key.to_sym] || default end
def http_content_length_limit(limit)
size of the body of the request.
When no Content-Length http header is present, it is compared against the
HTTP 413 status code is returned.
If the payload size (CONTENT_LENGTH) is larger than http_content_length_limit,
This limit is compared against Content-Length HTTP header.
Specify how big the request payload should be, in bytes.
def http_content_length_limit(limit) @options[:http_content_length_limit] = limit end
def initialize(options, config)
def initialize(options, config) @config = config @options = options @plugins = [] end
def inject(&blk)
def inject(&blk) instance_eval(&blk) end
def io_selector_backend(backend)
- See: https://github.com/socketry/nio4r/blob/master/lib/nio/selector.rb -
def io_selector_backend(backend) @options[:io_selector_backend] = backend.to_sym end
def load(file)
Load additional configuration from a file
def load(file) @options[:config_files] ||= [] @options[:config_files] << file end
def log_formatter(&block)
def log_formatter(&block) @options[:log_formatter] = block end
def log_requests(which=true)
Enable request logging
def log_requests(which=true) @options[:log_requests] = which end
def lowlevel_error_handler(obj=nil, &block)
[200, {}, ["error page"]]
lowlevel_error_handler do |err|
@example
configuration file to change the default error on the server.
Use +obj+ or +block+ as the low level error handler. This allows the
def lowlevel_error_handler(obj=nil, &block) obj ||= block raise "Provide either a #call'able or a block" unless obj @options[:lowlevel_error_handler] = obj end
def max_fast_inline(num_of_requests)
the reactor to be subject to normal ordering.
The number of requests to attempt inline before sending a client back to
def max_fast_inline(num_of_requests) @options[:max_fast_inline] = Float(num_of_requests) end
def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
def mutate_stdout_and_stderr_to_sync_on_write(enabled=true) @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled end
def on_booted(&block)
puts 'After booting...'
on_booted do
@example
Code to run after puma is booted (works for both: single and clustered)
def on_booted(&block) @config.options[:events].on_booted(&block) end
def on_refork(key = nil, &block)
- Version: - 5.0.0
Other tags:
- Note: - Cluster mode with `fork_worker` enabled only.
def on_refork(key = nil, &block) process_hook :before_refork, key, block, 'on_refork' end
def on_restart(&block)
puts 'On restart...'
on_restart do
@example
This can be called multiple times to add code each time.
close log files, database connections, etc.
Code to run before doing a restart. This code should
def on_restart(&block) @options[:on_restart] ||= [] @options[:on_restart] << block end
def on_worker_boot(key = nil, &block)
- Note: - Cluster mode only.
def on_worker_boot(key = nil, &block) warn_if_in_single_mode('on_worker_boot') process_hook :before_worker_boot, key, block, 'on_worker_boot' end
def on_worker_fork(&block)
- Note: - Cluster mode only.
def on_worker_fork(&block) warn_if_in_single_mode('on_worker_fork') process_hook :before_worker_fork, nil, block, 'on_worker_fork' end
def on_worker_shutdown(key = nil, &block)
- Note: - Cluster mode only.
def on_worker_shutdown(key = nil, &block) warn_if_in_single_mode('on_worker_shutdown') process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown' end
def out_of_band(&block)
or scheduling asynchronous tasks to execute after a response.
This hook is useful for running out-of-band garbage collection
The worker doesn't accept new requests until this code finishes.
processing and there are no busy threads on the worker.
These hooks run immediately after a request has finished
Code to run out-of-band when the worker is idle.
def out_of_band(&block) process_hook :out_of_band, nil, block, 'out_of_band' end
def persistent_timeout(seconds)
- See: Puma::Server.new -
def persistent_timeout(seconds) @options[:persistent_timeout] = Integer(seconds) end
def pidfile(path)
@example
Store the pid of the server in the file at "path".
def pidfile(path) @options[:pidfile] = path.to_s end
def plugin(name)
Load the named plugin for use by this configuration
def plugin(name) @plugins << @config.load_plugin(name) end
def port(port, host=nil)
@example
Define the TCP port to bind to. Use +bind+ for more advanced options.
def port(port, host=nil) host ||= default_host bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s end
def preload_app!(answer=true)
- Note: - Cluster mode only.
def preload_app!(answer=true) @options[:preload_app] = answer end
def process_hook(options_key, key, block, meth)
def process_hook(options_key, key, block, meth) @options[options_key] ||= [] if ON_WORKER_KEY.include? key.class @options[options_key] << [block, key.to_sym] elsif key.nil? @options[options_key] << block else raise "'#{meth}' key must be String or Symbol" end end
def prune_bundler(answer=true)
- Note: - This is only supported for RubyGems 2.2+
Note: - This is incompatible with +preload_app!+.
Other tags:
- See: extra_runtime_dependencies -
def prune_bundler(answer=true) @options[:prune_bundler] = answer end
def queue_requests(answer=true)
- See: Puma::Server -
def queue_requests(answer=true) @options[:queue_requests] = answer end
def quiet(which=true)
@example
Disable request logging, if this isn't used it'll be enabled by default.
def quiet(which=true) @options[:log_requests] = !which end
def rack_url_scheme(scheme=nil)
Only necessary if X-Forwarded-Proto is not being set by your proxy
Allows setting `env['rack.url_scheme']`.
def rack_url_scheme(scheme=nil) @options[:rack_url_scheme] = scheme end
def rackup(path)
@example
The default is "config.ru".
Load +path+ as a rackup file.
def rackup(path) @options[:rackup] ||= path.to_s end
def raise_exception_on_sigterm(answer=true)
- See: Puma::Cluster#setup_signals -
See: Puma::Launcher#setup_signals -
def raise_exception_on_sigterm(answer=true) @options[:raise_exception_on_sigterm] = answer end
def restart_command(cmd)
@example
to Puma, as those are the same as the original process.
load Puma itself (ie. 'ruby -Ilib bin/puma'), not the arguments
Command to use to restart Puma. This should be just how to
def restart_command(cmd) @options[:restart_cmd] = cmd.to_s end
def set_default_host(host)
def set_default_host(host) @options[:default_host] = host end
def set_remote_address(val=:socket)
entirely in your hands.
you wish. Because Puma never uses this field anyway, it's format is
5. **\
protocol attached to it, will fall back to :socket
HAproxy PROXY protocol, version 1. If the request does not have the PROXY
4. **proxy_protocol: :v1**- set the remote address to the value read from the
Puma will fall back to the behavior of :socket
headers such as X-Forwarded-For to be used as well. If this header is absent,
Only the first word (as separated by spaces or comma) is used, allowing
`set_remote_address header: "X-Real-IP"`.
provided http header. For instance:
3. **header:
2. **:localhost** - set the remote address to "127.0.0.1"
system call), Puma will return "0.0.0.0"
if the peer disconnects between the connection being accepted and the getpeername
syscall. This is the normal behavior. If this fails for any reason (e.g.,
1. **:socket** (the default) - read the peername from the socket using the
There are 5 possible values:
slows down the handling significantly.
a kernel syscall is required which for very fast rack handlers
is configurable because to calculate the true socket peer address
Control how the remote address of the connection is set. This
def set_remote_address(val=:socket) case val when :socket @options[:remote_address] = val when :localhost @options[:remote_address] = :value @options[:remote_address_value] = "127.0.0.1".freeze when String @options[:remote_address] = :value @options[:remote_address_value] = val when Hash if hdr = val[:header] @options[:remote_address] = :header @options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_") elsif protocol_version = val[:proxy_protocol] @options[:remote_address] = :proxy_protocol protocol_version = protocol_version.downcase.to_sym unless [:v1].include?(protocol_version) raise "Invalid value for proxy_protocol - #{protocol_version.inspect}" end @options[:remote_address_proxy_protocol] = protocol_version else raise "Invalid value for set_remote_address - #{val.inspect}" end else raise "Invalid value for set_remote_address - #{val}" end end
def shutdown_debug(val=true)
out why shutdown is hanging.
threads will be written to $stdout. This can help figure
When a shutdown is requested, the backtraces of all the
def shutdown_debug(val=true) @options[:shutdown_debug] = val end
def silence_fork_callback_warning
def silence_fork_callback_warning @options[:silence_fork_callback_warning] = true end
def silence_single_worker_warning
- Note: - Cluster mode only.
def silence_single_worker_warning @options[:silence_single_worker_warning] = true end
def ssl_bind(host, port, opts = {})
- Example: For JRuby, two keys are required: +keystore+ & +keystore_pass+ -
Example: Alternatively, you can provide +cert_pem+ and +key_pem+: -
Example: Using self-signed certificate with the +localhost+ gem: -
def ssl_bind(host, port, opts = {}) add_pem_values_to_options_store(opts) bind self.class.ssl_bind_str(host, port, opts) end
def state_path(path)
@example
used by +pumactl+ to query and control the server.
Use +path+ as the file to store the server info state. This is
def state_path(path) @options[:state] = path.to_s end
def state_permission(permission)
- Version: - 5.0.0
def state_permission(permission) @options[:state_permission] = permission end
def stdout_redirect(stdout=nil, stderr=nil, append=false)
@example
stdout_redirect '/app/lolcat/log/stdout', '/app/lolcat/log/stderr'
@example
specifies whether the output is appended, the default is +false+.
Redirect +STDOUT+ and +STDERR+ to files specified. The +append+ parameter
def stdout_redirect(stdout=nil, stderr=nil, append=false) @options[:redirect_stdout] = stdout @options[:redirect_stderr] = stderr @options[:redirect_append] = append end
def supported_http_methods(methods)
- Example: Allows any method -
Example: Restricts methods to the methods in the IANA Registry -
Example: Restricts methods to the array elements -
Example: Adds 'PROPFIND' to existing supported methods -
Other tags:
- Note: - If the `methods` value is `:any`, no method check with be performed,
def supported_http_methods(methods) if methods == :any @options[:supported_http_methods] = :any elsif Array === methods && methods == (ary = methods.grep(String).uniq) && !ary.empty? @options[:supported_http_methods] = ary else raise "supported_http_methods must be ':any' or a unique array of strings" end end
def tag(string)
@example
tag 'app name'
@example
to add a tag, use an empty string.
If you do not specify a tag, Puma will infer it. If you do not want Puma
Additional text to display in process listing.
def tag(string) @options[:tag] = string.to_s end
def threads(min, max)
@example
threads 0, 16
@example
If these environment variables aren't set, the default is "0, 5" in MRI or "0, 16" for other interpreters.
(or +MIN_THREADS+ / +MAX_THREADS+ if the +PUMA_+ variables aren't set).
The default is the environment variables +PUMA_MIN_THREADS+ / +PUMA_MAX_THREADS+
requests and +max+ the maximum.
Configure +min+ to be the minimum number of threads to use to answer
def threads(min, max) min = Integer(min) max = Integer(max) if min > max raise "The minimum (#{min}) number of threads must be less than or equal to the max (#{max})" end if max < 1 raise "The maximum number of threads (#{max}) must be greater than 0" end @options[:min_threads] = min @options[:max_threads] = max end
def wait_for_less_busy_worker(val=0.005)
- Version: - 5.0.0
Other tags:
- See: Puma::ThreadPool#wait_for_less_busy_worker -
See: Puma::Server#handle_servers -
def wait_for_less_busy_worker(val=0.005) @options[:wait_for_less_busy_worker] = val.to_f end
def warn_if_in_single_mode(hook_name)
def warn_if_in_single_mode(hook_name) return if @options[:silence_fork_callback_warning] if (@options[:workers] || 0) == 0 log_string = "Warning: You specified code to run in a `#{hook_name}` block, " \ "but Puma is not configured to run in cluster mode (worker count > 0 ), " \ "so your `#{hook_name}` block did not run" LogWriter.stdio.log(log_string) end end
def worker_boot_timeout(timeout)
- See: Puma::Cluster::Worker#ping_timeout -
Other tags:
- Note: - Cluster mode only.
def worker_boot_timeout(timeout) @options[:worker_boot_timeout] = Integer(timeout) end
def worker_check_interval(interval)
- See: Puma::Cluster#check_workers -
Other tags:
- Note: - Cluster mode only.
def worker_check_interval(interval) @options[:worker_check_interval] = Integer(interval) end
def worker_culling_strategy(strategy)
- See: Puma::Cluster#cull_workers -
Other tags:
- Note: - Cluster mode only.
def worker_culling_strategy(strategy) stategy = strategy.to_sym if ![:youngest, :oldest].include?(strategy) raise "Invalid value for worker_culling_strategy - #{stategy}" end @options[:worker_culling_strategy] = strategy end
def worker_shutdown_timeout(timeout)
- See: Puma::Cluster::Worker#term -
Other tags:
- Note: - Cluster mode only.
def worker_shutdown_timeout(timeout) @options[:worker_shutdown_timeout] = Integer(timeout) end
def worker_timeout(timeout)
- See: Puma::Cluster::Worker#ping_timeout -
Other tags:
- Note: - Cluster mode only.
def worker_timeout(timeout) timeout = Integer(timeout) min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval]) if timeout <= min raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})" end @options[:worker_timeout] = timeout end
def workers(count)
- See: Puma::Cluster -
Other tags:
- Note: - Cluster mode only.
def workers(count) @options[:workers] = count.to_i end