1 # -*- encoding: binary -*-
8 # Implements a simple DSL for configuring a Unicorn server.
10 # See http://unicorn.bogomips.org/examples/unicorn.conf.rb and
11 # http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
12 # example configuration files. An example config file for use with
13 # nginx is also available at
14 # http://unicorn.bogomips.org/examples/nginx.conf
15 class Configurator < Struct.new(:set, :config_file, :after_reload)
17 # Default settings for Unicorn
20 :logger => Logger.new($stderr),
21 :worker_processes => 1,
22 :after_fork => lambda { |server, worker|
23 server.logger.info("worker=#{worker.nr} spawned pid=#{$$}")
25 :before_fork => lambda { |server, worker|
26 server.logger.info("worker=#{worker.nr} spawning...")
28 :before_exec => lambda { |server|
29 server.logger.info("forked child re-executing...")
32 :preload_app => false,
35 def initialize(defaults = {}) #:nodoc:
36 self.set = Hash.new(:unset)
37 use_defaults = defaults.delete(:use_defaults)
38 self.config_file = defaults.delete(:config_file)
40 # after_reload is only used by unicorn_rails, unsupported otherwise
41 self.after_reload = defaults.delete(:after_reload)
43 set.merge!(DEFAULTS) if use_defaults
44 defaults.each { |key, value| self.send(key, value) }
45 Hash === set[:listener_opts] or
46 set[:listener_opts] = Hash.new { |hash,key| hash[key] = {} }
47 Array === set[:listeners] or set[:listeners] = []
52 instance_eval(File.read(config_file), config_file) if config_file
54 # working_directory binds immediately (easier error checking that way),
55 # now ensure any paths we changed are correctly set.
56 [ :pid, :stderr_path, :stdout_path ].each do |var|
57 String === (path = set[var]) or next
58 path = File.expand_path(path)
59 test(?w, path) || test(?w, File.dirname(path)) or \
60 raise ArgumentError, "directory for #{var}=#{path} not writable"
63 # unicorn_rails creates dirs here after working_directory is bound
64 after_reload.call if after_reload
67 def commit!(server, options = {}) #:nodoc:
68 skip = options[:skip] || []
69 set.each do |key, value|
70 value == :unset and next
71 skip.include?(key) and next
72 server.__send__("#{key}=", value)
80 # sets object to the +new+ Logger-like object. The new logger-like
81 # object must respond to the following methods:
82 # +debug+, +info+, +warn+, +error+, +fatal+
83 # The default Logger will log its output to the path specified
84 # by +stderr_path+. If you're running Unicorn daemonized, then
85 # you must specify a path to prevent error messages from going
88 %w(debug info warn error fatal).each do |m|
89 new.respond_to?(m) and next
90 raise ArgumentError, "logger=#{new} does not respond to method=#{m}"
96 # sets after_fork hook to a given block. This block will be called by
97 # the worker after forking. The following is an example hook which adds
98 # a per-process listener to every worker:
100 # after_fork do |server,worker|
101 # # per-process listener ports for debugging/admin:
102 # addr = "127.0.0.1:#{9293 + worker.nr}"
104 # # the negative :tries parameter indicates we will retry forever
105 # # waiting on the existing process to exit with a 5 second :delay
106 # # Existing options for Unicorn::Configurator#listen such as
107 # # :backlog, :rcvbuf, :sndbuf are available here as well.
108 # server.listen(addr, :tries => -1, :delay => 5, :backlog => 128)
110 # # drop permissions to "www-data" in the worker
111 # # generally there's no reason to start Unicorn as a priviledged user
112 # # as it is not recommended to expose Unicorn to public clients.
113 # worker.user('www-data', 'www-data') if Process.euid == 0
115 def after_fork(*args, &block)
116 set_hook(:after_fork, block_given? ? block : args[0])
119 # sets before_fork got be a given Proc object. This Proc
120 # object will be called by the master process before forking
122 def before_fork(*args, &block)
123 set_hook(:before_fork, block_given? ? block : args[0])
126 # sets the before_exec hook to a given Proc object. This
127 # Proc object will be called by the master process right
128 # before exec()-ing the new unicorn binary. This is useful
129 # for freeing certain OS resources that you do NOT wish to
130 # share with the reexeced child process.
131 # There is no corresponding after_exec hook (for obvious reasons).
132 def before_exec(*args, &block)
133 set_hook(:before_exec, block_given? ? block : args[0], 1)
136 # sets the timeout of worker processes to +seconds+. Workers
137 # handling the request/app.call/response cycle taking longer than
138 # this time period will be forcibly killed (via SIGKILL). This
139 # timeout is enforced by the master process itself and not subject
140 # to the scheduling limitations by the worker process. Due the
141 # low-complexity, low-overhead implementation, timeouts of less
142 # than 3.0 seconds can be considered inaccurate and unsafe.
144 # For running Unicorn behind nginx, it is recommended to set
145 # "fail_timeout=0" for in your nginx configuration like this
146 # to have nginx always retry backends that may have had workers
147 # SIGKILL-ed due to timeouts.
149 # # See http://wiki.nginx.org/NginxHttpUpstreamModule for more details
150 # # on nginx upstream configuration:
151 # upstream unicorn_backend {
152 # # for UNIX domain socket setups:
153 # server unix:/path/to/unicorn.sock fail_timeout=0;
156 # server 192.168.0.7:8080 fail_timeout=0;
157 # server 192.168.0.8:8080 fail_timeout=0;
158 # server 192.168.0.9:8080 fail_timeout=0;
161 Numeric === seconds or raise ArgumentError,
162 "not numeric: timeout=#{seconds.inspect}"
163 seconds >= 3 or raise ArgumentError,
164 "too low: timeout=#{seconds.inspect}"
165 set[:timeout] = seconds
168 # sets the current number of worker_processes to +nr+. Each worker
169 # process will serve exactly one client at a time. You can
170 # increment or decrement this value at runtime by sending SIGTTIN
171 # or SIGTTOU respectively to the master process without reloading
172 # the rest of your Unicorn configuration. See the SIGNALS document
173 # for more information.
174 def worker_processes(nr)
175 Integer === nr or raise ArgumentError,
176 "not an integer: worker_processes=#{nr.inspect}"
177 nr >= 0 or raise ArgumentError,
178 "not non-negative: worker_processes=#{nr.inspect}"
179 set[:worker_processes] = nr
182 # sets listeners to the given +addresses+, replacing or augmenting the
183 # current set. This is for the global listener pool shared by all
184 # worker processes. For per-worker listeners, see the after_fork example
185 # This is for internal API use only, do not use it in your Unicorn
186 # config file. Use listen instead.
187 def listeners(addresses) # :nodoc:
188 Array === addresses or addresses = Array(addresses)
189 addresses.map! { |addr| expand_addr(addr) }
190 set[:listeners] = addresses
193 # adds an +address+ to the existing listener set.
195 # The following options may be specified (but are generally not needed):
197 # +:backlog+: this is the backlog of the listen() syscall.
199 # Some operating systems allow negative values here to specify the
200 # maximum allowable value. In most cases, this number is only
201 # recommendation and there are other OS-specific tunables and
202 # variables that can affect this number. See the listen(2)
203 # syscall documentation of your OS for the exact semantics of
206 # If you are running unicorn on multiple machines, lowering this number
207 # can help your load balancer detect when a machine is overloaded
208 # and give requests to a different machine.
212 # +:rcvbuf+, +:sndbuf+: maximum receive and send buffer sizes of sockets
214 # These correspond to the SO_RCVBUF and SO_SNDBUF settings which
215 # can be set via the setsockopt(2) syscall. Some kernels
216 # (e.g. Linux 2.4+) have intelligent auto-tuning mechanisms and
217 # there is no need (and it is sometimes detrimental) to specify them.
219 # See the socket API documentation of your operating system
220 # to determine the exact semantics of these settings and
221 # other operating system-specific knobs where they can be
224 # Defaults: operating system defaults
226 # +:tcp_nodelay+: disables Nagle's algorithm on TCP sockets
228 # This has no effect on UNIX sockets.
230 # Default: operating system defaults (usually Nagle's algorithm enabled)
232 # +:tcp_nopush+: enables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
234 # This will prevent partial TCP frames from being sent out.
235 # Enabling +tcp_nopush+ is generally not needed or recommended as
236 # controlling +tcp_nodelay+ already provides sufficient latency
237 # reduction whereas Unicorn does not know when the best times are
238 # for flushing corked sockets.
240 # This has no effect on UNIX sockets.
242 # +:tries+: times to retry binding a socket if it is already in use
244 # A negative number indicates we will retry indefinitely, this is
245 # useful for migrations and upgrades when individual workers
246 # are binding to different ports.
250 # +:delay+: seconds to wait between successive +tries+
252 # Default: 0.5 seconds
254 # +:umask+: sets the file mode creation mask for UNIX sockets
256 # Typically UNIX domain sockets are created with more liberal
257 # file permissions than the rest of the application. By default,
258 # we create UNIX domain sockets to be readable and writable by
259 # all local users to give them the same accessibility as
260 # locally-bound TCP listeners.
262 # This has no effect on TCP listeners.
264 # Default: 0 (world read/writable)
265 def listen(address, opt = {})
266 address = expand_addr(address)
267 if String === address
268 [ :umask, :backlog, :sndbuf, :rcvbuf, :tries ].each do |key|
269 value = opt[key] or next
271 raise ArgumentError, "not an integer: #{key}=#{value.inspect}"
273 [ :tcp_nodelay, :tcp_nopush ].each do |key|
274 (value = opt[key]).nil? and next
275 TrueClass === value || FalseClass === value or
276 raise ArgumentError, "not boolean: #{key}=#{value.inspect}"
278 unless (value = opt[:delay]).nil?
280 raise ArgumentError, "not numeric: delay=#{value.inspect}"
282 set[:listener_opts][address].merge!(opt)
285 set[:listeners] << address
288 # sets the +path+ for the PID file of the unicorn master process
289 def pid(path); set_path(:pid, path); end
291 # Enabling this preloads an application before forking worker
292 # processes. This allows memory savings when using a
293 # copy-on-write-friendly GC but can cause bad things to happen when
294 # resources like sockets are opened at load time by the master
295 # process and shared by multiple children. People enabling this are
296 # highly encouraged to look at the before_fork/after_fork hooks to
297 # properly close/reopen sockets. Files opened for logging do not
298 # have to be reopened as (unbuffered-in-userspace) files opened with
299 # the File::APPEND flag are written to atomically on UNIX.
301 # In addition to reloading the unicorn-specific config settings,
302 # SIGHUP will reload application code in the working
303 # directory/symlink when workers are gracefully restarted.
304 def preload_app(bool)
306 when TrueClass, FalseClass
307 set[:preload_app] = bool
309 raise ArgumentError, "preload_app=#{bool.inspect} not a boolean"
313 # Allow redirecting $stderr to a given path. Unlike doing this from
314 # the shell, this allows the unicorn process to know the path its
315 # writing to and rotate the file if it is used for logging. The
316 # file will be opened with the File::APPEND flag and writes
317 # synchronized to the kernel (but not necessarily to _disk_) so
318 # multiple processes can safely append to it.
320 # If you are daemonizing and using the default +logger+, it is important
321 # to specify this as errors will otherwise be lost to /dev/null.
322 # Some applications/libraries may also triggering warnings that go to
323 # stderr, and they will end up here.
324 def stderr_path(path)
325 set_path(:stderr_path, path)
328 # Same as stderr_path, except for $stdout. Not many Rack applications
329 # write to $stdout, but any that do will have their output written here.
330 # It is safe to point this to the same location a stderr_path.
331 # Like stderr_path, this defaults to /dev/null when daemonized.
332 def stdout_path(path)
333 set_path(:stdout_path, path)
336 # sets the working directory for Unicorn. This ensures USR2 will
337 # start a new instance of Unicorn in this directory. This may be
339 def working_directory(path)
340 # just let chdir raise errors
341 path = File.expand_path(path)
343 config_file[0] != ?/ &&
344 ! test(?r, "#{path}/#{config_file}")
346 "config_file=#{config_file} would not be accessible in" \
347 " working_directory=#{path}"
350 HttpServer::START_CTX[:cwd] = ENV["PWD"] = path
353 # Runs worker processes as the specified +user+ and +group+.
354 # The master process always stays running as the user who started it.
355 # This switch will occur after calling the after_fork hook, and only
356 # if the Worker#user method is not called in the after_fork hook
357 def user(user, group = nil)
358 # raises ArgumentError on invalid user/group
360 Etc.getgrnam(group) if group
361 set[:user] = [ user, group ]
364 # expands "unix:path/to/foo" to a socket relative to the current path
365 # expands pathnames of sockets if relative to "~" or "~username"
366 # expands "*:port and ":port" to "0.0.0.0:port"
367 def expand_addr(address) #:nodoc
368 return "0.0.0.0:#{address}" if Integer === address
369 return address unless String === address
372 when %r{\Aunix:(.*)\z}
375 File.expand_path(address)
376 when %r{\A(?:\*:)?(\d+)\z}
378 when %r{\A(.*):(\d+)\z}
379 # canonicalize the name
380 packed = Socket.pack_sockaddr_in($2.to_i, $1)
381 Socket.unpack_sockaddr_in(packed).reverse!.join(':')
389 def set_path(var, path) #:nodoc:
391 when NilClass, String
398 def set_hook(var, my_proc, req_arity = 2) #:nodoc:
401 arity = my_proc.arity
402 (arity == req_arity) or \
404 "#{var}=#{my_proc.inspect} has invalid arity: " \
405 "#{arity} (need #{req_arity})"
407 my_proc = DEFAULTS[var]
409 raise ArgumentError, "invalid type: #{var}=#{my_proc.inspect}"