Install the CLI. See the install docs.
Log in:
runway login
Add an SSH key. See key setup, or use the shortcut:
runway local key setup
Let’s deploy an application written in Rust (using the popular axum framework)
The application structure is initialized with cargo init and the following is an example of the src/main.rs which contains our entire application - a very straight forward service that responds to / and /hello-world.
cargo init runway-rust
cd runway-rust
use axum::{
routing::{get, Router},
response::IntoResponse,
};
use std::env;
use std::net::SocketAddr;
use tower_http::trace::TraceLayer;
use tracing::{info, Level};
use tracing_subscriber;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.with_max_level(Level::INFO)
.init();
let port = env::var("PORT").unwrap_or_else(|_| "3000".to_string());
let addr = format!("0.0.0.0:{}", port)
.parse::<SocketAddr>()
.expect("Unable to parse socket address");
let app = Router::new()
.route("/", get(handler_root))
.route("/hello-world", get(handler_hello_world))
.layer(TraceLayer::new_for_http()); // Add middleware for logging requests
println!("Listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
// Route handler for "/"
async fn handler_root() -> impl IntoResponse {
info!("Handling request to /");
"Hello"
}
// Route handler for "/hello-world"
async fn handler_hello_world() -> impl IntoResponse {
info!("Handling request to /hello-world");
"Hello World"
}
Since we’re using axum, our Cargo.toml looks like the following:
[package]
name = "runway-rust"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.6"
tokio = { version = "1", features = ["full"] }
tower = "0.4"
tracing = "0.1" # For logging
tracing-subscriber = "0.3" # For logging configuration
tower-http = { version = "0.4", features = ["trace"] } # For TraceLayer middleware
cargo build next to generate the Cargo.lock file (which will keep your dependencies in check when deploying this app).Runway doesn’t (yet?) have a buildpack for Rust, but thanks to our Docker/buildkit-based pipeline, that’s not a showstopper. This flexibility showcases Runway’s versatility in handling just about any application, no matter the language or framework. Whatever you’re building, we’ve got your back!
So, let’s dive in—here’s a multi-stage Dockerfile, and we’ll walk you through it step by step:
FROM rust:1.81.0-bookworm AS build
# Set the working directory inside the container
WORKDIR /build
COPY Cargo.toml Cargo.lock ./
COPY . .
RUN cargo build --release
# Final image
FROM debian:bookworm-slim
RUN apt update \
&& apt install -y ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /app \
&& chown -R 65534:65534 /app
# Set the user and group to nobody:nobody (in numeric)
USER 65534:65534
# Copy the service binary
COPY --from=build --chown=65534:65534 /build/target/release/runway-rust /app/service
EXPOSE 3000
# Run the compiled binary
CMD ["/app/service"]
The first stage of the Dockerfile copies all the files into the environment and finally uses cargo build --release to produce a binary (which is our entire app!).
cargo init runway-rust, which is why our project is called runway-rust and the compiled binary is called runway-rust.In the next/final stage, we install ca certificates (for good measure and the future) and copy the runway-rust binary into the /app directory, set the user/group to the numeric equivalent of nobody and add an EXPOSE 3000 to tell the orchestrator which port the app runs on.
Create the app on Runway (this also initializes a git repository, if none exists yet), then commit your changes:
runway app create
git add -A && git commit -m "initial commit"
runway app deploy
Run runway app open to see it live, or check the Runway UI. Check runway app logs if something’s off.
Congrats, young Rustacean! You deployed your application and it’s running on Runway! 🦀🦀🦀