Health check not passing even though local request to the url returns an 200 OK http response

I am trying to deploy a sample web application built in rust. I have added the health check as well which when pinged locally(with the server running locally as well) responds with an HTTP 200 OK response.
Here is my fly.toml file :

app = "weathered-wave-1081"
primary_region = "ams"

  dockerfile = "Dockerfile"

  internal_port = 8080
  protocol = "tcp"
    hard_limit = 25
    soft_limit = 20

    path = "/healthz"

    force_https = true
    handlers = ["http"]
    port = "80"

    handlers = ["tls", "http"]
    port = "443"

    interval = 10000
    timeout = 2000

I am deploying the build using -

fly deploy --local-only

Thanks in advance.

There is nothing obviously wrong with your config there.

What does the output of fly checks list look like?

Is your application binding to Binding to is not enough.

See: Running Apps On UDP and TCP · Fly Docs

Yeah, my app binds to
I can share my file if that helps

use axum::{
    response::{Html, IntoResponse},
    http::{StatusCode, Uri, header::{self, HeaderMap, HeaderName}},

async fn hello_name(name: String) -> String {
    format!("Hello, {name}!").to_string()

async fn health_handler() -> (StatusCode, &'static str) {
    (StatusCode::OK, "OK")

async fn main() {
    let app = Router::new()
        .route("/", get(|| async { "Hello, World!" }))
        .route("/healthz", get(health_handler))
        .route("/hello/:name", get(hello_name));


The table in the output is empty.

I’m traveling at the moment, so I haven’t had a chance to try your app, but perhaps you can try mine? Put the following Dockerfile in an empty directory, run fly launch (feel free to accept the defaults), and then fly deploy.

# syntax = docker/dockerfile:1

# Shamelessly stolen from:
# Changes:
#   * changed to slim image
#   * bing to instead of
#   * added a loop
#   * added a second docker build stage to produce a minimal image

# Rust as the base image
FROM rust:slim as build

# 1. Create a new empty shell project
RUN USER=root cargo new --bin simple-web-server
WORKDIR /simple-web-server

# 2. Copy our manifests
COPY <<-"EOF" ./Cargo.toml
name = "simple-web-server"
version = "0.1.0"
authors = ["James Benner <>"]

time = "0.1"

# 3. Build only the dependencies to cache them
RUN cargo build
RUN rm src/*.rs

# 4. Now that the dependency is built, copy your source code
COPY <<-"EOF" src/
extern crate time;

use std::io::Write;
use std::net::TcpListener;

fn main() {
    // Bind allows us to create a connection on the port
    // and gets it ready to accept connections.
    let listener = TcpListener::bind("").unwrap();

    // The listener's accept method waits or 'blocks' until
    // we have a connection and then returns a new TcpStream
    // that we can read and write data to.
    loop {
        let mut stream = listener.accept().unwrap().0;
        let message    = "Hello, World!";
        let format     = "%a, %d %b %Y %T GMT";
        let time       = time::now_utc();
        let response   = format!("HTTP/1.1 200 OK\r\n\
                                  Date: {}\r\n\
                                  Content-Type: text/html; charset=utf-8\r\n\
                                  Content-Length: {}\r\n\
                                  time::strftime(format, &time).unwrap(),
        let _          = stream.write(response.as_bytes());

# 5. Build for release.
RUN rm -f ./target/release/deps/simple-web-server*
RUN cargo install --path .

# Minimal image for execution
FROM debian:bullseye-slim
COPY --from=build /simple-web-server/target/release/simple-web-server /usr/local/lib/simple-web-server
CMD ["/usr/local/lib/simple-web-server"]

I ran that and it worked for me first try. The only thing I can assume is that maybe it’s a dockerfile issue?

For cross checking, here’s the dockerfile I used (I called the crate testapp):

FROM rust:latest
RUN mkdir /app
COPY . /app/
RUN cargo install --path .
CMD ["testapp"]

Thanks a lot , this solved the health check problem. But this docker also copies target directory , which makes the docker size pretty big.
Can you suggest any changes to omit the target directory in docker , while also making sure that the app works as expected ?

Docker supports a feature called multi-stage builds.

With this, you can use a throwaway temporary environment to build your executable, then extract any artifacts into a clean environment.

The main downside to using this is that you’ll have to remember to install any native libraries (like openssl) in both containers.

Here’s an illustration of what this could look like:

# Make sure the debian versions (right now, bullseye) match up
# so that both environments share the same glibc version.
# Mixing them up can lead to nasty, difficult to debug errors
#                                   (speaking from experience!)
# (that said, also note that rust uses the convention
#  `slim-[version]` whereas debian base uses `[version]-slim`)
FROM rust:slim-bullseye as builder
RUN apt-get update && apt-get upgrade

RUN mkdir /app
COPY . /app/
RUN cargo build --release

# Run the compiled binary.
FROM debian:bullseye-slim
RUN apt-get update && apt-get upgrade

COPY --from=builder /app/target/release/testapp /usr/bin/testapp
CMD ["testapp"]
1 Like

Great, Thanks @allison .
Just one last query regarding this. Will this Dockerfile be appropriate for a rust server accepting websocket connections too ? Or are there some changes to be made ?

You shouldn’t have any trouble using WebSockets. iirc they use tcp ports 80 and 443, which should already be open for web traffic anyway! if something did go wrong, however, it’d almost certainly be with the fly.toml file rather than the Dockerfile

If you have any trouble with them, though, but they work locally, feel free to come back and post about it! this forum’s very active and there’s a ton of helpful people here :slight_smile:

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.