Files
playbooks/nextcloud/roles/postgres/tasks/main.yaml
T
tanshu be53cec757 Make the Nextcloud playbook standalone: first run installs, later runs update
The playbook is meant to be usable on its own, on any host, the way every other
one here is: the first run creates the container, and every run after that
converges it. It had picked up a migration shape instead -- a readme that assumed
a webroot copied in from monoco, a warning that nagged when config.php was
missing, and, worse, readiness gates that only passed for an instance that was
already installed.

That last one was a real bug for the intended use. On a host with no webroot, the
image initialises the code, writes no config.php because it has no admin
credentials to install with, and serves the setup wizard -- so `occ status` never
reports installed: true, and the play would have spent its whole retry budget and
then failed on a first run that was going exactly as designed.

What replaces it:

- The readiness gate is Apache answering status.php inside the container, which
  is Nextcloud's own endpoint and reports `installed` either way. It is the one
  thing `is-active` cannot tell us, and it passes for a wizard that has not been
  completed.
- config.php's presence then decides the report: wait for maintenance mode to
  clear and print `occ status` for an instance, or say what is left for a human to
  do for a fresh one -- and succeed. The two optional credentials in
  vars/default.yml install unattended instead, in which case the same endpoints
  come back installed and the wizard note never appears.
- The app creates its own database. Postgres is a shared unit with one default
  database and nothing else in this repo knows this app's database name, so a
  fresh cluster previously reached the installer and failed on "database
  nextcloud does not exist". It is a lookup and a `createdb`, a no-op afterwards,
  and it fails with the instruction to run postgres/playbook.yml if the cluster
  is not there rather than with a bare psql error.
- Updates. An update in a Quadlet deployment is "pull, then recreate": the unit
  names a tag, `podman run` reuses the local image, and nothing fetches a newer
  one. The role pulls on every run and compares the image ID either side, so an
  unchanged tag is not a change and a running site is not restarted for nothing.
- Handlers are flushed before verification, so a queued recreate -- new image,
  changed unit, changed environment -- happens before the checks rather than after
  them, and the checks describe what will keep running.

Removed: the entire monoco migration section and its copy commands, the
config.php warning that existed only to serve it, the host-specific notes, and the
narrative comments about this play's first runs. References to the audit and
migration planning docs go with them; the ownership reasoning is stated inline,
since it does not depend on where an instance came from. `hosts:` is now the only
host-specific thing in the directory.

The readme is rewritten around the two paths it now has -- "First run" and
"Updates" -- with the deploy order, the session-file trap and the debugging
commands kept.
2026-10-16 12:59:18 +05:30

65 lines
3.1 KiB
YAML

---
# Attach this app's network to the shared Postgres unit.
#
# Postgres knows nothing about its clients, exactly like Caddy: a service joins
# by dropping 50-nextcloud.conf and the shared 10-network-aliases.conf next to
# postgres.container. The canonical way to do that is the shared include below —
# it writes the same bytes every service writes, asserts that nextcloud.network
# already exists, and notifies the "Restart Postgres" handler defined in this
# role.
#
# This runs in the roles phase, which is after the register include in
# playbook.yml's pre_tasks has created nextcloud.network.
- name: Attach Nextcloud to Postgres
ansible.builtin.include_tasks: "{{ playbook_dir }}/../postgres/roles/attach/tasks/main.yaml"
# On a first attach both drop-ins are new, so Postgres has to be recreated to
# join nextcloud_net — and that restart is a *handler*, which by default runs at
# the end of the play, after the nextcloud role has already started the app. The
# app would then come up before `db` resolves, fail its own readiness check, and
# fail the play on a machine where nothing is actually wrong.
#
# Flushing applies the new drop-in before the app starts. It also flushes the
# "Restart Caddy" that the register pre_task queued, which is what attaches Caddy
# to nextcloud_net — also wanted before the app is expected to serve. On later
# runs nothing is pending and this is a no-op.
- name: Apply the Postgres network change before starting Nextcloud
ansible.builtin.meta: flush_handlers
# The database itself. Postgres is a shared unit that ships one default database,
# and nothing else in this repo knows what this app's database is called — so
# without this, a first run on a fresh host reaches the setup wizard (or the
# unattended install) only to fail on "database {{ pg_database }} does not exist".
# Later runs find it and do nothing.
#
# psql runs inside the Postgres container rather than over the app network, so the
# only thing this needs is a cluster that answers. The retry loop is there because
# the flush above may have just restarted it.
- name: Check whether the {{ pg_database }} database exists
ansible.builtin.command: >-
podman exec -e PGPASSWORD={{ pg_password }} {{ postgres_container }}
psql -U {{ pg_user }} -d postgres -tAc
"select datname from pg_database where datname = '{{ pg_database }}'"
register: nextcloud_db
until: nextcloud_db.rc == 0
retries: 12
delay: 5
changed_when: false
failed_when: false
- name: Fail if the shared Postgres is not reachable
ansible.builtin.assert:
that: nextcloud_db.rc == 0
fail_msg: >-
The shared Postgres container ({{ postgres_container }}) did not answer.
Run postgres/playbook.yml on this host first: this playbook attaches to that
cluster and creates its own database in it. Postgres said:
{{ nextcloud_db.stderr | default('') | trim }}
- name: Create the {{ pg_database }} database
ansible.builtin.command: >-
podman exec -e PGPASSWORD={{ pg_password }} {{ postgres_container }}
createdb -U {{ pg_user }} -O {{ pg_user }} {{ pg_database }}
when: nextcloud_db.stdout | trim | length == 0