Deploy with rootless podman + Quadlet on emily
Retargets the build and the playbook off rootful Docker on monoco, following the conventions already in place on emily's other services: Quadlet units in ~/.config/containers/systemd, per-service data in ~/podman-data, and a shared Caddy that every service registers itself with — a network drop-in plus a marked block in its Caddyfile. Nothing is published on the host; Caddy reaches the containers by name over mozimo_net. - Makefile: podman build + push instead of docker buildx, PLATFORMS=linux/arm64 (both hosts are aarch64). podman has no built-in QEMU the way buildx's builder image does, so a foreign-platform failure now points at qemu-user-static. - deploy.sh: pass COMMIT= to make, which the Makefile actually reads; it was passing TAG=, so the tag silently came from `git rev-parse` instead. - ansible: hosts emily, become false, XDG_RUNTIME_DIR for rootless podman and systemctl --user. Docker modules become Quadlet templates with user-service handlers, and the network role now ships mozimo.network plus the Caddy drop-in rather than calling docker_network. - UserNS=keep-id maps the container's uid 1000 (node) and root (vince) onto the host user, so PocketBase and Vince write host-owned files instead of subuid-owned ones; the play asserts the pinned uid still matches the host. - ansible/README.md: the unit layout, the service-registration convention, and the monoco -> emily migration (PocketBase 23 MB + Vince 119 MB, DNS cutover, rollback). Verified by a check-mode rehearsal against emily (13 ok / 8 changed / 0 failed) and a disposable Quadlet unit on that host proving the uid mapping, the EnvironmentFile and the isolated network. The real build and cutover have not been run.
This commit is contained in:
1 parent
1bc1d5f753
commit
d04314ab1c
20 files changed
+535
-127
No files matched your search
@@ -0,0 +1,9 @@
|
||||
---
|
||||
# Handlers for the analytics role.
|
||||
- name: Restart Vince
|
||||
ansible.builtin.systemd_service:
|
||||
name: "{{ vince_container }}.service"
|
||||
state: restarted
|
||||
scope: user
|
||||
when: not ansible_check_mode
|
||||
listen: "Restart Vince"
|
||||
@@ -1,44 +1,24 @@
|
||||
---
|
||||
# Self-hosted first-party analytics (Vince — Plausible-protocol, single
|
||||
# binary). Only reachable on the docker network:
|
||||
# - Caddy proxies /stats/* on the www host here (prefix stripped), serving
|
||||
# binary). Reachable only on mozimo's network:
|
||||
# - Caddy proxies /stats/* on the apex host here (prefix stripped), serving
|
||||
# the tracker script and event API first-party;
|
||||
# - Caddy publishes the dashboard on {{ stats_host }}.
|
||||
#
|
||||
# The image's ENTRYPOINT is the bare /vince binary (prints help and exits
|
||||
# with no args), so the container MUST be given the `serve` command.
|
||||
- name: Pull Vince image
|
||||
docker_image:
|
||||
name: "{{ vince_image }}"
|
||||
source: pull
|
||||
state: present
|
||||
force_source: "{{ vince_image_force_update | default(false) }}"
|
||||
# Analytics history lives in {{ vince_directory }} and is migrated separately
|
||||
# from monoco — see ansible/README.md.
|
||||
- name: Pull the Vince image
|
||||
ansible.builtin.command: "podman pull {{ vince_image }}"
|
||||
changed_when: false
|
||||
|
||||
- name: Ensure Vince data directory exists
|
||||
file:
|
||||
path: "{{ host_directory }}/vince_data"
|
||||
state: directory
|
||||
owner: "1000"
|
||||
group: "1000"
|
||||
- name: Install the Vince Quadlet unit
|
||||
ansible.builtin.template:
|
||||
src: vince.container.j2
|
||||
dest: "{{ quadlet_dir }}/{{ vince_container }}.container"
|
||||
mode: "0644"
|
||||
notify: Restart Vince
|
||||
|
||||
- name: Create Vince container
|
||||
docker_container:
|
||||
name: "{{ vince_container }}"
|
||||
image: "{{ vince_image }}"
|
||||
command: serve
|
||||
state: started
|
||||
restart_policy: "unless-stopped"
|
||||
env:
|
||||
VINCE_DATA: /data
|
||||
VINCE_LISTEN: "0.0.0.0:{{ vince_port }}"
|
||||
# serve (re)creates the admin account from these on every boot —
|
||||
# the env file is the source of truth, like the PocketBase superuser.
|
||||
VINCE_ADMIN_NAME: "{{ vince_admin_name }}"
|
||||
VINCE_ADMIN_PASSWORD: "{{ vince_admin_password }}"
|
||||
# create the tracked site on startup (CSV list)
|
||||
VINCE_DOMAINS: "{{ vince_site_domain }}"
|
||||
VINCE_URL: "https://{{ stats_host }}"
|
||||
volumes:
|
||||
- "{{ host_directory }}/vince_data:/data"
|
||||
networks:
|
||||
- name: "{{ docker_network }}"
|
||||
- name: Generate the user service from the Quadlet unit
|
||||
ansible.builtin.systemd_service:
|
||||
daemon_reload: true
|
||||
scope: user
|
||||
@@ -0,0 +1,42 @@
|
||||
# Vince (first-party analytics, Plausible protocol) — rootless Podman Quadlet.
|
||||
# Managed by Ansible. Do not edit by hand.
|
||||
#
|
||||
# The image's ENTRYPOINT is the bare /vince binary (prints help and exits with
|
||||
# no args), so the `serve` subcommand is required.
|
||||
#
|
||||
# Reachable only on {{ network_quadlet }}.network: Caddy proxies /stats/* on
|
||||
# the apex host here (prefix stripped) and publishes the dashboard on
|
||||
# {{ stats_host }}. There is no published port.
|
||||
|
||||
[Unit]
|
||||
Description=Vince analytics
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Container]
|
||||
ContainerName={{ vince_container }}
|
||||
Image={{ vince_image }}
|
||||
|
||||
# Vince runs as root in the image and writes /data; mapping container root
|
||||
# onto {{ podman_user }} keeps {{ vince_directory }} host-owned.
|
||||
UserNS=keep-id:uid=0,gid=0
|
||||
|
||||
Exec=serve
|
||||
Environment=VINCE_DATA=/data
|
||||
Environment=VINCE_LISTEN=0.0.0.0:{{ vince_port }}
|
||||
# serve (re)creates the admin account from these on every boot — the playbook
|
||||
# vars are the source of truth, like the PocketBase superuser.
|
||||
Environment=VINCE_ADMIN_NAME={{ vince_admin_name }}
|
||||
Environment=VINCE_ADMIN_PASSWORD={{ vince_admin_password }}
|
||||
# create the tracked site on startup (CSV list)
|
||||
Environment=VINCE_DOMAINS={{ vince_site_domain }}
|
||||
Environment=VINCE_URL=https://{{ stats_host }}
|
||||
|
||||
Volume={{ vince_directory }}:/data
|
||||
Network={{ network_quadlet }}.network
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
Reference in new issue
Block a user