Deploying Rust applications to Heroku, with example code for Rustful
Update, 24 June 2014: I’m planning to add support for building projects with Cargo shortly. I’ll announce it here and on Twitter when it’s ready.
Update, 12 July 2014: Highly experimental Cargo support is now available.
Update, 17 September 2014: This post is obsolete. Please see Deploying Rust to Heroku, with example code for Iron.
If you have git
and the Heroku toolbelt on your system, this
should be easy:
git clone https://github.com/emk/heroku-rust-hello.git
cd heroku-rust-hello
heroku create --buildpack https://github.com/emk/heroku-buildpack-rust.git
git push heroku master
If you want to create your own Rust web server from scratch, keep reading. But fair warning: Rust is not yet ready for serious web development.
Setting up rustc
First, we need to install a Rust compiler. Assuming that we’re running Linux, let’s grab the latest nightly build, and hope that all of our supporting libraries are up to date:
curl -O http://static.rust-lang.org/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz
tar xzf rust-nightly-x86_64-unknown-linux-gnu.tar.gz
cd rust-nightly-x86_64-unknown-linux-gnu
./install.sh
If you’re not running Linux, check out these instructions for downloading nightly builds. If you’re running an older Linux, you may want to check out my Ubuntu 10.04 instructions.
Compiling our test program
Now, let’s create a git repository for our code, and get a copy of all our dependencies:
mkdir heroku-rust-hello
cd heroku-rust-hello
git init
git submodule add https://github.com/Ogeon/rustful.git lib/rustful
git submodule update --init --recursive
Next, place the following code into hello.rs
. This is heavily based on
the rustful example code, and you may want to double-check there
to see if any APIs have changed.
extern crate rustful;
extern crate http;
use std::os::getenv;
use std::io::net::ip::Port;
use rustful::{Server, Router, Request, Response};
use http::method::Get;
// An example handler for "/".
fn hello(request: &Request, response: &mut Response) {
match response.write("Hello from Rust!".as_bytes()) {
Err(e) => println!("error: {}", e),
_ => {}
}
}
/// Look up our server port number in PORT, for
/// compatibility with Heroku.
fn get_server_port() -> Port {
getenv("PORT")
.and_then(|s| from_str::<Port>(s.as_slice()))
.unwrap_or(8080)
}
/// Configure and run our server.
fn main() {
let routes = [
(Get, "/", hello)
];
let server = Server::new(get_server_port(), Router::from_routes(routes));
server.run();
}
To build it, we’ll need a Makefile
:
default: all
all: deps build
deps:
(cd lib/rustful && make deps && make)
build:
rustc -L lib/rustful/lib/ -o hello hello.rs
.PHONY: all deps build
And now we can build and test our program!
make
./hello
# Look at http://0.0.0.0:8080/ in a browser.
If this fails, begin by double-checking the rustful example code and seeing if any APIs have changed. When you’re finished running this, hit Control-C.
Verifying that we’re statically linked
While we’re here, let’s check to make sure that we’ve statically linked the
Rust library. Run ldd
to check what libraries we use:
$ ldd ./hello
linux-vdso.so.1 => (0x...)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x...)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x...)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x...)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x...)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x...)
/lib64/ld-linux-x86-64.so.2 (0x...)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x...)
If you see something like the above list, everything’s good. But if you see anything like:
libnative-1fb5e2c0-0.11.0-pre.so => ...
libsyntax-555559ea-0.11.0-pre.so => ...
…then you’re dynamically linking the Rust runtime libraries, and you won’t
be able to run on Heroku. To fix this, look for dependencies which are
only available as .so
files. These force rust
to link dynamically.
Your first suspect should be libsyntax
, which is used to implement
macros, but which should only be needed at compile time. Particular thanks
go to Huon Wilson for explaining this.
Preparing for Heroku
Create a Procfile
telling Heroku how to run your application:
web: ./hello
Now for the tricky part: Since the Rust language is still under heavy
development, we need to save a copy of our nightly build somewhere that we
can find it again. Take your copy of
rust-nightly-x86_64-unknown-linux-gnu.tar.gz
and toss it up on a
webserver somewhere, or in an S3 bucket. Now, tell Heroku where to find it
by creating a RustConfig
file containing two variables:
URL="https://example.com/rust-nightly-x86_64-unknown-linux-gnu.tar.gz"
VERSION="2014-05-29"
Here, the VERSION
string can be a date, or version number, or just about
any other identifier. Every time VERSION
changes, our Heroku buildpack
will download and cache a new copy of rustc
.
And finally, let’s commit our changes to git
:
git add Makefile Procfile hello.rs RustConfig
git commit -m "Create initial application"
Deploying to Heroku
For this next step, you will need Heroku toolbelt installed.
heroku create --buildpack https://github.com/emk/heroku-buildpack-rust.git
git push heroku master
If you’re lucky, Heroku should deploy your application and print out a URL. If you open that URL in a web browser, you should see “Hello from Rust!”
I had lots of help
I owe many thanks to Chris Morgan and Huon Wilson for their Rust expertise, and to Ogeon for some incredibly quick enhancements to his rustful library. Without their help, this would have taken much longer.
Also, before continuing, please be aware that the Rust language is still under heavy development, and your code will almost certainly break from week to week. And for web-related projects in Rust, please review Chris Morgan’s Are we web yet? before building anything more than toy servers.
When this breaks
I intend to keep these instructions up-to-date for the immediate future. If something breaks, please see my contact page. You may also want to take a look at my heroku-buildpack-rust repository. Happy Rust hacking!
Want to contact me about this article? Or if you're looking for something else to read, here's a list of popular posts.