Rocket icon indicating copy to clipboard operation
Rocket copied to clipboard

Documentation on deploying Rocket 🚀 to Heroku (and other similar ☁️ environments)

Open tcbyrd opened this issue 7 years ago • 38 comments

First off I'd like to just say I'm really enjoying this framework and big 👏 to @SergioBenitez for the ✨ work and great documentation and examples so far. I'm just getting started with Rust and this has been a great introduction to the language for me.

I'm not sure what other platforms people are using to deploy Rust web servers, but since most of my experience on the back-end has been with NodeJS and Ruby, I typically deploy to Heroku so I can take advantage of their free dynos while getting built-in Git/GitHub integrations. Unfortunately, Heroku doesn't have an officially supported buildpack or any documentation on how to work with Rust, so I was venturing into uncharted territory. I was originally going to open an Issue here to see if anybody had tips, but I decided to see if I could piece it together since the documentation seemed already pretty thorough.

A big part of getting this working is thanks to an actively maintained custom Heroku buildpack for Rust that supports Cargo here: https://github.com/emk/heroku-buildpack-rust.

TL;DR - Using the steps outlined in that :point_up: repo and a 🆕 recently shipped feature to override Rocket's config parameters via environment variables, here is what ultimately worked for me:

$ heroku create --buildpack https://github.com/emk/heroku-buildpack-rust.git
$ git remote add heroku https://git.heroku.com/<heroku-project-name>.git
$ echo "web: ROCKET_PORT=$PORT ROCKET_ENV=prod ./target/release/<MyApp>" > Procfile
# Dynamically binds Rocket to the Dyno's $PORT on 0.0.0.0
$ echo "VERSION=nightly" > RustConfig
# Ensures the buildpack uses Rust nightly
$ git add . && git commit -m "Add Heroku deploy configuration"
$ git push heroku master

Slightly longer explanation

Outlined below are the some of the problems I ran into and how I ultimately ended up getting it all working properly (I Think :wink:). I know an Issue probably isn't the best place for this kind of stuff long term, so feel free to use any of this to enhance existing documentation where it makes sense.

Config variables

While it made complete since that I could modify the port manually and have independent configurations for different environments, because Heroku dynamically allocates the port every time a dyno (container) is executed, this can't be defined in a .toml file ahead of time.

It wasn't until I found this issue that I realized I could use CONFIG_PORT to override the config variables on the command line. This allows you to do CONFIG_PORT=$PORT on Heroku to dynamically define the port at launch time.

Which worked great! To get the right port, at least. But I still ran into problems getting the server started. In NodeJS (what I'm most familiar with), once you get the port right, it works. But Heroku was still complaining that the process wasn't binding to $PORT:

🔧  Configured for development.
=> address: localhost
=> port: 28693
=> log: normal
=> workers: 8
🚀  Rocket has launched from http://localhost:28693...
Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch

# Also fails on 127.0.0.1:
🚀  Rocket has launched from http://127.0.0.1:18003...
Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch

In some cases in Node, an application needs to be aware of the URL that gets created by Heroku, so I thought I would pass that in as the address. This made sense to me because the default was "localhost", but doing it this way, once Heroku finished with the deploy, I would get an "internal application error" when I tried to browse to that URL. Looking at the logs, this was caused by a panic from Hyper when trying to create the server at that address:

$ ROCKET_ADDRESS=immense-sierra-89480.herokuapp.com ROCKET_PORT=$PORT ./target/release/main
🔧  Configured for development.
    => address: immense-sierra-89480.herokuapp.com
    => port: 39205
    => log: normal
    => workers: 8
Error: Failed to start server.
thread "main" panicked at "Can't assign requested address (os error 49)", lib/src/rocket.rs:554

Looking at the function that caused the panic, basically it just means I was trying to bind to an invalid address.

Ok so that didn't work, but in tracing down that error, I came across the defaults that get used by Rocket in different stages and noticed this: https://github.com/SergioBenitez/Rocket/blob/master/lib/src/config/config.rs#L157

Production => {
    Config {
        environment: Production,
        address: "0.0.0.0".to_string(),
        port: 80,
        ...

Of course! Bind the server to 0.0.0.0!

$ ROCKET_ADDRESS=0.0.0.0 ROCKET_PORT=$PORT ./target/release/main
🔧  Configured for development.
    => address: 0.0.0.0
    => port: 50795
    => log: normal
    => workers: 8
🚀  Rocket has launched from http://0.0.0.0:50795...
State changed from starting to up

It works!!! :boom:🎉:boom: 🎊 :boom:

This also made me realize that if I configured the Heroku Procfile command to launch in the production configuration, it would default to 0.0.0.0, meaning I could leave out the address config variable and only define the port:

$ ROCKET_PORT=$PORT ROCKET_ENV=prod ./target/release/main

Conclusion

This is ultimately a very simple solution, which I'm sure speaks to my inexperience with Rust/Hyper and the incredible engineering going on under the hood. So far though, this server is blazingly fast and handles a lot more concurrent connections than a similar NodeJS server I was using on Heroku. Great work again and let me know if anything isn't clear. Cheers! 👋

tcbyrd avatar Feb 04 '17 22:02 tcbyrd

Awesome! Thank you for posting this!

I definitely want to add a "Deploying" section to the guide; it's been my intention since day one, but so much else has taken precedence. I'm hoping to open source the guide soon, and I'd love it if you could contribute these steps to the soon-to-exist "Deploying" section of the guide!

P.S: I really enjoyed reading through your adventures. 😄

SergioBenitez avatar Feb 04 '17 22:02 SergioBenitez

Does this still work?

It seems to be ignoring ROCKET_PORT for me, so I'm having trouble binding to the port heroku wants me to use.

Since this issue is only 18 hours old, I'll say I'm using rocket 0.1.6

RustyRails avatar Feb 05 '17 16:02 RustyRails

@RustyRails Looking at this issue, the ROCKET_PARAM feature shipped in 0.2.

tcbyrd avatar Feb 05 '17 16:02 tcbyrd

I'm running into a different issue. I successfully get the port to show up but encounter this error:

thread 'main' panicked at 'Permission denied (os error 13)', /app/tmp/cache/cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.1.6/src/rocket.rs:474

Full backtrace

Starting process with command `ROCKET_PORT="30861" ROCKET_ENV=prod ./target/release/app`
Process exited with status 101
Error: Failed to start server.
   1:     0x7f8d4dff793c - std::sys::imp::backtrace::tracing::imp::write::hf7294f5e24536b4a
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42
   2:     0x7f8d4dffae2e - std::panicking::default_hook::{{closure}}::h9a07d0b00c43fbee
thread 'main' panicked at 'Permission denied (os error 13)', /app/tmp/cache/cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.1.6/src/rocket.rs:474
stack backtrace:
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:367
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:351
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:555
   6:     0x7f8d4dffb089 - std::panicking::begin_panic_fmt::h7bf5635a07d66272
   3:     0x7f8d4dffaa34 - std::panicking::default_hook::hf25feff2d08bf39b
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:501
   7:     0x7f8d4dfa22f8 - rocket::rocket::Rocket::launch::hd7f438e43cdd8762
   4:     0x7f8d4dffb2cb - std::panicking::rust_panic_with_hook::h4cb8c6fbb8386ccf
   5:     0x7f8d4dffb164 - std::panicking::begin_panic::heb3517ba798e470b
                        at /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/libstd/panicking.rs:517
  10:     0x7f8d4dffba36 - std::rt::lang_start::h0637c2e100ff36fc
   8:     0x7f8d4df6f01b - app::main::h9e9dbfcfb36fe94a

Edit: Looks like that's here which points to the server not being able to launch.

nanoxd avatar Feb 05 '17 16:02 nanoxd

I definitely want to add a "Deploying" section to the guide

Yes! This is exactly what I wanted. I kept looking at your docs thinking a natural next step would be a few quick examples of how to deploy it on a server or container.

tcbyrd avatar Feb 05 '17 16:02 tcbyrd

I'm running into a different issue. I successfully get the port to show up but encounter this error:

Are you sure it's not just ignoring it? That's the error you'll get if it tries to bind to port 80 (e.g. it's not using ROCKET_PORT). As per @tcbyrd's comment it seems you need to use master to have that feature

RustyRails avatar Feb 05 '17 16:02 RustyRails

Looks like that was my issue. Thanks for the quick help 👍

nanoxd avatar Feb 05 '17 16:02 nanoxd

I've changed my dependencies to:

[dependencies]
rocket = { git = "https://github.com/SergioBenitez/Rocket.git" }
rocket_codegen = "*"

and it's now working

RustyRails avatar Feb 05 '17 16:02 RustyRails

For reference (and because I like spelunking through code), here is the new function that was added: https://github.com/SergioBenitez/Rocket/blob/master/lib/src/config/mod.rs#L311-L342

That should be in the /lib/src/config directory of your project if you have the right version.

Relevant commit

tcbyrd avatar Feb 05 '17 16:02 tcbyrd

Updated the OP to make it more clear this uses a relatively :new: feature.

tcbyrd avatar Feb 05 '17 16:02 tcbyrd

this is my repo https://github.com/mozillaperu/rustpe I have an error

remote: error[E0004]: non-exhaustive patterns: GlobalAsm(_) not covered remote: --> /app/tmp/cache/cargo/registry/src/github.com-1ecc6299db9ec823/rocket_codegen-0.2.4/src/lints/utils.rs:157:15 remote: | remote: 157 | match *self { remote: | ^^^^^ pattern GlobalAsm(_) not covered remote: remote: error: aborting due to previous error remote: remote: error: Could not compile rocket_codegen. remote: Build failed, waiting for other jobs to finish...

marti1125 avatar Apr 16 '17 18:04 marti1125

@marti1125 This was caused by a change in the latest nightly. Since the nightly was released later than usual, my cron job missed it. In any case, 0.2.5 was just published; using 0.2.5 will remedy the issue you're seeing.

SergioBenitez avatar Apr 16 '17 21:04 SergioBenitez

Thanks a lot!! https://vast-fjord-26312.herokuapp.com/ 😭 😄

marti1125 avatar Apr 17 '17 02:04 marti1125

This helped out a lot.

Any chance you need help writing up documentation for the deployment section @SergioBenitez?

hiimtaylorjones avatar Jun 14 '17 03:06 hiimtaylorjones

@hiimtaylorjones I'd love the help! I imagine that such a section would be composed of at least two subsections:

  1. Deploying on a VPS (using NGINX as a reverse proxy)
  2. Deploying on Heroku

That will likely cover 75%+ of use cases, is my guess. Maybe there's a third, popular deployment strategy? In any case, I'd be more than happy to review an addition of such a section to the guide. As I've previously stated, it's something I've wanted for a while.

SergioBenitez avatar Jun 16 '17 02:06 SergioBenitez

I am happy to write one up about building a Rocket app into a docker container. If it is compiled with musl-libc it can be placed in a "scratch" image which is less than a megabyte so a simple Rocket app wrapped in a container is about 5mb

corbinu avatar Jun 16 '17 06:06 corbinu

@corbinu Here is an example of building a statically compiled Rocket app docker image:

https://github.com/fede1024/kafka-view/blob/ff4453b2230a41002e770cd5862dff8cf802acc6/Dockerfile

The resulting image is only 7MB

messense avatar Jan 25 '18 01:01 messense

@messense Cool thanks had been waiting on rustup to support musl

corbinu avatar Jan 29 '18 21:01 corbinu

Thanks @tcbyrd, nice explanation! I was using a Dockerfile for deploying Rocket on Hasura cloud provider and having both ENV ROCKET_ADDRESS=0.0.0.0, ENV ROCKET_PORT=8080 worked for me. 🎊

anirudhmurali avatar Feb 06 '18 10:02 anirudhmurali

Yup, default localhost does not work on any docker file images I tried, and must always be set to 0.0.0.0 it would seem (trust me, I tried several). I did discover how to build rust-musl files along the way though, and my docker image size for a simple API has gone from 1.8Gb to 15.1Mb!!!!!!! Insane, go musl!

cmac4603 avatar Feb 16 '18 19:02 cmac4603

Zeit also supports deploying Rust-Rocket applications in their new beta. ( https://zeit.co/blog/serverless-docker )

An example how to do it can be found here: https://github.com/zeit/now-examples/tree/master/rust-rocket

If a deploy section is available, I'm happy to add a proper example for deploying to Zeit.

JeroenKnoops avatar Aug 22 '18 21:08 JeroenKnoops

I have tried deploying a Rust web app in Now and failed. My deployment just died after 15 mins. My app takes longer to compile from scratch. I don't know if they have a way to do caching between deployments, but I couldn't get the first deployment to succeed.

Maybe Heroku does this better as they have CACHE_DIR. I will like to hear how long it takes for you.

Because how long the Rust compiler takes for non-trivial apps, it doesn't seem like deploying with Now (or maybe Heroku) is a great way. It would be nicer to document a way that works by creating a binary locally or in CI and deploying that.

sporto avatar Oct 31 '18 10:10 sporto

@sporto

Maybe Heroku does this better as they have CACHE_DIR. I will like to hear how long it takes for you.

Because how long the Rust compiler takes for non-trivial apps, it doesn't seem like deploying with Now (or maybe Heroku) is a great way. It would be nicer to document a way that works by creating a binary locally or in CI and deploying that.

The rust build pack makes use of CACHE_DIR to allow fast deploy.

Ten0 avatar Jan 06 '19 11:01 Ten0

Hi everyone, we've just launched native Rust support for Render, a new cloud provider. We've also created a couple of examples using Rocket:

https://render.com/docs/deploy-rocket-rust https://render.com/docs/deploy-rust-graphql

With Render you can:

  • Use any nightly toolchain version.
  • Build and deploy Rocket apps directly from GitHub on every push.
  • Speed up builds because Render caches cargo and rustup directories.

Is anyone actively working on a Deployment Guide? If not, I'd be happy to contribute one.

anurag avatar Jun 01 '19 20:06 anurag

Assuming no one is working on it, I'll submit a deployment guide for Render, and hopefully others can add guides for other providers as well.

anurag avatar Jun 13 '19 18:06 anurag

@anurag If you can do this, I can add one native kubernetes if anyone is interested. It's a very nice idea, and I wish it existed when I started.

cmac4603 avatar Jun 13 '19 18:06 cmac4603

Working on it now.

On Thu, Jun 13, 2019 at 11:23 AM, Colin MacRae < [email protected] > wrote:

@ anurag ( https://github.com/anurag ) If you can do this, I can add one native kubernetes if anyone is interested. It's a very nice idea, and I wish it existed when I started.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub ( https://github.com/SergioBenitez/Rocket/issues/171?email_source=notifications&email_token=AAAOUAJ3KKJORZMHCOZR3BDP2KGDHA5CNFSM4C65GVVKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXUTYNY#issuecomment-501824567 ) , or mute the thread ( https://github.com/notifications/unsubscribe-auth/AAAOUAJYUNKZRE6TY6H463DP2KGDHANCNFSM4C65GVVA ).

anurag avatar Jun 13 '19 18:06 anurag

@SergioBenitez thoughts on the deployment guide PR?

Happy to edit as needed.

anurag avatar Jun 13 '19 19:06 anurag

Bumping: we're already seeing Rocket users using the links above to deploy Rocket on Render. It would be helpful to get this as an option in the official guide.

anurag avatar Jul 02 '19 01:07 anurag

I have tried many things for deploying a Rocket app, including Heroku. So far Render has been the only service that provided me with a good experience. +1, will use again.

sporto avatar Jul 02 '19 02:07 sporto