{"id":15487364,"url":"https://github.com/dblock/heroku-forward","last_synced_at":"2025-04-09T10:06:33.803Z","repository":{"id":5895818,"uuid":"7114274","full_name":"dblock/heroku-forward","owner":"dblock","description":"Beat Heroku's 60s boot timeout with a proxy.","archived":false,"fork":false,"pushed_at":"2015-06-15T17:51:18.000Z","size":275,"stargazers_count":108,"open_issues_count":4,"forks_count":22,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-02T08:08:42.559Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dblock.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-12-11T15:36:45.000Z","updated_at":"2024-08-24T01:27:13.000Z","dependencies_parsed_at":"2022-09-06T04:41:12.361Z","dependency_job_id":null,"html_url":"https://github.com/dblock/heroku-forward","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dblock%2Fheroku-forward","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dblock%2Fheroku-forward/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dblock%2Fheroku-forward/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dblock%2Fheroku-forward/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dblock","download_url":"https://codeload.github.com/dblock/heroku-forward/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248018060,"owners_count":21034048,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T06:25:50.896Z","updated_at":"2025-04-09T10:06:33.780Z","avatar_url":"https://github.com/dblock.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"Heroku::Forward [![Build Status](https://travis-ci.org/dblock/heroku-forward.png?branch=master)](https://travis-ci.org/dblock/heroku-forward)\n===============\n\nBeat Heroku's 60 seconds timeout with a proxy.\n\nWhat's this?\n------------\n\n[Heroku](http://www.heroku.com/) will report an application crashing and yield an `R10 Boot Timeout` error when a web process took longer than 60 seconds to bind to its assigned `$PORT`. This error is often caused by a process being unable to reach an external resource, such as a database or because Heroku is pretty slow and you have a lot of gems in your `Gemfile`.\n\nThis gem implements a proxy using [em-proxy](https://github.com/igrigorik/em-proxy). This proxy is booted almost immediately, binding to the port assigned by Heroku. Heroku now reports the dyno up. The proxy then spawns your application's web server and establishes a connection over a unix domain socket (a file) between the proxy and the application. Once the application is ready, it will be able to serve HTTP requests normally. Until then requests may queue and some may timeout depending on how long it actually takes to start.\n\nUsage\n-----\n\nAdd `heroku-forward` to your `Gemfile`.\n\n``` ruby\ngem \"heroku-forward\"\n```\n\nCreate a new application rackup file, eg. `my_app.ru` that boots your application. Under Rails, this is the file that calls `run`.\n\n``` ruby\nrequire ::File.expand_path('../config/environment',  __FILE__)\nrun MyApp::Application\n```\n\nModify your default rackup file as follows. Under Rails this file is called `config.ru`.\n\n``` ruby\nrequire 'rubygems'\nrequire 'bundler'\n\n$stdout.sync = true\nBundler.require(:rack)\n\nport = (ARGV.first || ENV['PORT'] || 3000).to_i\nenv = ENV['RACK_ENV'] || 'development'\n\nrequire 'em-proxy'\nrequire 'logger'\nrequire 'heroku-forward'\nrequire 'heroku/forward/backends/thin'\n\napplication = File.expand_path('../my_app.ru', __FILE__)\nbackend = Heroku::Forward::Backends::Thin.new(application: application, env: env)\nproxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port)\nproxy.logger = Logger.new(STDOUT)\nproxy.forward!\n```\n\nThis sets up a proxy on the port requested by Heroku and runs your application with [Thin](http://code.macournoyer.com/thin). Other back-ends are also supported, see below.\n\nAdd the following line to `config/development.rb` to see Rails logger output on `STDOUT`:\n\n```ruby\nconfig.middleware.use Rails::Rack::LogTailer\n```\n\nForeman\n-------\n\nHeroku Cedar expects a `Procfile` that defines your application processes.\n\n```\nweb: bundle exec ruby config.ru\nworker: bundle exec rake jobs:work\n```\n\nYou can use `foreman` to test the proxy locally with `foreman start web`.\n\nHeroku Log\n----------\n\nHere's the log output from an application that uses this gem. Notice that Heroku reports the state of `web.1` up after just 4 seconds, while the application takes 67 seconds to boot.\n\n```\n2012-12-11T23:33:42+00:00 heroku[web.1]: Starting process with command `bundle exec ruby config.ru`\n2012-12-11T23:33:46+00:00 app[web.1]:  INFO -- : Launching Backend ...\n2012-12-11T23:33:46+00:00 app[web.1]:  INFO -- : Launching Proxy Server at 0.0.0.0:42017 ...\n2012-12-11T23:33:46+00:00 app[web.1]: DEBUG -- : Attempting to connect to /tmp/thin20121211-2-1bfazzx.\n2012-12-11T23:33:46+00:00 app[web.1]:  WARN -- : no connection, 10 retries left.\n2012-12-11T23:33:46+00:00 heroku[web.1]: State changed from starting to up\n2012-12-11T23:34:32+00:00 app[web.1]: \u003e\u003e Thin web server (v1.5.0 codename Knife)\n2012-12-11T23:34:32+00:00 app[web.1]: \u003e\u003e Maximum connections set to 1024\n2012-12-11T23:34:32+00:00 app[web.1]: \u003e\u003e Listening on /tmp/thin20121211-2-1bfazzx, CTRL+C to stop\n2012-12-11T23:34:53+00:00 app[web.1]: DEBUG -- : Attempting to connect to /tmp/thin20121211-2-1bfazzx.\n2012-12-11T23:34:53+00:00 app[web.1]: DEBUG -- : Proxy Server ready at 0.0.0.0:42017 (67s).\n```\n\nProxy Forwarding Options\n------------------------\n\n`Heroku::Forward::Proxy::Server.forward!` accepts the following options:\n\n* **delay**: number of seconds to sleep before launching the proxy, eg. `proxy.forward!(delay: 15)`. This prevents queuing of requests or reporting invalid `up` status to Heroku. It's recommended to set this value to as close as possible to the boot time of your application and less than the Heroku's 60s boot limit. Also note that the proxy itself needs time to boot.\n\nAvailable Backends\n------------------\n\n### Thin\n\nFor more information about Thin see [http://code.macournoyer.com/thin](http://code.macournoyer.com/thin).\n\n``` ruby\nrequire 'heroku/forward/backends/thin'\n\napplication = File.expand_path('../my_app.ru', __FILE__)\nbackend = Heroku::Forward::Backends::Thin.new(application: application, env: env)\nproxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port)\nproxy.forward!\n```\n\nThe Thin back-end supports the following options.\n\n* **application**: application to load\n* **env**: environment, eg. `:development`\n\nSSL is also supported.\n\n* **ssl**: enable SSL\n* **ssl-key-file**: path to private key\n* **ssl-cert-file**: path to certificate\n* **ssl-verify**: enable SSL certificate verification\n\n```ruby\noptions = { application: File.expand_path('../my_app.ru', __FILE__) }\n\n# branch to disable SSL depending on your environment\nif ENV['THIN_SSL_ENABLED']\n  options[:ssl] = true\n  options[:ssl_verify] = true\n  options[:ssl_cert_file] = ENV['THIN_SSL_CERT_FILE']\n  options[:ssl_key_file] = ENV['THIN_SSL_KEY_FILE']\nend\n\nbackend = Heroku::Forward::Backends::Thin.new(options)\n```\n\n### Unicorn\n\nFor more information about Unicorn see [http://unicorn.bogomips.org](http://unicorn.bogomips.org/).\n\nThe Unicorn back-end supports the following options.\n\n* **application**: application to load\n* **env**: environment, eg. `:development`\n* **config_file**: Unicorn configuration file\n\n```ruby\nrequire 'heroku-forward'\nrequire 'heroku/forward/backends/unicorn'\n\napplication = File.expand_path('../my_app.ru', __FILE__)\nconfig_file = File.expand_path('../config/unicorn.rb', __FILE__)\nbackend = Heroku::Forward::Backends::Unicorn.new(application: application, env: env, config_file: config_file)\nproxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port)\nproxy.forward!\n```\n\n### Puma\n\nFor more information about Puma see [http://puma.io](http://puma.io/).\n\nThe Puma back-end supports the following options.\n\n* **application**: application to load\n* **env**: environment, eg. `:development`\n* **socket**: socket, eg. `/tmp/puma.sock`\n* **config_file**: Puma configuration file\n* **term_signal**: Signal to send to Puma cluster when the backend is terminated; defaults to `TERM`\n\n```ruby\nrequire 'heroku-forward'\nrequire 'heroku/forward/backends/puma'\n\napplication = File.expand_path('../my_app.ru', __FILE__)\nconfig_file = File.expand_path('../config/puma.rb', __FILE__)\nbackend = Heroku::Forward::Backends::Puma.new(application: application, env: env, config_file: config_file)\nproxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port)\nproxy.forward!\n```\n\nNote that heroku-forward does not currently run on JRuby, see [em-proxy#39](https://github.com/igrigorik/em-proxy/issues/39) for details.\n\nFail-Safe\n---------\n\nIf you're worried about always using heroku-forward, consider building a fail-safe. Modify your `config.ru` to run without a proxy if `DISABLE_FORWARD_PROXY` is set as demonstrated in [this gist](https://gist.github.com/4263488). Add `DISABLE_FORWARD_PROXY` via `heroku config:add DISABLE_FORWARD_PROXY=1`.\n\nReading Materials\n-----------------\n\n* [Heroku R10 Boot Timeout](https://devcenter.heroku.com/articles/error-codes#r10-boot-timeout)\n* [Beating Heroku's 60s Boot Times with the Cedar Stack and a Reverse Proxy](http://noverloop.be/beating-herokus-60s-boot-times-with-the-cedar-stack-and-a-reverse-proxy/) by Nicolas Overloop\n* [Fighting the Unicorns: Becoming a Thin Wizard on Heroku](http://jgwmaxwell.com/fighting-the-unicorns-becoming-a-thin-wizard-on-heroku/) by JGW Maxwell\n* [eventmachine](https://github.com/eventmachine/eventmachine)\n* [em-proxy](https://github.com/igrigorik/em-proxy)\n* [Setup rails 3.2 \u0026 heroku-forward with SSL](https://gist.github.com/4540602)\n* [Puma signals](https://github.com/puma/puma/blob/master/docs/signals.md)\n\nContributing\n------------\n\nFork the project. Make your feature addition or bug fix with tests. Send a pull request. Bonus points for topic branches.\n\nCopyright and License\n---------------------\n\nMIT License, see [LICENSE](http://github.com/dblock/heroku-forward/raw/master/LICENSE.md) for details.\n\n(c) 2012-2013 [Daniel Doubrovkine](http://github.com/dblock), [Artsy](http://artsy.github.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdblock%2Fheroku-forward","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdblock%2Fheroku-forward","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdblock%2Fheroku-forward/lists"}