Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation on deploying Rocket πŸš€ to Heroku (and other similar ☁️ environments) #171

Open
tcbyrd opened this issue Feb 4, 2017 · 38 comments
Labels
docs Improvements or additions to documentation
Milestone

Comments

@tcbyrd
Copy link

tcbyrd commented Feb 4, 2017

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 ☝️ 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 πŸ˜‰). 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!!! πŸ’₯πŸŽ‰πŸ’₯ 🎊 πŸ’₯

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! πŸ‘‹

@SergioBenitez
Copy link
Member

SergioBenitez commented Feb 4, 2017

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 SergioBenitez added the docs Improvements or additions to documentation label Feb 4, 2017
@RustyRails
Copy link

RustyRails commented Feb 5, 2017

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

@tcbyrd
Copy link
Author

tcbyrd commented Feb 5, 2017

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

@nanoxd
Copy link

nanoxd commented Feb 5, 2017

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.

@tcbyrd
Copy link
Author

tcbyrd commented Feb 5, 2017

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.

@RustyRails
Copy link

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

@nanoxd
Copy link

nanoxd commented Feb 5, 2017

Looks like that was my issue. Thanks for the quick help πŸ‘

@RustyRails
Copy link

I've changed my dependencies to:

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

and it's now working

@tcbyrd
Copy link
Author

tcbyrd commented Feb 5, 2017

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
Copy link
Author

tcbyrd commented Feb 5, 2017

Updated the OP to make it more clear this uses a relatively πŸ†• feature.

@marti1125
Copy link

marti1125 commented Apr 16, 2017

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...

@SergioBenitez
Copy link
Member

@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.

@marti1125
Copy link

Thanks a lot!! https://vast-fjord-26312.herokuapp.com/ 😭 πŸ˜„

@hiimtaylorjones
Copy link

This helped out a lot.

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

@SergioBenitez
Copy link
Member

@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.

@corbinu
Copy link

corbinu commented Jun 16, 2017

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

@messense
Copy link
Sponsor Contributor

messense commented Jan 25, 2018

@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

@corbinu
Copy link

corbinu commented Jan 29, 2018

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

@anirudhmurali
Copy link

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. 🎊

@cmac4603
Copy link

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!

@JeroenKnoops
Copy link

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.

@sporto
Copy link

sporto commented Oct 31, 2018

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.

@Ten0
Copy link

Ten0 commented Jan 6, 2019

@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.

@anurag
Copy link

anurag commented Jun 1, 2019

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
Copy link

anurag commented Jun 13, 2019

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.

@cmac4603
Copy link

@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.

@anurag
Copy link

anurag commented Jun 13, 2019 via email

@anurag
Copy link

anurag commented Jun 13, 2019

@SergioBenitez thoughts on the deployment guide PR?

Happy to edit as needed.

@anurag
Copy link

anurag commented Jul 2, 2019

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.

@sporto
Copy link

sporto commented Jul 2, 2019

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.

@jrmiller82
Copy link

+1 for need of a deployment guide. Definitely needs a section on the Guide.

@jrmiller82
Copy link

jrmiller82 commented Nov 28, 2019

I rolled my own service with systemd last night, and just do a simple scp of the binary and the Rocket.toml to the production service. Working really nicely.

The systemd service /etc/systemd/system/FOO.service looks something like this:

[Unit]
Description=example.com internal contact page service
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=5
User=FOOservice
WorkingDirectory=/opt/FOO-contact
ExecStart=/opt/FOO-contact/FOO

[Install]
WantedBy=multi-user.target

Then it's a simple
sudo systemctl start FOO
sudo journalctl -f -u FOO to check on if it's working
sudo systemctl enable FOO for persistence after reboot

My rocket.toml is super simple:

[production]
address = "localhost"
port = 50088

And I then use nginx to reverse proxy to port 50088 for the corresponding url and children of that url. Nginx is set up with lets encrypt so it is handling encryption so I'm not bothering with rocket's TLS or SASL. Assuming your nginx is already otherwise working, all I needed was to add this block to my nginx config:

location /contact {
                proxy_pass http://localhost:50088;
        }

and then run sudo systemctl restart nginx.

@jebrosen
Copy link
Collaborator

@jrmiller82 that example is excellent and fits perfectly into one of the two aims @SergioBenitez listed earlier:

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

I almost started a deployment guide a while back, but I have been focusing on #1065 now instead.

@jrmiller82
Copy link

jrmiller82 commented Nov 28, 2019 via email

@shenshing
Copy link

Thank you everyone, I have read all of the comments. I have problem when deploying Rocket to Heroku.
I have 3 files: 1 binary and 2 library (the 2 library use in binary file)
What should I do to host it? Do I need to upload the 2 library first then main lastly.
Is it the correct way to do it?

@jebrosen
Copy link
Collaborator

jebrosen commented May 9, 2020

Is it the correct way to do it?

This depends very much on what the libraries are and how much control Heroku gives you over the environment. Usually it's easiest to compile on the same operating system you are deploying to. If the libraries are common (like openssl) Heroku probably already provides them. If Heroku does not have them, then you will need to install or bundle them with your application. I do not use Heroku, but it looks like you can create and deploy a docker container from one of heroku's base images - that may be an option here.

@isaacdarcilla
Copy link

isaacdarcilla commented Jun 13, 2020

I got error

2020-06-13T08:27:14.282528+00:00 heroku[web.1]: State changed from crashed to starting
2020-06-13T08:27:15.487135+00:00 heroku[web.1]: Starting process with command ROCKET_PORT=10975 ROCKET_ENV=prod ./target/release/main
2020-06-13T08:27:18.610270+00:00 app[web.1]: bash: ./target/release/main: No such file or directory
2020-06-13T08:27:18.673628+00:00 heroku[web.1]: Process exited with status 127
2020-06-13T08:27:18.761046+00:00 heroku[web.1]: State changed from starting to crashed

EDIT

Fixed, renamed web: ROCKET_PORT=$PORT ROCKET_ENV=prod ./target/release/main to
web: ROCKET_PORT=$PORT ROCKET_ENV=prod ./target/release/sbadmin_rust

https://github.com/isaacdarcilla/sbadmin2-rust

@renatocassino
Copy link

Thanks for this. I added the PORT to project but I forgot to add the ROCKET_ADDRESS
This is my Procfile working:

web: ROCKET_ADDRESS=0.0.0.0 ROCKET_PORT=$PORT ROCKET_ENV=prod ./target/release/<myAppName>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Improvements or additions to documentation
Projects
Status: Ready
Development

No branches or pull requests