← Docker articles

ACE-U field notes

From docker compose up to a public URL: ports, health and scale-to-zero

What changes when a local demo needs a real address, and how to keep it close to free when nobody is looking at it.

Shubham Anandani··4 min read

Problem

A local demo is easy. A public one raises new questions: which port is exposed, how the platform knows the app is healthy, what the first request does after a quiet night, and how to be sure a traffic spike does not turn into a surprise bill. You do not need a cluster for this. A managed container platform with scale-to-zero gives you a URL that costs almost nothing when idle.

Options and trade-offs

  • Run a VM and keep it on: simple, but you pay for the idle hours.
  • Run a cluster: powerful, and far more than a demo needs.
  • Use a managed container platform with scale-to-zero: you accept a cold start in return for near-zero idle cost.

My recommendation

Make the app platform-friendly first: it listens on a configurable port, has a health endpoint, and redirects the root path somewhere useful. Then deploy a digest-pinned image with a minimum of zero replicas, managed TLS, and budget alerts set well below the ceiling.

Worked example

A tiny Java server with no dependencies. It reads the port from the environment, answers a health check, and redirects the root path to the docs.

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

public class Main {
    public static void main(String[] args) throws IOException {
        int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080"));
        HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);

        server.createContext("/health", ex -> respond(ex, 200, "application/json", "{\"status\":\"ok\"}"));
        server.createContext("/docs", ex -> respond(ex, 200, "text/html", "<h1>Docs</h1>"));
        server.createContext("/", ex -> {
            if ("/".equals(ex.getRequestURI().getPath())) {
                ex.getResponseHeaders().add("Location", "/docs");
                ex.sendResponseHeaders(302, -1);
                ex.close();
            } else {
                respond(ex, 404, "text/plain", "not found");
            }
        });
        server.start();
    }

    private static void respond(HttpExchange ex, int status, String type, String body) throws IOException {
        byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
        ex.getResponseHeaders().add("Content-Type", type);
        ex.sendResponseHeaders(status, bytes.length);
        try (OutputStream out = ex.getResponseBody()) {
            out.write(bytes);
        }
    }
}

The deployment description is illustrative, since every platform has its own field names, but the shape is the same everywhere:

{
  "name": "demo-app",
  "image": "registry.example.com/team/app@sha256:<digest>",
  "containerPort": 8080,
  "healthCheckPath": "/health",
  "ingress": { "tls": true },
  "autoscale": { "minReplicas": 0, "maxReplicas": 3 }
}
docker compose up --build
curl -i http://127.0.0.1:8080/
curl -s http://127.0.0.1:8080/health

Checklist

  1. Read the port from the environment, and expose the one the platform expects.
  2. Add a cheap health endpoint, and point the platform at it.
  3. Redirect the root path to something useful.
  4. Deploy by digest, with managed TLS.
  5. Set minimum replicas to zero, and test the first request after idle.
  6. Set a budget alert well under your ceiling, and send it somewhere you will see it.

Where this doesn't apply

If you need steady low latency, scale-to-zero is the wrong choice, because the first request after a quiet period will be slow.

One honest limitation

A cold start on a JVM service can take seconds. You can soften it with a smaller image and class-data sharing, but you cannot remove it, so decide whether a slow first visit is acceptable for the demo.

Sources

  • Docker Docs: Compose file reference (ports)
  • Docker Docs: Dockerfile HEALTHCHECK
  • Oracle Java Documentation: com.sun.net.httpserver package