Start on a fresh upaas volume, and a README section on running under upaas #129

Closed
opened 2026-09-28 11:09:58 +02:00 by clawbot · 2 comments
Collaborator

Part of #17 (definition of done: #17 (comment)). Start only after #111 (health check and smoke test) and #128 (every setting as an environment variable) are on next: the README section below lists what they add.

What upaas does

Read from https://git.eeqj.de/sneak/upaas at 6026e39; re-check against its current main before writing the README.

  • An app volume is a Docker bind mount of an absolute host path, typed in by the operator, onto an absolute container path (buildMounts in internal/docker/client.go, mount.TypeBind). upaas neither creates the host directory nor changes its owner, and Docker refuses a bind mount whose host directory does not exist, so the directory must exist before the first deploy.
  • upaas sets no container user; the image's USER applies.
  • upaas passes the app's environment variables and maps a host port to a container port.
  • 60 seconds after a deploy upaas inspects the container: with a Docker HEALTHCHECK it requires healthy, without one only running (IsContainerHealthy).

The obstacle

The image runs as the unprivileged pixad user. A host directory made with mkdir as root is owned by root with mode 755, and pixa checks at startup that state_dir is writable, so on a fresh upaas volume the container exits at startup.

Definition of done

  1. The container starts on a fresh, root-owned host directory bind-mounted at /var/lib/pixa. Recommended: the image starts as root only long enough to give /var/lib/pixa to pixad when pixad does not already own it, then runs the server as pixad through su-exec (alpine's package for this). The server process never runs as root. The alternative, a fixed user id plus a documented chown step, documents the obstacle rather than removing it; name it in the PR if you take it.
  2. Checked once by hand and stated in one line in the PR body, no transcripts: build the image; create a root-owned host directory (created from inside a container running as root, so it is not owned by your own user); run the container twice with --mount type=bind,source=<that directory>,target=/var/lib/pixa and the same PIXA_SIGNING_KEY. The first run fetches and caches one image from an allowed host (set through PIXA_ALLOWLIST_HOSTS); the second run serves that image from the cache without fetching it again. Afterwards remove every container, image tag and directory the check created.
  3. README.md gains a short "Running under upaas" section that lists exactly:
    • the container port: 8080;
    • the volume: container path /var/lib/pixa, and that the host directory must exist before the first deploy;
    • each environment variable upaas should set, with its meaning: PIXA_SIGNING_KEY (required), plus those a deployment usually sets, taken from the table #128 adds, with a pointer to that table for the rest;
    • the health check: the image's HEALTHCHECK on /.well-known/healthcheck.json, which upaas reads 60 seconds after a deploy; it probes the port from PORT (default 8080), so a port changed only in a mounted config file is not seen by it: change the port with PORT;
    • any first-run step (creating the host directory).
      Only settings upaas actually has; invent nothing.

Model: opus-5-5

Part of https://git.eeqj.de/sneak/pixa/issues/17 (definition of done: https://git.eeqj.de/sneak/pixa/issues/17#issuecomment-102965). Start only after https://git.eeqj.de/sneak/pixa/issues/111 (health check and smoke test) and https://git.eeqj.de/sneak/pixa/issues/128 (every setting as an environment variable) are on `next`: the README section below lists what they add. ## What upaas does Read from https://git.eeqj.de/sneak/upaas at `6026e39`; re-check against its current `main` before writing the README. - An app volume is a Docker bind mount of an absolute host path, typed in by the operator, onto an absolute container path (`buildMounts` in `internal/docker/client.go`, `mount.TypeBind`). upaas neither creates the host directory nor changes its owner, and Docker refuses a bind mount whose host directory does not exist, so the directory must exist before the first deploy. - upaas sets no container user; the image's `USER` applies. - upaas passes the app's environment variables and maps a host port to a container port. - 60 seconds after a deploy upaas inspects the container: with a Docker `HEALTHCHECK` it requires `healthy`, without one only running (`IsContainerHealthy`). ## The obstacle The image runs as the unprivileged `pixad` user. A host directory made with `mkdir` as root is owned by root with mode 755, and pixa checks at startup that `state_dir` is writable, so on a fresh upaas volume the container exits at startup. ## Definition of done 1. The container starts on a fresh, root-owned host directory bind-mounted at `/var/lib/pixa`. Recommended: the image starts as root only long enough to give `/var/lib/pixa` to `pixad` when `pixad` does not already own it, then runs the server as `pixad` through `su-exec` (alpine's package for this). The server process never runs as root. The alternative, a fixed user id plus a documented `chown` step, documents the obstacle rather than removing it; name it in the PR if you take it. 2. Checked once by hand and stated in one line in the PR body, no transcripts: build the image; create a root-owned host directory (created from inside a container running as root, so it is not owned by your own user); run the container twice with `--mount type=bind,source=<that directory>,target=/var/lib/pixa` and the same `PIXA_SIGNING_KEY`. The first run fetches and caches one image from an allowed host (set through `PIXA_ALLOWLIST_HOSTS`); the second run serves that image from the cache without fetching it again. Afterwards remove every container, image tag and directory the check created. 3. `README.md` gains a short "Running under upaas" section that lists exactly: - the container port: 8080; - the volume: container path `/var/lib/pixa`, and that the host directory must exist before the first deploy; - each environment variable upaas should set, with its meaning: `PIXA_SIGNING_KEY` (required), plus those a deployment usually sets, taken from the table https://git.eeqj.de/sneak/pixa/issues/128 adds, with a pointer to that table for the rest; - the health check: the image's `HEALTHCHECK` on `/.well-known/healthcheck.json`, which upaas reads 60 seconds after a deploy; it probes the port from `PORT` (default 8080), so a port changed only in a mounted config file is not seen by it: change the port with `PORT`; - any first-run step (creating the host directory). Only settings upaas actually has; invent nothing. Model: opus-5-5
clawbot added this to the 1.0.0 milestone 2026-09-28 11:09:58 +02:00
clawbot self-assigned this 2026-09-28 11:09:58 +02:00
Author
Collaborator

Plan:

  • Dockerfile runtime stage: add alpine's su-exec package and drop USER pixad, so the container starts as root. A new start-up script, deploy/docker-entrypoint.sh, becomes the ENTRYPOINT: when pixad does not own /var/lib/pixa, it gives the directory (not its contents) to pixad, then runs the server as pixad through su-exec. The server never runs as root; make docker-smoke and the HEALTHCHECK stay as they are.
  • The two-run check from item 2, done once by hand and stated in one line in the PR.
  • README.md: a "Running under upaas" section with the container port, the volume, PIXA_SIGNING_KEY plus PIXA_ALLOWLIST_HOSTS and PIXA_CACHE_MAX_BYTES from the Configuration table (with a pointer to it), the health check, and the first-run step. The upaas summary above still holds at upaas main 97a17e5.
  • A TODO.md entry.

Judgement call: the image's user becomes root, so the health probe and docker exec also run as root; only the server drops to pixad.

Model: opus-5-5

Plan: - `Dockerfile` runtime stage: add alpine's `su-exec` package and drop `USER pixad`, so the container starts as root. A new start-up script, `deploy/docker-entrypoint.sh`, becomes the `ENTRYPOINT`: when `pixad` does not own `/var/lib/pixa`, it gives the directory (not its contents) to `pixad`, then runs the server as `pixad` through `su-exec`. The server never runs as root; `make docker-smoke` and the `HEALTHCHECK` stay as they are. - The two-run check from item 2, done once by hand and stated in one line in the PR. - `README.md`: a "Running under upaas" section with the container port, the volume, `PIXA_SIGNING_KEY` plus `PIXA_ALLOWLIST_HOSTS` and `PIXA_CACHE_MAX_BYTES` from the Configuration table (with a pointer to it), the health check, and the first-run step. The upaas summary above still holds at upaas `main` `97a17e5`. - A `TODO.md` entry. Judgement call: the image's user becomes root, so the health probe and `docker exec` also run as root; only the server drops to `pixad`. Model: opus-5-5
Author
Collaborator

Built as planned in the comment above: #135

Model: opus-5-5

Built as planned in the comment above: https://git.eeqj.de/sneak/pixa/pulls/135 Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#129