The simplest way to build and deploy a web app on Shuttle looks like this:

src/main.rs
use axum::{routing::get, Router};

async fn hello_world() -> &'static str {
    "Hello, world!"
}

#[shuttle_runtime::main]
async fn main() -> shuttle_axum::ShuttleAxum {
    let router = Router::new().route("/", get(hello_world));

    Ok(router.into())
}

This example starts an HTTP server where the GET / endpoint returns Hello, world!. But most importantly, the code you see in the snippet above, is all it takes for shuttle deploy to deploy it.

This is possible due to the #[shuttle_runtime::main] procedural macro. The macro wraps your app with Shuttle’s runtime, which handles resource provisioning and initialization for you.

Provisioning Resources

In the example above, the 2 annotations in the main function expand to code that makes the Shuttle runtime provision them.

Here are the examples on how to use them, once you add the required annotations to your code:

Secrets

use shuttle_runtime::SecretStore;

#[shuttle_runtime::main]
async fn main(
    #[shuttle_runtime::Secrets] secrets: SecretStore,
) -> shuttle_axum::ShuttleAxum {
    // Get secret defined in `Secrets.toml` file.
    let secret = secrets.get("MY_API_KEY").expect("secret was not found");
}

Postgres Database

#[shuttle_runtime::main]
async fn main(
    #[shuttle_shared_db::Postgres] pool: PgPool,
) -> shuttle_axum::ShuttleAxum {
    // Use the connection pool to query the Postgres database
    pool.execute(include_str!("../schema.sql"))
        .await
        .map_err(CustomError::new)?;
}

For more info on resources, head on over to our Resources section.

Deploying your Project

When you run shuttle deploy, your project code is archived and sent to our platform where it is built into a Docker image. Your service is then be started on Shuttle’s infrastructure on AWS in London (eu-west-2). The generated code from shuttle_runtime::main handles resource provisioning and initialization, leaving you to focus on what matters.