Commit graph

20 commits

Author SHA1 Message Date
Claude
031c3bdd41
fix: defer podman system migrate to after the build to eliminate early downtime
podman system migrate was stopping all containers immediately (visible in
the terminal output as "stopped <id>" lines), before the build even began.

Moving it to just before compose down/up means running containers stay
alive for the entire duration of the image build.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-24 10:48:45 +00:00
Claude
a16ccdcef4
fix: build images before tearing down compose to reduce downtime
Old behaviour: compose down → long build → compose up
New behaviour: long build (service stays live) → compose down → compose up

Downtime is now limited to the few seconds of the swap instead of the
entire duration of the Rust/image build.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-24 10:43:36 +00:00
Claude
e7fd2a4365
fix: auto-enable cgroup swap accounting on Pi before starting containers
runc (used by Podman) always writes memory.swap.max when initializing the
cgroup v2 memory controller, even without explicit --memory flags. On
Raspberry Pi OS this file is absent because swap accounting is disabled
by default in the kernel, causing every container start to fail with:

  openat2 …/memory.swap.max: no such file or directory

start.sh now detects this condition early, patches the kernel cmdline
(cgroup_enable=memory cgroup_memory=1 swapaccount=1) in either
/boot/firmware/cmdline.txt (Pi OS Bookworm) or /boot/cmdline.txt
(older releases), and tells the user to reboot once before continuing.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 18:05:11 +00:00
Claude
b5e6c8fcd3
Fix rootless Podman lchown EINVAL by ensuring uidmap and fresh service
Two root causes for "invalid argument" when chowning non-root UIDs/GIDs
in image layers:

1. Missing uidmap package: without setuid newuidmap/newgidmap binaries,
   Podman can only map a single UID (0 → current user) in the user
   namespace.  Any layer file owned by gid=42 (shadow) or similar then
   has no mapping and lchown returns EINVAL.  Now install uidmap if absent.

2. Stale Podman service: a service started before subuid/subgid entries
   existed silently keeps the single-UID mapping for its lifetime even
   after the entries are added and podman system migrate is run.  Now
   always kill and restart the service on each start.sh run so it always
   reads the current subuid/subgid configuration.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 10:32:13 +00:00
Claude
b64195c58a
Always run podman system migrate, not only when subuid/subgid entries are added
If entries already existed before this script first ran, _HIY_SUBID_CHANGED
stayed 0 and migrate was skipped, leaving Podman storage out of sync with
the namespace mappings and causing lchown errors on layer extraction.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 10:25:25 +00:00
Claude
4f5c2e8432
Add subuid/subgid entries for rootless Podman user namespace mapping
Without entries in /etc/subuid and /etc/subgid, Podman cannot map the
UIDs/GIDs present in image layers (e.g. gid 42 for /etc/shadow) into
the user namespace, causing 'lchown: invalid argument' on layer extraction.

Add a 65536-ID range starting at 100000 for the current user if missing,
then run podman system migrate so existing storage is updated.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 10:19:21 +00:00
Claude
dae5fd3b53
Allow rootless Podman to bind ports 80 and 443
Rootless processes cannot bind privileged ports (<1024) by default.
Lower net.ipv4.ip_unprivileged_port_start to 80 at startup, and persist
it to /etc/sysctl.conf so the setting survives reboots.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 10:11:21 +00:00
Claude
d2cba788ab
Fix rootless Podman by owning /run/user/<uid> instead of redirecting to /tmp
Podman rootless unconditionally resets XDG_RUNTIME_DIR to /run/user/<uid>
if that directory exists, overriding any env var we set. Redirecting to
/tmp is therefore ineffective.

Instead, ensure /run/user/<uid> exists and is owned by the current user
(using sudo if needed), mirroring what PAM/logind does for login sessions.
All Podman runtime state (socket, events, netavark) then works correctly.

Remove the now-unnecessary storage.conf/containers.conf writes and the
inline env override on podman system service.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 08:02:10 +00:00
Claude
0932308ed6
Fix make and podman compose to use correct paths when run from repo root
make build was looking for Makefile in cwd (repo root) instead of infra/.
Use -C "$SCRIPT_DIR" so it always finds infra/Makefile regardless of where
the script is invoked from.

Add -f flag to podman compose up so it finds infra/docker-compose.yml
from any working directory.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 07:55:58 +00:00
Claude
ea5b6e5594
Write containers.conf tmp_dir and force env var inline on podman call
Podman's events engine reads tmp_dir from containers.conf, not from
XDG_RUNTIME_DIR directly. Write both storage.conf and containers.conf
to /tmp/podman-<uid> so no path under /run/user/<uid> is ever used.
Also use `env XDG_RUNTIME_DIR=...` prefix on podman invocation to
override any stale value in the calling shell environment.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 07:49:00 +00:00
Claude
0690e3c48a
Unconditionally redirect Podman runtime to /tmp; override storage.conf
Stop relying on conditional checks. Always point XDG_RUNTIME_DIR and
storage.conf runroot to /tmp/podman-<uid> so Podman never touches
/run/user/<uid>, which requires PAM/logind to create.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 07:42:54 +00:00
Claude
cf50332a8f
Check XDG_RUNTIME_DIR is writable, not just set
SSH sessions may export XDG_RUNTIME_DIR=/run/user/<uid> even when that
directory doesn't exist or isn't writable. Check writability rather than
emptiness before falling back to /tmp/podman-<uid>.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 07:40:53 +00:00
Claude
139a03c774
Set XDG_RUNTIME_DIR before any podman call in non-login shells
Podman uses XDG_RUNTIME_DIR for its RunRoot, events dirs, and default
socket path. Without it pointing to a writable location, podman fails
with 'mkdir /run/user/<uid>: permission denied' even before the socket
is created. Export it to /tmp/podman-<uid> when unset.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 07:39:34 +00:00
Claude
26701675f2
Use XDG_RUNTIME_DIR or /tmp fallback for Podman socket dir
/run/user/<uid> is created by PAM/logind and doesn't exist in non-login
shells. Fall back to /tmp/podman-<uid> when XDG_RUNTIME_DIR is unset so
mkdir always succeeds.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 07:38:40 +00:00
Claude
5359c43cb8
Replace systemctl --user with podman system service for socket activation
systemctl --user fails in non-interactive shells (no D-Bus session bus).
podman system service starts the socket directly without systemd/D-Bus,
backgrounding the process and waiting up to 5 s for the socket to appear.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-22 07:37:02 +00:00
Claude
06ababa7c6
Fix Podman socket for rootless setup on Raspberry Pi
start.sh now activates the Podman user socket via systemctl --user if it
isn't running yet, then exports DOCKER_HOST and PODMAN_SOCK so that
podman compose (which delegates to the docker-compose plugin) can connect.

docker-compose.yml mounts ${PODMAN_SOCK} into the socat proxy container
at a fixed internal path (/podman.sock), so it works for both rootful
(/run/podman/podman.sock) and rootless (/run/user/<UID>/podman/podman.sock)
without hardcoding the UID.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-21 18:08:10 +00:00
Claude
dd107aacdb
Fix start.sh: docker compose → podman compose
Missed in the previous Podman migration commit.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
2026-03-21 18:03:41 +00:00
Claude
2060606adc
Consolidate to single .env at repo root
Add ACME_EMAIL to root .env.example.
start.sh now reads root .env and passes it to docker compose.
Removed infra/.env.example.
2026-03-20 10:21:35 +00:00
Claude
d5a5875899
Add TLS setup to start.sh; drop Cloudflare requirement
start.sh now generates proxy/caddy.json at launch time with Let's Encrypt
automatic HTTPS (HTTP-01 or TLS-ALPN-01 challenge — no Cloudflare needed).

Reads DOMAIN_SUFFIX and ACME_EMAIL from infra/.env before starting.
Added infra/.env.example to document required vars.
2026-03-20 10:18:01 +00:00
Claude
b060ec68af
Add start.sh and Makefile build-only targets
start.sh builds via 'make build' (platform auto-detected) then starts
services detached with 'docker compose up -d'.

Makefile gains build/build-<platform> targets that build images without
starting, mirroring the existing up/<platform> targets.
2026-03-20 10:06:24 +00:00