Rocket
Rocket copied to clipboard
Documentation on deploying Rocket 🚀 to Heroku (and other similar ☁️ environments)
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! 👋
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. 😄
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 Looking at this issue, the ROCKET_PARAM feature shipped in 0.2.
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.
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.
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
Looks like that was my issue. Thanks for the quick help 👍
I've changed my dependencies to:
[dependencies]
rocket = { git = "https://github.com/SergioBenitez/Rocket.git" }
rocket_codegen = "*"
and it's now working
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.
Updated the OP to make it more clear this uses a relatively :new: feature.
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 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.
Thanks a lot!! https://vast-fjord-26312.herokuapp.com/ 😭 😄
This helped out a lot.
Any chance you need help writing up documentation for the deployment section @SergioBenitez?
@hiimtaylorjones I'd love the help! I imagine that such a section would be composed of at least two subsections:
- Deploying on a VPS (using NGINX as a reverse proxy)
- 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.
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 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 Cool thanks had been waiting on rustup to support musl
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. 🎊
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!
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.
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
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.
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.
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 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.
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 ).
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.
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.