--- # --------------------------------------------------------------------------- # 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: Nextcloud listens on # 80 inside the container and 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" # --------------------------------------------------------------------------- # Nextcloud # --------------------------------------------------------------------------- host: cloud.hopsngrains.com container_name: nextcloud 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 `nextcloud:latest`. image: docker.io/library/nextcloud:latest # The Quadlet file is .network. caddy/roles/register pins the # Podman network name to _net (so: nextcloud.network, # nextcloud_net). network_name: "{{ container_name }}" # The image's VOLUME and Apache document root — and, unlike the other services # here, it holds the *code* as well as the state: config.php, data/, # custom_apps/ and every app's PHP files live under it. The Docker-era playbook # bind-mounted the whole thing from /var/lib/nextcloud, and this one keeps that # shape on purpose. See readme.md, "What is state, and what is not". host_directory: "{{ podman_data_dir }}/{{ container_name }}" webroot: /var/www/html # The uid the *server* writes as: the image's entrypoint starts as container-root, # rsyncs the code in with --chown www-data:www-data, and Apache then drops to # www-data. `docker exec nextcloud id -u` reports 0, which is misleading # (docs/podman-compat-audit.md §3.2). app_uid: 33 app_gid: 33 # The port Apache listens on inside the container, and the one Caddy proxies to. container_port: 80 # POSTGRES_* and REDIS_HOST are read by the entrypoint on every start — REDIS_HOST # in particular rewrites PHP's session handler config — and POSTGRES_PASSWORD is in # there, so it goes in this 0600 file rather than in the world-readable unit. env_file: "{{ host_directory }}/nextcloud.env" # The shared Postgres unit this app attaches itself to (postgres/playbook.yml). # roles/postgres owns this app's drop-ins there and the "Restart Postgres" # handler that applies them. postgres_container: postgres postgres_unit: "{{ postgres_container }}.service" # The credentials and database name the Docker-era playbook embedded in its # `env:` block, kept as-is so the already-restored `nextcloud` database (320 MB, # one of the 12 in postgres/migrate-from-monoco.md) and config.php agree. pg_user: postgres pg_password: '123456' pg_database: nextcloud # `db` is the host config.php actually connects to — the Docker-era container set # POSTGRES_HOST=db, and postgres/roles/attach adds that alias to the shared # Postgres unit. db_host: db # The shared Valkey unit this app attaches itself to (valkey/playbook.yml), and # the alias it is reached by. REDIS_HOST is not cosmetic: config.php's # `memcache.locking` is Redis, so an unresolvable `redis` breaks uploads rather # than merely slowing them down. valkey_container: valkey valkey_unit: "{{ valkey_container }}.service" redis_host: redis # The shared Caddy unit and its Caddyfile, both owned by caddy/playbook.yml. caddy_name: caddy caddyfile_path: "{{ podman_data_dir }}/{{ caddy_name }}/conf/Caddyfile" # --------------------------------------------------------------------------- # Background jobs # --------------------------------------------------------------------------- # Nextcloud wants `cron.php` every five minutes. The Docker-era playbook put it in # *root's* crontab as `docker exec -u www-data nextcloud php cron.php`; root # cannot `podman exec` into another user's rootless containers, so it becomes a # systemd user timer next to the Quadlet units (docs/podman-migration.md §8(b)). cron_unit: "{{ container_name }}-cron" cron_schedule: "*:0/5" user_unit_dir: "{{ ansible_facts['user_dir'] }}/.config/systemd/user" # The site block, unchanged from the Docker-era vars: Caddy terminates TLS and # Nextcloud has always asked for HSTS. There is no request-body limit to carry # over — Caddy streams bodies and imposes none by default, whereas the dead nginx # front end needed client_max_body_size spelled out. snippet_block: | {{ host }} { reverse_proxy {{ container_name }}:{{ container_port }} header { Strict-Transport-Security "max-age=15552000; includeSubDomains; preload" } }