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=8080Set 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
- 1Provision a managed PostgreSQL. PandaStack injects
DATABASE_URL. - 2Connect the Git repo as a container app; the Dockerfile is auto-detected.
- 3Ensure
ROCKET_ADDRESS=0.0.0.0and the port matches the platform'sPORT. - 4Run sqlx migrations as a deploy step (
sqlx migrate run). - 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.
- [ ]
--releasebuild.
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).