Back to Blog
Tutorial9 min read2026-07-02

How to Deploy a Rocket Rust Web App

Rocket is a batteries-included Rust web framework with a focus on ergonomics. This guide covers its critical address config gotcha, a multi-stage build, database pools via fairings, and production deployment.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

Rocket is one of the most ergonomic Rust web frameworks — declarative routing with attribute macros, built-in request guards, and managed state. It's a pleasure to write, but it has one deployment gotcha that bites nearly everyone the first time: Rocket binds to 127.0.0.1 by default, which is invisible to a container platform's proxy. Fix that and the rest is a standard Rust deploy.

A minimal Rocket app

# Cargo.toml
[dependencies]
rocket = { version = "0.5", features = ["json"] }
// src/main.rs
#[macro_use] extern crate rocket;
use rocket::serde::json::{json, Value};

#[get("/health")]
fn health() -> Value {
    json!({ "status": "ok" })
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![health])
}

The #[launch] macro wires everything up. But by default Rocket listens on 127.0.0.1:8000 — which a load balancer outside the container can't reach.

The address gotcha (read this twice)

You must tell Rocket to bind to all interfaces and to use the platform's port. Rocket reads configuration from Rocket.toml *or* environment variables prefixed with ROCKET_:

ROCKET_ADDRESS=0.0.0.0
ROCKET_PORT=8080

Set these in your production environment. If your platform provides a PORT variable instead, map it: set ROCKET_PORT to the platform's port value, or read it in code:

#[launch]
fn rocket() -> _ {
    let port: u16 = std::env::var("PORT").ok()
        .and_then(|p| p.parse().ok()).unwrap_or(8080);
    let figment = rocket::Config::figment()
        .merge(("address", "0.0.0.0"))
        .merge(("port", port));
    rocket::custom(figment).mount("/", routes![health])
}

This is the single most common reason a Rocket deploy returns "connection refused" from the platform — it's listening only on localhost.

Databases via fairings

Rocket's rocket_db_pools crate manages connection pools as a fairing (Rocket's middleware/lifecycle hook):

rocket_db_pools = { version = "0.2", features = ["sqlx_postgres"] }
use rocket_db_pools::{Database, Connection};
use rocket_db_pools::sqlx;

#[derive(Database)]
#[database("main")]
struct Db(sqlx::PgPool);

#[get("/api/posts")]
async fn posts(mut db: Connection<Db>) -> Value {
    let rows = sqlx::query!("SELECT title FROM posts LIMIT 20")
        .fetch_all(&mut **db).await.unwrap();
    json!({ "count": rows.len() })
}

Configure the database URL — Rocket maps the databases.main.url config, which you can set via environment:

ROCKET_DATABASES='{main={url="'$DATABASE_URL'"}}'

That env syntax is fiddly; many teams prefer reading DATABASE_URL directly and building the figment in code to avoid the nested-table escaping.

Multi-stage build

FROM rust:1-slim AS build
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo 'fn main(){}' > src/main.rs && cargo build --release && rm -rf src
COPY . .
RUN cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=build /app/target/release/myapp /usr/local/bin/myapp
ENV ROCKET_ADDRESS=0.0.0.0
ENV ROCKET_PORT=8080
EXPOSE 8080
CMD ["myapp"]

Setting ROCKET_ADDRESS=0.0.0.0 in the Dockerfile bakes in the fix so you can't forget it.

Deploying on PandaStack

  1. 1Provision a managed PostgreSQL. PandaStack injects DATABASE_URL.
  2. 2Connect the Git repo as a container app; the Dockerfile is auto-detected.
  3. 3Ensure ROCKET_ADDRESS=0.0.0.0 and the port matches the platform's PORT.
  4. 4Run sqlx migrations as a deploy step (sqlx migrate run).
  5. 5Add a custom domain; SSL is automatic.

Like all Rust services, the resulting binary is small and starts instantly — well-suited to scale-to-zero tiers.

Production checklist

  • [ ] ROCKET_ADDRESS=0.0.0.0 (the #1 gotcha).
  • [ ] Port matches the platform's expected port.
  • [ ] Multi-stage build with ca-certificates.
  • [ ] Database pool configured via fairing or figment.
  • [ ] Migrations applied as a deploy step.
  • [ ] --release build.

Verifying

curl -s https://app.example.com/health
# {"status":"ok"}

If you get a response (and not a connection refused), your address config is correct and the app is live.

References

  • [Rocket configuration guide](https://rocket.rs/guide/v0.5/configuration/)
  • [rocket_db_pools documentation](https://api.rocket.rs/v0.5/rocket_db_pools/)
  • [Rocket overview and guide](https://rocket.rs/guide/v0.5/)
  • [Rust official Docker images](https://hub.docker.com/_/rust)

---

Get the 0.0.0.0 bind right and Rocket is a clean PandaStack container deploy — Dockerfile auto-detected, DATABASE_URL injected from a managed PostgreSQL. Start free at [dashboard.pandastack.io](https://dashboard.pandastack.io).

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also