Podman

Build on the host, health-check a release socket, switch traffic when healthy. Deployment hooks is the contract.

Requires Guide host setup. On the remote: Podman, curl, OpenSSL, Caddy. The app answers GET /health on a Unix socket in the container.

orbit init writes this recipe when it recognizes a Stario or Python HTTP app (--recipe stario-podman or python-podman). Source: examples/podman-slot.

Each deploy: build image → start container on a release socket → health-check → point stable socket at it and reload Caddy → stop the previous container.

Project layout

text
ops/
  .containerignore
  Containerfile
  deploy
  status
  remove

ops/.containerignore excludes Git metadata, caches, and ops/ from the Podman build context. Orbit still uploads ops/ to $SLOT_SRC.

ops/Containerfile

Multi-stage: uv sync, copy app, default stario serve. The deploy hook overrides the command to listen on /sockets/app.sock.

dockerfile
FROM python:3.14-slim-bookworm AS builder
 
WORKDIR /workspace
RUN pip install --no-cache-dir uv
COPY . /workspace
RUN --mount=type=cache,target=/root/.cache/uv uv sync --no-dev
 
FROM python:3.14-slim-bookworm
WORKDIR /workspace
COPY --from=builder /workspace /workspace
ENV PATH="/workspace/.venv/bin:$PATH"
CMD ["stario", "serve", "app.main:bootstrap"]

For another Python server, change CMD and APP_CMD in ops/deploy. Listen on /sockets/app.sock in both. The container command sets STARIO_UNIX_SOCKET=/sockets/app.sock.

The complete hook also handles preview basic auth and idempotent re-deploy.

Three failure modes

Health-check the release

Poll GET /health on the release socket before you switch the stable route. On failure, remove the new container and exit non-zero. The old release keeps serving.

Caddy 502 after a healthy status

Caddy runs as group orbit. Without chgrp orbit on the socket, Caddy returns 502 while status still reports healthy.

Re-deploy or a missing Caddy binary

The complete ops/deploy and ops/remove hooks handle preview auth, an idempotent re-deploy, and a missing Caddy binary.

Public hostname

Point DNS at the host (A record) and use the slot name as the public hostname (app.example.com). Orbit does not configure DNS for you.

First deploy

First deploy in the guide. Then mostly:

bash
orbit deploy
bash
orbit remove ship app.example.com        # keep data and env
orbit remove ship app.example.com --purge