Rust-Heroku buildpack rust: heroku-buildpack-rust — A buildpack for Rust applications on Heroku

Heroku buildpack for Rust

Build Status

This is a Heroku buildpack for Rust with support for cargo and rustup. Features include:

  • Caching of builds between deployments.
  • Automatic updates to the latest stable Rust by default.
  • Optional pinning of Rust to a specific version.
  • Support for export so that other buildpacks can access the Rust toolchain.
  • Support for compiling Rust-based extensions for projects written in other languages.

Example projects

Here are several example projects:

Using this buildpack

To deploy an application to Heroku, we recommend installing the Heroku CLI.

If you're creating a new Heroku application, cd to the directory containing your code, and run:

heroku create --buildpack emk/rust

This will only work if your application has a Cargo.toml and uses git. If you want to set a particular name for application, see heroku create --help first.

To use this as the buildpack for an existing application, run:

heroku buildpacks:set emk/rust

You will also need to create a Procfile pointing to the release version of your application, and commit it to git:

web: ./target/release/hello

...where hello is the name of your binary.

To deploy your application, run:

git push heroku master

Running Diesel migrations during the release phase

This will install the diesel CLI at build time and make it available in your dyno. Migrations will run whenever a new version of your app is released. Add the following line to your RustConfig

RUST_INSTALL_DIESEL=1

and this one to your Procfile

release: ./target/release/diesel migration run

Specifying which version of Rust to use

By default, your application will be built using the latest stable Rust. Normally, this is pretty safe: New stable Rust releases have excellent backwards compatibility.

But you may wish to use nightly Rust or to lock your Rust version to a known-good configuration for more reproducible builds. To specify a specific version of the toolchain, use a rust-toolchain file in the format rustup uses.

Note: if you previously specified a VERSION variable in RustConfig, that will continue to work, and will override a rust-toolchain file.

Combining with other buildpacks

If you have a project which combines both Rust and another programming language, you can insert this buildpack before your existing one as follows:

heroku buildpacks:add --index 1 emk/rust

If you have a valid Cargo.toml in your project, this is all you need to do. The Rust buildpack will run first, and your existing buildpack will run second.

But if you only need Rust to build a particular Ruby gem, and you have no top-level Cargo.toml file, you'll need to let the buildpack know to skip the build stage. You can do this by adding the following line to RustConfig:

RUST_SKIP_BUILD=1

Customizing build flags

If you want to change the cargo build command, you can set the RUST_CARGO_BUILD_FLAGS variable inside the RustConfig file.

RUST_CARGO_BUILD_FLAGS="--release -p some_package --bin some_exe --bin some_bin_2"

The default value of RUST_CARGO_BUILD_FLAGS is --release. If the variable is not set in RustConfig, the default value will be used to build the project.

Using the edge version of the buildpack

The emk/rust buildpack from the Heroku Registry contains the latest stable version of the buildpack. If you'd like to use the latest buildpack code from this Github repository, you can set your buildpack to the Github URL:

heroku buildpacks:set https://github.com/emk/heroku-buildpack-rust

Development notes

If you need to tweak this buildpack, the following information may help.

Testing with Docker

To test changes to the buildpack using the included docker-compose-test.yml, run:

./test_buildpack

Then make sure there are no Rust-related *.so files getting linked:

ldd heroku-rust-cargo-hello/target/release/hello

This uses the Docker image heroku/cedar, which allows us to test in an official Cedar-like environment.

We also run this test automatically on Travis CI.

Comments

  • Make (certain) environment variables available at build time
    Make (certain) environment variables available at build time

    May 17, 2017

    Right now, this buildpack always builds with --release. But there are situations in which one might want to build in debug mode... or at least with debug symbols enabled.

    Maybe this could be added as a configuration in RustConfig?

    enhancement help wanted 
    Reply
  • Add a release script
    Add a release script

    May 11, 2018

    It appears that modern cloud foundry requires a simple release script in order to run.

    Reply
  • Fix check for existing install of rustup
    Fix check for existing install of rustup

    Jan 16, 2019

                                                                                                                                                                                                           
    Reply
  • Add support for custom ssh keys
    Add support for custom ssh keys

    Jan 21, 2019

    Due to how cargo uses ssh keys, it is required to run

    eval $(ssh-agent -s)
    ssh-add $HOME/.ssh/my_key
    

    before executing the install step, if there are any keys present that need to be used. There are a couple of build packs for heroku that allow to setup the key, but adding those lines above to this buildpack are required to make it work as I found out.

    enhancement help wanted 
    Reply
  • Cannot find diesel during Heroku release step
    Cannot find diesel during Heroku release step

    Feb 5, 2019

    Hi, I think I have followed all the steps to run Diesel migrations on release with Heroku correctly but I keep getting this error in Heroku deploys:

    /bin/sh: 1: ./target/release/diesel: not found
    

    It seems the step which is supposed to install the diesel-cli does not seem to run. These are my RustConfig and Procfile:

    VERSION=nightly
    RUST_INSTALL_DIESEL=1
    
    release: ./target/release/diesel migration run
    

    Is there something I missed in the setup?

    bug help wanted 
    Reply
  • support cargo ssh keys
    support cargo ssh keys

    Nov 22, 2019

    This is required if you have private repos in github, otherwise Cargo can't access them. The idea is to export a machine (Github user) ssh key as env var on Heroku. This is not related to Heroku but with cargo itself, how it handles ssh keys. It was first noted by @dignifiedquire in #39

    Reply
  • Remove build artifacts after the build process?
    Remove build artifacts after the build process?

    Nov 21, 2016

    Heroku has a limit of 300 MB for the slug size (ie. the output of the build process).

    Unfortunately I've run into an annoying problem this morning, which is that running cargo build --release creates a target directory that weights 310 MB, though the actual binary itself is only 41 MB.

    Heroku's docs suggest that you use a .slugignore file (similar to a .gitignore). However the files that match .slugignore are removed before the build process, so it's useless in my situation.

    I don't know if you have a solution to suggest for me and people who run in the same situation as me? Removing the /target/release/deps in the buildpack would be nice, but as I'm not familiar with heroku's caching process I don't know if it's a good idea to do so.

    Reply
  • Avoid copying non-executable files into BUILD_DIR
    Avoid copying non-executable files into BUILD_DIR

    Feb 28, 2017

    This does not rely upon any Rust toolchain support, but rather upon the good old find program. Thus, it copies all executables in $CARGO_TARGET_DIR/release, rather than any specifically-named executable file.

    In short, this is a somewhat hacky method for circumnavigating #17.

    Reply
  • Not able to build nightly
    Not able to build nightly

    Oct 30, 2018

    When I specify the buildpack to use nightly, I get this on heroku:

    -----> Rust app detected
    -----> Downloading rustup
    -----> Using rustup to install Rust channel nightly
    info: downloading installer
    error: invalid toolchain name: 'nightly
     !     Push rejected, failed to compile Rust app.
     !     Push failed
    
    Reply
  • Updated buildpack to use rustup.sh
    Updated buildpack to use rustup.sh

    May 21, 2016

    Previously this buildpack was trying to download and install rust manually which caused many stdlibs to be missing. Rust provides a script to automate a bunch of the install processes and switching to this greatly simplified the buildpack.

    I tried it on a test app using iron and everything seemed to work correctly (https://github.com/binarycleric/resume-api). Forgive any bad code in that repo, I'm still learning Rust.

    Reply
  • Ruby/Rust ld cannot find lruby
    Ruby/Rust ld cannot find lruby

    Sep 26, 2016

    I've built a Ruby/Rust hello application, but it does not want to deploy to Heroku. @schneems

    remote:        Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
    remote:        
    remote:        current directory: /tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3/ex
    remote:        /tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/ruby-2.3.0/bin/ruby -r ./siteconf20160926-228-cc92i.rb extconf.rb
    remote:        cargo 0.12.0-nightly (6b98d1f 2016-07-04)
    remote:        rustc 1.11.0 (9b21dcd6a 2016-08-15)
    remote:        
    remote:        current directory: /tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3/ex
    remote:        make "DESTDIR=" clean
    remote:        rm -rf target
    remote:        
    remote:        current directory: /tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3/ex
    remote:        make "DESTDIR="
    remote:        cargo build --release
    remote:        Updating registry `https://github.com/rust-lang/crates.io-index`
    remote:        Downloading ruru v0.7.8
    remote:        Downloading ruby-sys v0.2.13
    remote:        Downloading libc v0.2.16
    remote:        Compiling ruby-sys v0.2.13
    remote:        Compiling libc v0.2.16
    remote:        Compiling ruru v0.7.8
    remote:        Compiling string-utility-rb-native v1.0.0 (file:///tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3/ext)
    remote:        src/lib.rs:4:12: 4:21 warning: unused import, #[warn(unused_imports)] on by default
    remote:        src/lib.rs:4 use ruru::{AnyObject, Class, RString, VM};
    remote:        ^~~~~~~~~
    remote:        src/lib.rs:4:39: 4:41 warning: unused import, #[warn(unused_imports)] on by default
    remote:        src/lib.rs:4 use ruru::{AnyObject, Class, RString, VM};
    remote:        ^~
    remote:        src/lib.rs:5:19: 5:23 warning: unused import, #[warn(unused_imports)] on by default
    remote:        src/lib.rs:5 use ruru::types::{Argc, Value};
    remote:        ^~~~
    remote:        src/lib.rs:5:25: 5:30 warning: unused import, #[warn(unused_imports)] on by default
    remote:        src/lib.rs:5 use ruru::types::{Argc, Value};
    remote:        ^~~~~
    remote:        src/lib.rs:6:5: 6:25 warning: unused import, #[warn(unused_imports)] on by default
    remote:        src/lib.rs:6 use ruru::traits::Object;
    remote:        ^~~~~~~~~~~~~~~~~~~~
    remote:        error: linking with `cc` failed: exit code: 1
    remote:        note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-L" "/app/tmp/cache/multirust/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3/ext/target/release/stringutility.0.o" "-o" "/tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3/ext/target/release/libstringutility.so" "/tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3/ext/target/release/stringutility.metadata.o" "-Wl,-O1" "-nodefaultlibs" "-L" "/tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3/ext/target/release" "-L" "/tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3/ext/target/release/deps" "-L" "/app/vendor/ruby-2.3.1/lib" "-L" "/app/tmp/cache/multirust/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "-Wl,-Bdynamic" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/libruru-35c12ee1cec9de02.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/libruby_sys-a3c0096998cfe6fb.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/liblibc-1417726cb94dbc83.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/libstd-39b92f95.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/libpanic_unwind-39b92f95.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/libunwind-39b92f95.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/librand-39b92f95.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/libcollections-39b92f95.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/librustc_unicode-39b92f95.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/liballoc-39b92f95.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/liballoc_system-39b92f95.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/liblibc-39b92f95.rlib" "-Wl,--no-whole-archive" "-Wl,--whole-archive" "/tmp/rustc.q8eAleDtOvmp/libcore-39b92f95.rlib" "-Wl,--no-whole-archive" "-l" "ruby" "-l" "util" "-l" "dl" "-l" "pthread" "-l" "gcc_s" "-l" "pthread" "-l" "c" "-l" "m" "-l" "rt" "-l" "util" "-shared" "-l" "compiler-rt"
    remote:        note: /usr/bin/ld: cannot find -lruby
    remote:        collect2: error: ld returned 1 exit status
    remote:        
    remote:        error: aborting due to previous error
    remote:        error: Could not compile `string-utility-rb-native`.
    remote:        
    remote:        To learn more, run the command again with --verbose.
    remote:        make: *** [all] Error 101
    remote:        
    remote:        make failed, exit code 2
    remote:        
    remote:        Gem files will remain installed in /tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/gems/string-utility-2.7.3 for inspection.
    remote:        Results logged to /tmp/build_b49520d155f32d44a5ec710ef7b84985/vendor/bundle/ruby/2.3.0/extensions/x86_64-linux/2.3.0-static/string-utility-2.7.3/gem_make.out
    remote:        An error occurred while installing string-utility (2.7.3), and Bundler cannot
    remote:        continue.
    remote:        Make sure that `gem install string-utility -v '2.7.3'` succeeds before bundling.
    
    Reply
  • Export Rust build environment for use by other buildpacks
    Export Rust build environment for use by other buildpacks

    Sep 22, 2016

    See #9. The theory here is that we might have a Ruby application that uses a gem implemented in Rust, and in order to compile the gem, the Ruby buildpack needs access to the Rust compiler.

    To support this, we need to create an export file in our buildpack directory containing all the environment variables needed to run cargo and rustc.

    Note that this still has some limitations: You almost certainly need to have a valid Cargo.toml in your project for the buildpack to detect and build before you can run the other buildpack that needs Rust.

    • CC @binarycleric Feel free to take a look at this and let me know what you think.
    • CC @schneems @elifoster Please let me know if this works for your use cases.

    To try this, I think you can run heroku buildpacks:set https://github.com/emk/heroku-buildpack-rust#multirust_export and redeploy. But please set it back when you're done, because I'll delete the branch when I merge it.

    Reply