Run Valkey as a rootless Podman Quadlet
Replaces the docker_container playbook, which published 127.0.0.1:6379 and kept its /data in an anonymous Docker volume -- the one the audit flagged because no backup script can see it (docs/podman-compat-audit.md 3.1). Valkey becomes the base Quadlet unit, the same shape as Postgres: it knows nothing about its clients, and a service attaches itself by dropping a file into valkey.container.d/ via valkey/roles/attach. Nothing on the host needs 6379, so there is no PublishPort at all rather than a narrower one. The name stays `valkey` for the directory, container, unit and Podman network, and only the alias clients connect on is `redis`: Nextcloud's config.php (written by the image from REDIS_HOST=redis), outline and overleaf reach it as `redis`, while paperless-ngx and yamtrack use the container name. Renaming would touch every REDIS_HOST for no gain. The uid/gid mapping is the detail worth spelling out, because the audit only records the uid. The Alpine image's `valkey` account is 999:1000, not 999:999 -- Alpine already has a gid 999, and the Dockerfile says so before creating the group with the next id. 999 is the uid the anonymous volume was owned by, so the uid matches what was already on disk, but mapping only the uid would leave every file valkey-server writes, dump.rdb included, group-owned by a subgid. Both ids map onto the connection user, so ~/podman-data/valkey stays tanshu:tanshu. Persistence stays on deliberately, and the mount with it. The contents are disposable -- nothing is copied off monoco, and a cold start is survivable -- but this is not purely a cache: Nextcloud keeps its PHP sessions and memcache.locking in it, and Yamtrack uses it as its Celery broker. With RDB on, a rebuild is a graceful stop that writes a final snapshot on SIGTERM that the next container loads, so a redeploy does not log every Nextcloud user out or discard queued tasks. Tmpfs=/data with `save ""` would remove the mount and the whole keep-id mapping with it, and is the right shape for a pure cache, but it would make every redeploy exactly that. The rootless-Podman prerequisites are not applied here: they are the shared task file at caddy/roles/podman, and a host that has run caddy/playbook.yml has them. Two ordering details are load-bearing: - The attach include asserts that <name>.network exists before writing a drop-in that names it. Quadlet rejects the whole valkey unit, with an error that never mentions the drop-in, when a referenced .network file is missing. - A service that attaches flushes handlers before starting the app. The restart that joins Valkey to the new network is a handler, so by default it runs at the end of the play, after the app has already come up and failed to resolve `redis`. nextcloud/roles/valkey is the working example. The play waits on `valkey-cli ping` inside the container rather than trusting systemctl is-active. Restart=always reports active even while the container crash-loops, which is exactly what a wrong keep-id looks like.
This commit is contained in:
1 parent
9bf2cbd7a9
commit
3587bc9001
7 files changed
+635
-52
No files matched your search
+56
-2
@@ -1,4 +1,58 @@
|
||||
---
|
||||
container_name: valkey
|
||||
container_image: valkey/valkey:alpine
|
||||
# ---------------------------------------------------------------------------
|
||||
# Rootless Podman / Quadlet
|
||||
# ---------------------------------------------------------------------------
|
||||
# Duplicated on purpose from caddy/vars/default.yml: a play cannot include that
|
||||
# file wholesale, because it also sets `host_directory` — to the Caddy data
|
||||
# directory — and every playbook defines that for itself. Keep these three in
|
||||
# sync with caddy/vars/default.yml. (Its fourth, podman_unprivileged_port_start,
|
||||
# belongs to the podman tasks, which this play does not run: Valkey publishes
|
||||
# nothing host-side.)
|
||||
#
|
||||
# podman_user is derived from the connection: this play carries no become, so
|
||||
# the Quadlet units belong to whoever Ansible connects as. Requires gather_facts
|
||||
# (set in playbook.yml).
|
||||
podman_user: "{{ ansible_facts['user_id'] }}"
|
||||
podman_data_dir: "{{ ansible_facts['user_dir'] }}/podman-data"
|
||||
quadlet_dir: "{{ ansible_facts['user_dir'] }}/.config/containers/systemd"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Valkey
|
||||
# ---------------------------------------------------------------------------
|
||||
container_name: valkey
|
||||
unit_name: "{{ container_name }}.service"
|
||||
|
||||
# Fully qualified on purpose: rootless Podman resolves short names against
|
||||
# unqualified-search-registries, and there is no TTY to answer a prompt when the
|
||||
# systemd unit starts. The Docker-era playbook said `valkey/valkey:alpine`.
|
||||
image: docker.io/valkey/valkey:alpine
|
||||
|
||||
host_directory: "{{ podman_data_dir }}/{{ container_name }}"
|
||||
|
||||
# The image's WORKDIR and VOLUME: the RDB snapshot and, if it is ever enabled,
|
||||
# the AOF live here. The contents are disposable — nothing needs to be copied
|
||||
# off monoco — but the mount is not: RDB persistence is on by default, and the
|
||||
# graceful stop of a rebuild writes a final snapshot here that the next
|
||||
# container loads. Keep it unless you also set `save ""`. See readme.md, "The
|
||||
# trade-off".
|
||||
data_mount: /data
|
||||
|
||||
# The uid and gid of the image's `valkey` account. The uid is 999 — the one the
|
||||
# anonymous Docker volume was owned by (docs/podman-compat-audit.md §3.2) — but
|
||||
# the gid is *1000*, not 999: Alpine already has a gid 999, and the image's
|
||||
# Dockerfile says so outright ("alpine already has a gid 999, so we'll use the
|
||||
# next id") before dropping to the account with `setpriv --regid=valkey`.
|
||||
# Mapping both ids onto podman_user is what keeps {{ host_directory }} — owner
|
||||
# and group alike — with the connection user, instead of stranding the group on
|
||||
# a subgid the moment valkey-server writes a snapshot.
|
||||
app_uid: 999
|
||||
app_gid: 1000
|
||||
|
||||
# No PublishPort, on purpose: the Docker-era playbook published 127.0.0.1:6379,
|
||||
# and nothing on the host needs it. Clients reach Valkey over the app networks
|
||||
# that attach themselves; {{ container_port }} is the port inside the container.
|
||||
container_port: 6379
|
||||
|
||||
# Quadlet drop-in directory for the Valkey unit. Services attach themselves
|
||||
# here; see the "How services attach themselves" section of readme.md.
|
||||
valkey_dropin_dir: "{{ quadlet_dir }}/{{ container_name }}.container.d"
|
||||
Reference in new issue
Block a user