日本語

In a container

The server template builds a container image with Docker, which a host such as Fly.io, Cloud Run, or Render runs.

Deploy from the template

These commands need Rust 1.99 or later and Docker. cargo generate copies the server template into my-app and names the package after it, and the last two lines build the image and run it.

cargo install cargo-generate
cargo generate --git https://github.com/imaimai17468/hypersig templates/server --name my-app
cd my-app
docker build -t my-app .
docker run --rm -p 3000:3000 my-app

Open http://localhost:3000. The project is the quick start counter, listening on every interface at the port in PORT, with a Dockerfile that builds the binary in one stage and copies it into a small image in the next. The browser runtime is compiled into the binary, so the image holds the binary alone. A host that builds from a Dockerfile, such as Fly.io, Cloud Run, or Render, takes the project as it is, and a host that runs an image takes the one built here, pushed to its registry.

Add the Dockerfile to a project you have

The template's Dockerfile names no package, so it builds any project whose package has one binary. Download it to the directory that holds Cargo.toml, with a .dockerignore that keeps target out of the build.

curl -O https://raw.githubusercontent.com/imaimai17468/hypersig/main/templates/server/Dockerfile
echo target >> .dockerignore

A main that binds 127.0.0.1, as the examples in this repository do, cannot be reached from outside the container. Listen on every interface at the port in PORT instead.

// A container host names the port in `PORT` and connects from outside the
// container, so listen on every interface.
let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_owned());
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}")).await?;
axum::serve(listener, app).await

The Dockerfile builds with cargo install --path ., which fails at the root of a workspace, because the root holds no package. There, change . to the package's directory. The image's Rust is 1.99, so raise the FROM line where the project's rust-version is newer.

What the host has to allow

  • A stream holds its request open for as long as it sends patches. A host that ends a request after a time limit, as Cloud Run does, closes the stream at that limit, so raise the limit, and a call with reconnect() opens the stream again.
  • State kept in memory, such as a counter in the router's state, belongs to one process. When the host runs more than one instance, keep that state in a database.